拓冰建站拓冰建站
首页 / 资讯中心 / 正文

用Zombienet搭建Substrate平行链测试网络:从配置到CI集成

先从一个真实场景说起。我在本地开发一条基于 Substrate 的平行链业务逻辑写了大半单节点--dev模式跑得飞起但一旦要验证跨链转账、插槽拍卖、收集人切换、验证人共识这类场景单节点立刻就不够用了。手动打开一堆终端挨个启动polkadot和polkadot-parachain进程改端口、改 chain spec、等同步、再祈祷不出错。跑起来之后一旦环境变了又无法复现调试成本比写业务逻辑还高。后来切到 Zombienet这个问题才真正解决一条命令复现一整套中继链加平行链的网络。Zombienet 是 Polkadot/Substrate 生态里专门做网络编排的轻量工具。你只需要写一个 TOML 文件描述网络里有哪些节点、哪些平行链、用什么命令启动Zombienet 就会负责拉起整个进程组、等待节点就绪、收集日志还给你一个交互式命令行随时查看状态。这篇文章我就用实际跑过的配置来讲从最简单的一条中继链开始到把平行链接进来再到调试和 CI 集成希望帮你少踩一点我踩过的坑。1. 为什么平行链开发需要一套可编排的模拟网络1.1 单节点 dev 模式与真实网络的差距到底在哪很多 Substrate 开发者上手时习惯用polkadot-parachain --dev这个模式非常快非常适合验证 runtime 的业务逻辑。但--dev本质上只是本地单节点它把所有共识相关的部分都弱化了。你验证不了两个 collator 之间的交接验证不了中继链验证人对平行链候选区块的确认也验证不了跨链消息从一条平行链发出、经中继链转发、再进入另一条平行链的完整路径。当你的项目开始涉及 XCM、HRMP、插槽拍卖、根据中继链状态做出的链上决策时单节点模拟的偏差会让测试结果失真。最典型的例子是你在--dev模式下给平行链配置一个assets模块自己发行资产、转账都很正常但一旦要跟另一条平行链互操作你会发现中继链根本没有注册你的 para id也没有 collator 在持续提交候选区块整个跨链链路根本走不通。1.2 Zombienet 的定位网络编排器不是一个测试框架Zombienet 做的事情可以概括成一句话把“启动一整套测试网”变成可声明、可重复的操作。你写一个拓扑文件它负责把中继链验证人、平行链收集人全部拉起来等网络健康后告诉你“可以开始干活了”。值得注意的是Zombienet 本身不是测试框架它不负责断言业务逻辑是否正确。它更像是 DevOps 里的编排工具类似本地用 docker-compose 拉起一组容器只不过它拉起的是一个个区块链节点进程并且对这些进程做了很多区块链接口层面的探测比如等待某个节点同步到最新区块、确认 parachain 是否注册成功、检查 collator 是否开始产块。有了这个“环境生产器”你再在上层接自己的测试脚本、断言逻辑就会轻松很多。1.3 与手动脚本和 polkadot-launch 的对比在 Zombienet 流行之前手动脚本和polkadot-launch是两种主流方案。手动脚本的优点是灵活缺点也最明显每个开发者维护一套自己的启动脚本端口、日志目录、chain spec 的生成逻辑各不相同导致环境很难复现。polkadot-launch比脚本规范一些也支持配置中继链和平行链但它更新节奏偏慢对较新的 Runtime 版本、对容器和云环境的支持不如 Zombienet 灵活。对比项手动脚本polkadot-launchZombienet网络拓扑描述每个脚本一套配置文件配置文件TOML等待节点就绪自行 sleep/轮询较弱内置健康检查日志收集手工重定向一般按节点分文件支持 logs 命令容器/云支持维护成本高有限native / podman / kubernetes交互式调试无有限有独立交互命令维护活跃度看个人较低官方生态主力工具我自己的体会是Zombienet 最值钱的地方不是“能启动网络”而是“等待就绪 可交互调试 日志规范”这一整套开发体验。它把这些分散的脏活收拢成了一个固定流程让你把注意力放回平行链业务本身。2. 从零启动第一条中继链网络安装、配置、验证2.1 安装方式与版本注意点Zombienet 有两个常用安装方式npm 包和 GitHub Release 里的二进制。我一般用 npm 方式因为升级方便也更容易在 CI 里指定版本。# 全局安装 npm install -g zombienet/cli # 或者不全局安装直接用 npx npx --yes zombienet/cli --version如果你更喜欢单文件二进制去官方 Release 页面下载对应平台的文件比如zombienet-linux-x64然后加执行权限就行。注意一个容易被忽略的点Zombienet 本身不打包 Polkadot 相关二进制它只是帮你拉起外部进程。所以你在[relaychain]里指定的default_command指向的polkadot以及平行链 collator 的polkadot-parachain都需要你自己提前准备好并且最好和当前项目使用的版本保持对齐。另外提醒一句Zombienet 的 CLI 参数在新老版本之间有一些调整。老版本常见写法是zombienet -p config.toml新版本则更倾向于子命令形式zombienet spawn -p config.toml。如果你手头的版本提示参数不对先跑一下zombienet --help看看当前版本支持什么写法不要硬记某一条命令。2.2 最小可用配置三条验证人的中继链先从一个最简单的网络开始不接平行链只启动一条本地中继链用三个默认的开发账号作为验证人。配置文件relaychain.toml长这样[relaychain] default_command polkadot chain rococo-local [[relaychain.nodes]] name alice validator true [[relaychain.nodes]] name bob validator true [[relaychain.nodes]] name charlie validator truechain rococo-local表示使用本地生成的 Rococo 开发链 spec不需要连接外部测试网。alice、bob、charlie是 Substrate 开发链里默认存在的预置账号它们有足够的余额和 session key直接可以当验证人用。validator true让 Zombienet 启动节点时带上--validator参数。2.3 启动命令与验证网络是否健康在配置文件和polkadot可执行文件都准备好的目录里运行zombienet spawn -p relaychain.toml启动过程会依次拉起 alice、bob、charlie 三个节点然后等待它们彼此连接并开始出块。最终你会看到类似输出告诉你每个节点的 WebSocket 端口和 Prometheus 端口还会给出日志目录。此时按回车会进入交互模式输入help能看到支持的命令列表。我最常用的验证方法是直接看日志logs alice正常出块时alice 的日志里会持续出现Prepared block for proposing at 123一类的信息说明中继链已经在本地稳定出块。你也可以直接用浏览器打开polkadot.js/apps在设置里把节点地址指向本地端口比如ws://127.0.0.1:9944检查区块是否在增长。这一步跑通了说明 Zombienet 的基本链路没有问题可以开始往配置里加平行链。3. 把平行链接进网络TOML 配置的完整拆解3.1 中继链、平行链、收集人三者怎么配合在写配置之前值得花三十秒把三个角色的关系理清楚。中继链relaychain是整个网络的安全和共识层它自身可以出块但更重要的是接收并验证各条平行链提交的候选区块。平行链parachain跑的是自己的 runtime有自己的业务逻辑但没有独立共识它的出块节点叫做 collator中文常称“收集人”。collator 的职责可以理解成“帮平行链把区块整理好再递交给中继链的验证人”。验证人验证通过后这个区块就获得了中继链级别的最终性保证。所以你在配置里要做的事很清晰启动一条中继链然后启动一个或多个 collator让它们作为平行链的区块生产者同时接入同一条中继链。用个生活化的类比中继链像一条主干道验证人是交警平行链是接入主干道的支线公路collator 是支线路口的工作人员负责把支线上的车区块整理好并引导到主干道上交给交警验收。3.2 一个可运行的平行链网络配置示例下面这个配置我在本地验证过包含两条验证人的中继链和一个 parachain id 为 100 的平行链[relaychain] default_command polkadot chain rococo-local default_args [-lrinfo] [[relaychain.nodes]] name alice validator true [[relaychain.nodes]] name bob validator true [[parachains]] id 100 add_to_genesis true [parachains.collator] name collator01 command polkadot-parachain args [-lparachaindebug, --parachain-id100, --tmp]关键点有三个。第一add_to_genesis true让这条平行链在创世阶段就被注册到中继链上省掉了后续手动提交注册交易的麻烦。如果你把这个字段改成false就需要在链跑起来之后通过治理模块手动注册调试时会多一步操作不是不行只是没必要。第二[parachains.collator]下面的command指向你自己编译出来的平行链节点可执行文件。如果你是用 Cumulus 模板开发的平行链这个文件通常是polkadot-parachain但更准确的是你项目target/release/下自己的节点二进制比如my-parachain。第三collator 的args里必须带上--parachain-id100这个值要和[[parachains]]的id一致。不一致时 collator 就算启动成功也会因为 para id 对不上注册流程而无法正常产块。这个错误很隐蔽我见过几次才记住。3.3 启动后最容易遇到的三个问题跑平行链网络时最常见的现象是 collator 一直在重启或者平行链迟迟不出块。第一类问题是 collator 连不上中继链。日志里出现Could not connect to the relay chain这类信息时先确认中继链是不是已经在正常出块然后确认 collator 启动参数里的 chain 是不是rococo-local和 relaychain 的chain字段保持一致。其次确认两个二进制文件的版本是否兼容平行链节点和中继链节点的网络层、Runtime 版本对不上时连接非常不稳定。第二类问题是平行链不产块但 collator 日志里也没有明显错误。这种情况多数是平行链尚未真正注册到中继链上。你可以用add_to_genesis true避免这个问题如果已经启动了去中继链的链上状态里查一下 para id 100 是否存在于 parachains 相关模块中。没有的话就得走注册流程再处理。第三类是端口冲突。默认情况下 Zombienet 会给节点分配递增的 WebSocket 端口但如果你本机有旧进程占用 9944/9945 等端口节点会启动失败。此时要么先清掉残留进程要么在节点配置里显式指定ws_port、port等字段。前者更省事pkill -f polkadot这个命令在调试时非常常用因为 Zombienet 偶尔退出时来不及清理所有子进程残留的节点会占着端口干扰下一次启动。4. 网络跑起来之后如何调试交互命令、日志与崩溃现场4.1 交互模式里值得记住的常用命令Zombienet 启动完成后默认进入交互模式按回车或者直接输入命令就能用。不需要背太多最常用的这几个足够覆盖大部分调试场景命令作用使用场景help查看所有命令临时确认某个命令是否存在logs alice查看 alice 节点的日志快速看节点状态ping alice检测节点 WS 接口是否响应判断节点是否活着kill alice杀掉指定节点模拟节点宕机restart alice重启指定节点验证恢复能力show查看当前网络整体状态确认所有节点状态我建议你在调试线上问题之前先在本地把kill和restart用一遍。它能帮你验证平行链在没有某个 collator 时是否依然能向中继链提交区块以及重启后是否能恢复产块。这些场景通过手动脚本模拟很麻烦但 Zombienet 只需要两条命令。4.2 排查 collator 不产块的完整思路有一次我在本地跑平行链collator 进程一直活着中继链也正常出块但平行链高度始终停在一个区块。当时的排查链路大概是这样。第一步确认 collator 有没有在正常发起区块生产。我用logs collator01查看日志发现里面有大量Preparing a proposal之类的信息说明 collator 自己认为它在工作。第二步查看 collator 是否把区块提交给了中继链。这时候需要把日志级别调高在 collator 的args里加-lparachaindebug然后重新启动网络。日志里能看到类似Candidate submitted to relay chain的记录如果完全没有说明提交路径有问题。第三步检查中继链是否在验证这个候选区块。这时看中继链节点的日志尤其是验证人节点的日志。如果中继链收到了候选区块会有相应的Candidate backed、Candidate included日志。如果只到backed没有included往往是平行链的 wasm runtime 与中继链验证逻辑不兼容最常见的原因是版本不匹配。那次问题的最终原因就是平行链节点的版本比中继链节点老了两三个 release两个二进制对区块头的处理细节不一致导致验证永远不通过。升级到匹配版本后就好了。4.3 日志管理可复现性的最后一个环节Zombienet 默认会把日志写到临时目录退出后未必能保留。如果你需要把现场保存下来分析或者给同事传递排查素材可以在启动时指定日志目录zombienet spawn -p network.toml --log-dir /tmp/mynet-logs这个习惯在 CI 里尤其重要。很多测试失败是间歇性的光靠控制台输出很难定位。我会在 CI 里把整个日志目录上传为构建产物失败时直接下载 logs 查看每一个节点的完整输出。Zombienet 的日志文件是以节点名命名的logs/alice.log、logs/collator01.log这样分得很清楚查问题比看一堆混在一起的终端输出舒服太多。5. 资源占用、CI 集成与 provider 选择的实战心得5.1 本地开发时怎么让测试网轻一点Zombienet 默认配置下每个节点都会占用不少内存因为每个节点都是一个独立的区块链进程都要维护自己的数据库和网络连接。我在内存只有 16G 的笔记本上跑过两条验证人加一个 collator 的网络任务管理器一开内存占用接近 8G编译 Rust 的时候会非常紧张。减少资源占用的办法有几个。中继链验证人不一定非要三条两条也能跑通alice和bob足够。日志级别从debug降到info不要小看这一步-lrdebug在节点跑一段时间后产生的日志量和内存开销都很大。然后是尽量用--tmp这类参数让节点数据写在临时目录退出后自动清理避免多次启动后磁盘空间被链数据撑满。5.2 把 Zombienet 集成到 GitHub Actions在 CI 里用 Zombienet要解决的问题比本地多一个如何让网络在后台运行同时让测试脚本能够访问它。我的做法是后台启动 Zombienet然后跑测试脚本最后统一清理- name: Run integration tests run: | nohup ./zombienet spawn -p ./zombienet/network.toml --headless zombie.log 21 sleep 30 ./scripts/integration-test.sh status$? pkill -f polkadot exit $status--headless表示不进入交互模式适合自动化场景。sleep 30是给网络留出启动时间具体数值取决于你的节点二进制大小和机器性能。更稳妥的做法是写一个等待脚本轮询某个 RPC 端口直到网络出块而不是固定 sleep。CI 里最容易踩的坑是 worker 资源不足。GitHub Actions 的免费 runner 内存有限跑两三个节点还可以再多就容易 OOM。如果你确实需要一整套大网络建议用自托管 runner 或者把网络拆成多个更小的测试场景没必要每次都在同一个 runner 里跑满全量节点。5.3 provider 怎么选native、podman 还是 kubernetesZombienet 支持多种 provider本地开发最常用的是native它直接使用你本机的二进制启动快、日志直接、调试最方便。如果你在团队协作希望环境一致可以用podman或docker方式跑容器镜像但代价是镜像拉取时间长容器网络也会带来一定延迟。kubernetes主要用于大规模云上测试普通平行链项目一般用不上。我在实际使用中的习惯是本地用native配合自己编译的二进制调试最顺手CI 里也优先用native但会固定依赖的版本只有环境差异问题反复出现时才考虑切到容器 provider。容器方案能解决环境不一致但会引入新的网络和性能问题属于非必要不启用。最后分享一点个人经验我现在几乎每个平行链项目都会在仓库里放一个zombienet/目录把网络拓扑配置当作工程文档一样维护。配置写清楚后本地调试、CI 回归、同事接手项目都能用同一条命令复现同一套网络。建议你先从最小配置跑通再逐步加节点、加平行链、加 HRMP 通道不要一上来就铺一大堆节点。网络本身越简单你定位问题时的认知负担就越小。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门