昨天,也就是 3 月 29 日,ABT Network(ABT 链网) 正式发布。ABT Network 以彻底去中心化方式链接编织多条区块链造成的网络,以云节点和织链为网的方式从新定义了新一代区块链基础架构。本文谈谈 ABT Network 诞生前发生的有趣经历。node
为方便阅读,先简单介绍一下本文谈到的一些概念:git
ABT Network:多条使用 ArcBlock 技术打造的区块链造成的网路。数据库
ABT Chain Node:ArcBlock 使用 Forge Framework 打造的区块链节点软件。浏览器
Forge Framework:ArcBlock 为区块链开发打造的框架,能够看作区块链世界里的 Ruby on Rails。安全
在开发 Forge Framework 和 ABT Chain Node 时,咱们有一个深深的信念:运行在 Forge 之上的区块链项目能够是阳春白雪,也能够是下里巴人;能够是每日千百万级 transaction 的大型应用,也能够是独立开发者及其小圈子的自娱自乐。于是,一个节点只要有过得去的算力,就能够运行 Forge,这样,只要愿意,人人均可以部署本身可负担的节点。网络
那什么算是「过得去的算力」呢?考虑到 App 开发者的开发期的经济能力,咱们将其定位在单节点月支出在 $15 之内,在 Digital Ocean 上,这对应:架构
也就是 1GB / 1CPU / 25GB disk 一路到 2GB / 2CPU / 60GB disk 的乞丐版云主机。并发
在今年一二月份的大部分开发时间里,咱们都在使用 $5 的至尊乞丐版主机 —— 并且,咱们一口气在美西 (SF),美东 (NY),西欧 (London) 和东南亚 (Singapore) 部署了四个节点,组成一个 P2P 网络,来开发 Forge。咱们相信,极端恶劣的环境,能打造尽量健壮的软件,让各类问题都提早暴露出来。app
有了环境,咱们须要有足够模拟真实应用场景的流量。为此咱们开发了一个 simulator(模拟器),而且作了一个简单的描述语言来描述咱们如何开启 simulation(节选):框架
经过改变 pool size,咱们能够调节并发程度,经过控制 tick,咱们控制 traffic 的速率;经过添加更多的 simulation,咱们改变 traffic 的多样性。
在 simulator 的做用下,很长一段时间里,咱们的开发网络三天两头 crash(崩溃) —— 一会 out of memory,一会 too many open files,一会 gen_server tiemout,一会 tcp send/receive buffer full。这些问题,若是换上个 4G memory / 4 CPU / 100G disk 的主机,只有很小的几率才暴露出来,而咱们主动让其发生在开发环境中,使得大部分问题获得了妥善处理。好比说,咱们发现咱们使用的 consensus engine(共识引擎) 不稳定,时不时 crash,crash 以后很容易把 state db(状态数据库) 写坏,使得节点完全崩溃,没法恢复。对此,咱们的作法是,一旦 consensus engine crash,咱们让 Forge 自动 crash(惋惜了 erlang VM 强大的 crash recover 机制),而后由咱们开发的 forge starter 将 forge 重启。重启后,咱们回溯到上一个区块的数据,从新 apply,若是 consensus engine 能够恢复,那么旧继续日后走;不然便继续 crash 和继续回溯。
在这样严苛的环境下 Forge 逐渐成长,至尊乞丐节点组成的网络,不断死亡,不断重生,就像「明日边缘」里的汤姆克鲁斯,从小白一路成长为小强,迎来了第一百万个 transaction。
好景不长,在大约 1.5M txs 时,网络再次 crash:
此次 crash 的一干二净,全部节点全军覆没,连 ssh 都上不去。Digital Ocean 的监控显示 CPU 基本为 0,正琢磨着是否是 disk 写满了,一台机器回光返照,给我登上去 du 的机会。果真,25G 的 disk 被吃得一干二净。take snapshot,换大硬盘,搞定。
三月上旬咱们终于抛弃了 $5 的机器,换装 $15 的「大」节点。在 Digital Ocean 的云上,咱们同时跑了好几个网络,作 rolling upgrade。以前咱们一周一个 milestone,出一个大版本,若干小版本,三月第二周起,咱们天天出一个版本,于是,版本太多而网络不够用了。。。
很快,1 million txs 的里程碑被 5 million 取代:
继而被 6M,7M,… 取代。后来咱们 breaking change 太多,也就没有继续累积这个数字。
可让区块链节点稳定地在 $15 的机器上部署是咱们 ArcBlock 的一个创举。咱们作过别的公链的节点 —— 对方给出的推荐配置,一个节点一个月要一千多美金。若是一个应用开发者开发者,想部署一个本身的链,初期经过本身的节点来服务其用户,假设节点部署在全球四个区域:每一个区域两个节点,那单单是这样一笔开销,就超过上万美金每个月 —— 没有充沛现金的小玩家,是烧不起这个钱的。因此咱们但愿这个数字可以低至几百。
然而 Digital Ocean 毕竟是服务于小客户的,一个严肃的 dApp,在开发阶段使用 DO 无可厚非,在生产环境 —— 当链上线以后,更具实力的云服务是更好的选择,好比咱们本身的 ABT network 就部署在 aws。
因为 ABT network 强调织链为网,咱们首发三条以化学元素「氩(Argon)」「溴(Bromine)」「钛(Titanium)」命名的元素链(其中 Bromine 是一条专门运行最新 nightly build 版本的测试链)。于是咱们须要为这三条链准备安全可信的生产环境。
咱们是这样考虑线上的生产环境的:
每条链都部署到亚太欧美四个区域;
Argon 和 Titanium 各十六个节点;Bromine 四个节点
全部节点都只对外暴露 p2p 端口;
节点的 GraphQL RPC 和自带的区块浏览器经过 ELB 容许外部访问,而 gRPC 只容许本地访问;
每一个 region,每条链的 ELB 的域名,由 route 53 按照 latency 来 load balancing。
最重要的,要自动化,要足够省钱。
自动化,这个不消说,咱们已经有深厚的 ansible / terraform 经验。
省钱是个学问。
按照上面的配置,哪怕只用物美价廉的 c4.large / c5.large,每一个节点配 110G EBS,每条链每一个区域都配一个 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。
咱们的目标,是尽量下降这个成本。
因而咱们的目光投向了 spot instance。下图是 spot instance 在 us-east-2 和 ap-southeast-1 的价格走势:
价格基本稳定在 on-demand instance 的 2 折,也就意味着 EC2 这块,咱们能够把成本降到 $600,总价只需 $1300 每个月。
然而用 spot instance,绕不过去的坎就是万一 instance 被杀掉,如何尽快恢复服务?尤为是验证人节点?
咱们采用的方式是 root disk 和 data disk 分离,Forge 存储的全部数据放 data disk,而 Forge 的配置,节点私钥,验证人私钥,放 root disk,而后在初始化以后备份到一个 AES 加密的,只容许单次写的 S3 bucket 中。以后,在节点运行的时候,每条链每一个区域按期备份某一个健康节点的 data disk。这样,当验证人节点被杀掉时,咱们能够从最近的一个备份中恢复 data disk,而后从 S3 中找回该验证人节点的私钥和配置。
这个思路提及来挺简单直观,作起来可要颇费一番心思的。不过最终咱们趟平了这条路,证实了它是可行的,对 dApp 开发者,甚至其余区块链的同行,这种使用 spot instance 运行区块链节点的方式都有借鉴意义。
最终咱们的部署脚本 forge-deploy 分红四部分:
只须要一次性运行的脚本:好比为每一个区域每一个 VPC 建立 security group
制做 Forge AMI 的脚本:咱们每 release 一个新的版本,都会建立一个新的 AMI。
建立一条新链所须要的资源的脚本:好比建立 spot request,EBS,建立 ELB,target group,设置 listener (及 listener rules),建立域名及域名解析的 policy。
管理一条已有链的脚本:好比初始化链,重启节点,升级节点,修复损坏的节点,添加新的节点等
三月的最后两周,forge-deploy 在原有零散脚本(部署 DO 机器的脚本)的基础上边开发边测试 —— 咱们的链建了拆,拆了建,两周趟过了不少区块链团队可能一年都没有趟过的路:最多的时候咱们有 6 条链并行运行,算上那些朝生暮死的 abtchain,origin,bigbang,test,abc 等链,咱们前先后后建立了和销毁了三十多条链 —— 注意,这里说的是多区域多节点的链,单个节点的链并不包含在内。
因为以前累积了足够的自信,在 ABT Network 上线的那一天,咱们自负地把以前为发布已经建立好的三条链:Argon,Bromine 和 Titanium 在上线倒计时前不到半小时拆掉从新发布,让整个团队和社区关心咱们的人能够看到区块从零到一的跃迁。虽然中间有点波折 —— 部署脚本运行得比预想要慢一些 —— 于是在发布倒计时结束后咱们尚未部署完成,但最终,耽搁了大约二十分钟,三条链仍是如愿上线。每条链的部署只须要两条命令:
其中,create_fleet 会在四个区域里都作这些事情:
获取当前区域的 default VPC id
获取 VPC 的 subnet id
获取预先建立好的几个 security group 的 id
用预设的配置为验证人节点申请 spot fleet
用预设的配置为哨兵节点申请 spot fleet
等待全部申请好的 instance 能够正常工做
建立 ELB
建立 target group,并将全部 instance 加入 target group
获取预先上传好的证书 id
建立两个 ELB listener,80 端口直接 301 到 443,而 443 端口把流量转发到 target group
建立 DNS 域名记录,设置 latency based policy
当四个区域都完成以后,为这条链的全部 instance 建立 ansible inventory,以便后续处理。
接下来,在 init_forge_network 里,会作这些事情:
把 data disk mount 到对应的 instance 上,并格式化文件系统为 XFS
使用临时配置文件启动 Forge,生成 node key 和 validator key
把生成的 key 备份到 S3
根据 inventory file,找出验证人节点,将其 validator address 写入 genesis 配置中
启动 forge
全部节点起来后,稍候片刻,一条链就完美诞生了!