3月29日,ABT Network(ABT 鏈網(wǎng))正式發(fā)布。ABT Network以完全去中心化方式連接編織多條區(qū)塊鏈形成的網(wǎng)絡(luò),以云節(jié)點和織鏈為網(wǎng)的方式重新定義了新一代區(qū)塊鏈基礎(chǔ)架構(gòu)。本文談?wù)?ABT Network 誕生前發(fā)生的有趣經(jīng)歷。
為方便閱讀,先簡單介紹一下本文談到的一些概念:
· ABT Network:多條使用 ArcBlock 技術(shù)打造的區(qū)塊鏈形成的網(wǎng)路。
· ABT Chain Node:ArcBlock 使用 Forge Framework 打造的區(qū)塊鏈節(jié)點軟件。
· Forge Framework:ArcBlock 為區(qū)塊鏈開發(fā)打造的框架,可以看做區(qū)塊鏈?zhǔn)澜缋锏?Ruby on Rails。
打造人人都可部署的節(jié)點
在開發(fā) Forge Framework 和 ABT Chain Node 時,我們有一個深深的信念:運行在 Forge 之上的區(qū)塊鏈項目可以是陽春白雪,也可以是下里巴人;可以是每日千百萬級 transaction 的大型應(yīng)用,也可以是獨立開發(fā)者及其小圈子的自娛自樂。因而,一個節(jié)點只要有過得去的算力,就可以運行 Forge,這樣,只要愿意,人人都可以部署自己可負(fù)擔(dān)的節(jié)點。
那什么算是「過得去的算力」呢?考慮到 App 開發(fā)者的開發(fā)期的經(jīng)濟能力,我們將其定位在單節(jié)點月支出在 $15 以內(nèi),在 Digital Ocean 上,這對應(yīng):
也就是 1GB / 1CPU / 25GB disk 一路到 2GB / 2CPU / 60GB disk 的乞丐版云主機。
在今年一二月份的大部分開發(fā)時間里,我們都在使用 $5 的至尊乞丐版主機 —— 而且,我們一口氣在美西 (SF),美東 (NY),西歐 (London) 和東南亞 (Singapore) 部署了四個節(jié)點,組成一個 P2P 網(wǎng)絡(luò),來開發(fā) Forge。我們相信,極端惡劣的環(huán)境,能打造盡可能健壯的軟件,讓各種問題都提前暴露出來。
有了環(huán)境,我們需要有足夠模擬真實應(yīng)用場景的流量。為此我們開發(fā)了一個 simulator(模擬器),并且做了一個簡單的描述語言來描述我們?nèi)绾伍_啟 simulation(節(jié)選):
pools:
create_asset: 5
declare: 5
exchange: 5
transfer: 10
update_asset: 5
consume_asset: 5
poke: 5
meta:
tick: 500
simulations:
- name: exchange token and assets
interval: 2
num: 2
type: exchange
settings:
value: “1000..20000”
- name: transfer token and assets
interval: 5
num: 2
type: transfer
settings:
value: “1000..5000”
after:
- interval: 1
action: consume_asset
通過改變 pool size,我們可以調(diào)節(jié)并發(fā)程度,通過控制 tick,我們控制 traffic 的速率;通過添加更多的 simulation,我們改變 traffic 的多樣性。
在 simulator 的作用下,很長一段時間里,我們的開發(fā)網(wǎng)絡(luò)三天兩頭 crash(崩潰) —— 一會 out of memory,一會 too many open files,一會 gen_server tiemout,一會 tcp send/receive buffer full。這些問題,如果換上個 4G memory / 4 CPU / 100G disk 的主機,只有很小的概率才暴露出來,而我們主動讓其發(fā)生在開發(fā)環(huán)境中,使得大部分問題得到了妥善處理。比如說,我們發(fā)現(xiàn)我們使用的 consensus engine(共識引擎) 不穩(wěn)定,時不時 crash,crash 之后很容易把 state db(狀態(tài)數(shù)據(jù)庫) 寫壞,使得節(jié)點徹底崩潰,無法恢復(fù)。對此,我們的做法是,一旦 consensus engine crash,我們讓 Forge 自動 crash(可惜了 erlang VM 強大的 crash recover 機制),然后由我們開發(fā)的 forge starter 將 forge 重啟。重啟后,我們回溯到上一個區(qū)塊的數(shù)據(jù),重新 apply,如果 consensus engine 可以恢復(fù),那么舊繼續(xù)往后走;否則便繼續(xù) crash 和繼續(xù)回溯。
在這樣嚴(yán)苛的環(huán)境下 Forge 逐漸成長,至尊乞丐節(jié)點組成的網(wǎng)絡(luò),不斷死亡,不斷重生,就像「明日邊緣」里的湯姆克魯斯,從小白一路成長為小強,迎來了第一百萬個 transaction。
好景不長,在大約 1.5M txs 時,網(wǎng)絡(luò)再次 crash:
這次 crash 的一干二凈,所有節(jié)點全軍覆沒,連 ssh 都上不去。Digital Ocean 的監(jiān)控顯示 CPU 基本為 0,正琢磨著是不是 disk 寫滿了,一臺機器回光返照,給我登上去 du 的機會。果然,25G 的 disk 被吃得一干二凈。take snapshot,換大硬盤,搞定。
三月上旬我們終于拋棄了 $5 的機器,換裝 $15 的「大」節(jié)點。在 Digital Ocean 的云上,我們同時跑了好幾個網(wǎng)絡(luò),做 rolling upgrade。之前我們一周一個 milestone,出一個大版本,若干小版本,三月第二周起,我們每天出一個版本,因而,版本太多而網(wǎng)絡(luò)不夠用了。。。
很快,1 million txs 的里程碑被 5 million 取代:
繼而被 6M,7M,… 取代。后來我們 breaking change 太多,也就沒有繼續(xù)累積這個數(shù)字。
可以讓區(qū)塊鏈節(jié)點穩(wěn)定地在 $15 的機器上部署是我們 ArcBlock 的一個創(chuàng)舉。我們做過別的公鏈的節(jié)點 —— 對方給出的推薦配置,一個節(jié)點一個月要一千多美金。如果一個應(yīng)用開發(fā)者開發(fā)者,想部署一個自己的鏈,初期通過自己的節(jié)點來服務(wù)其用戶,假設(shè)節(jié)點部署在全球四個區(qū)域:每個區(qū)域兩個節(jié)點,那單單是這樣一筆開銷,就超過上萬美金每月 —— 沒有充沛現(xiàn)金的小玩家,是燒不起這個錢的。所以我們希望這個數(shù)字能夠低至幾百。
然而 Digital Ocean 畢竟是服務(wù)于小客戶的,一個嚴(yán)肅的 dApp,在開發(fā)階段使用 DO 無可厚非,在生產(chǎn)環(huán)境 —— 當(dāng)鏈上線之后,更具實力的云服務(wù)是更好的選擇,比如我們自己的 ABT network 就部署在 aws。
簡約而不簡單的生產(chǎn)環(huán)境
由于 ABT network 強調(diào)織鏈為網(wǎng),我們首發(fā)三條以化學(xué)元素「氬(Argon)」「溴(Bromine)」「鈦(Titanium)」命名的元素鏈(其中 Bromine 是一條專門運行最新 nightly build 版本的測試鏈)。因而我們需要為這三條鏈準(zhǔn)備安全可信的生產(chǎn)環(huán)境。
我們是這樣考慮線上的生產(chǎn)環(huán)境的:
1. 每條鏈都部署到亞太歐美四個區(qū)域;
2. Argon 和 Titanium 各十六個節(jié)點;Bromine 四個節(jié)點
3. 所有節(jié)點都只對外暴露 p2p 端口;
4. 節(jié)點的 GraphQL RPC 和自帶的區(qū)塊瀏覽器通過 ELB 允許外部訪問,而 gRPC 只允許本地訪問;
5. 每個 region,每條鏈的 ELB 的域名,由 route 53 按照 latency 來 load balancing。
最重要的,要自動化,要足夠省錢。
自動化,這個不消說,我們已經(jīng)有深厚的 ansible / terraform 經(jīng)驗。
省錢是個學(xué)問。
按照上面的配置,哪怕只用物美價廉的 c4.large / c5.large,每個節(jié)點配 110G EBS,每條鏈每個區(qū)域都配一個 ELB,一個月下來光固定成本就要 $3721。
計算公式: 0.11 (c4.large 價格) x 36 x 24 x 31 + 36 x 110 x 0.12 (EBS 價格) + 25 (ELB 價格) x 12
這其中,EC2 占了大頭,接近 $3000。
我們的目標(biāo),是盡可能降低這個成本。
于是我們的目光投向了 spot instance。下圖是 spot instance 在 us-east-2 和 ap-southeast-1 的價格走勢:
價格基本穩(wěn)定在 on-demand instance 的 2 折,也就意味著 EC2 這塊,我們可以把成本降到 $600,總價只需 $1300 每月。
然而用 spot instance,繞不過去的坎就是萬一 instance 被殺掉,如何盡快恢復(fù)服務(wù)?尤其是驗證人節(jié)點?
我們采用的方式是 root disk 和 data disk 分離,F(xiàn)orge 存儲的所有數(shù)據(jù)放 data disk,而 Forge 的配置,節(jié)點私鑰,驗證人私鑰,放 root disk,然后在初始化之后備份到一個 AES 加密的,只允許單次寫的 S3 bucket 中。之后,在節(jié)點運行的時候,每條鏈每個區(qū)域定期備份某一個健康節(jié)點的 data disk。這樣,當(dāng)驗證人節(jié)點被殺掉時,我們可以從最近的一個備份中恢復(fù) data disk,然后從 S3 中找回該驗證人節(jié)點的私鑰和配置。
這個思路說起來挺簡單直觀,做起來可要頗費一番心思的。不過最終我們趟平了這條路,證明了它是可行的,對 dApp 開發(fā)者,甚至其他區(qū)塊鏈的同行,這種使用 spot instance 運行區(qū)塊鏈節(jié)點的方式都有借鑒意義。
最終我們的部署腳本 forge-deploy 分成四部分:
1. 只需要一次性運行的腳本:比如為每個區(qū)域每個 VPC 創(chuàng)建 security group
2. 制作 Forge AMI 的腳本:我們每 release 一個新的版本,都會創(chuàng)建一個新的 AMI。
3. 創(chuàng)建一條新鏈所需要的資源的腳本:比如創(chuàng)建 spot request,EBS,創(chuàng)建 ELB,target group,設(shè)置 listener (及 listener rules),創(chuàng)建域名及域名解析的 policy。
4. 管理一條已有鏈的腳本:比如初始化鏈,重啟節(jié)點,升級節(jié)點,修復(fù)損壞的節(jié)點,添加新的節(jié)點等
三月的最后兩周,forge-deploy 在原有零散腳本(部署 DO 機器的腳本)的基礎(chǔ)上邊開發(fā)邊測試 —— 我們的鏈建了拆,拆了建,兩周趟過了很多區(qū)塊鏈團隊可能一年都沒有趟過的路:最多的時候我們有 6 條鏈并行運行,算上那些朝生暮死的 abtchain,origin,bigbang,test,abc 等鏈,我們前前后后創(chuàng)建了和銷毀了三十多條鏈 —— 注意,這里說的是多區(qū)域多節(jié)點的鏈,單個節(jié)點的鏈并不包含在內(nèi)。
由于之前累積了足夠的自信,在 ABT Network 上線的那一天,我們自負(fù)地把之前為發(fā)布已經(jīng)創(chuàng)建好的三條鏈:Argon,Bromine 和 Titanium 在上線倒計時前不到半小時拆掉重新發(fā)布,讓整個團隊和社區(qū)關(guān)心我們的人可以看到區(qū)塊從零到一的躍遷。雖然中間有點波折 —— 部署腳本運行得比預(yù)想要慢一些 —— 因而在發(fā)布倒計時結(jié)束后我們還沒有部署完成,但最終,耽擱了大約二十分鐘,三條鏈還是如愿上線。每條鏈的部署只需要兩條命令:
其中,create_fleet 會在四個區(qū)域里都做這些事情:
1. 獲取當(dāng)前區(qū)域的 default VPC id
2. 獲取 VPC 的 subnet id
3. 獲取預(yù)先創(chuàng)建好的幾個 security group 的 id
4. 用預(yù)設(shè)的配置為驗證人節(jié)點申請 spot fleet
5. 用預(yù)設(shè)的配置為哨兵節(jié)點申請 spot fleet
6. 等待所有申請好的 instance 可以正常工作
7. 創(chuàng)建 ELB
8. 創(chuàng)建 target group,并將所有 instance 加入 target group
9. 獲取預(yù)先上傳好的證書 id
10. 創(chuàng)建兩個 ELB listener,80 端口直接 301 到 443,而 443 端口把流量轉(zhuǎn)發(fā)到 target group
11. 創(chuàng)建 DNS 域名記錄,設(shè)置 latency based policy
當(dāng)四個區(qū)域都完成之后,為這條鏈的所有 instance 創(chuàng)建 ansible inventory,以便后續(xù)處理。
接下來,在 init_forge_network 里,會做這些事情:
1. 把 data disk mount 到對應(yīng)的 instance 上,并格式化文件系統(tǒng)為 XFS
2. 使用臨時配置文件啟動 Forge,生成 node key 和 validator key
3. 把生成的 key 備份到 S3
4. 根據(jù) inventory file,找出驗證人節(jié)點,將其 validator address 寫入 genesis 配置中
5. 啟動 forge
所有節(jié)點起來后,稍候片刻,一條鏈就完美誕生了!
評論
查看更多