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