Polkadot 上的平行链出块节点有个专门名字:Collator,平时聊天里大家更习惯叫它“收集人”。我前前后后帮团队部署过好几套 Collator,最常用的组合就是 Docker 装节点、Systemd 管进程。这篇我打算把从空服务器到真正参与出块的完整链路讲透,包括硬件准备、镜像选择、容器启动、Systemd Service 编写、Session Key 注册,以及上线后怎么判断节点到底有没有在干活。
如果你是第一次接触这类节点,建议把这篇文章当成一份可以直接照着抄的作业;如果你已经跑过节点,重点看后面注册和排查部分,里面很多坑不是文档里会写的。整条链路拆开看都不复杂,但串起来之后,任何一个环节配错都可能让你白跑一晚上同步,所以我尽量把每一步的“为什么”也讲清楚。
1. 先搞懂 Collator 要干什么,再谈部署
1.1 Collator 在 Polkadot 生态里承担的职责
Polkadot 是一条中继链,它本身不处理太多平行链业务,真正跑业务的是挂在它上面的平行链。Collator 就是负责给平行链出块的节点。它要做的事情,概括起来就两件:收集平行链上的用户交易,打包成一个候选区块;把这个候选区块提交给中继链的验证人。验证人经过有效性检查后,这个区块才算真正上链。
所以 Collator 和验证人不是一回事。验证人是 Polkadot 中继链的安全核心,负责最终确认;Collator 更像是一条条平行链自己的“区块生产者”。这个角色有个很大的好处,就是单个 Collator 需要质押的门槛通常比验证人低,很多平行链还允许团队或社区候选节点参与出块,这也是为什么不少人愿意折腾 Collator 的原因。
理解了职责之后,你就知道一个 Collator 节点至少需要两层能力:一层是能持续同步中继链状态,另一层是能稳定运行平行链的 Runtime 并打包区块。听起来复杂,实际操作上就是一个polkadot-parachain客户端同时做这两件事,不需要你额外跑两套进程。
1.2 为什么把 Docker 和 Systemd 放在一起用
很多跑节点的朋友一开始只会在终端里敲一条命令,然后开着screen或者tmux挂着。说实话,短时间测试可以,长期生产我强烈不建议。一旦服务器重启、SSH 断了、进程意外退出,节点就趴了,你可能过了好几个小时才发现在线率已经不行了。
用 Docker 的动机很直接:节点二进制、Runtime、依赖库全打包进镜像,换机器部署时不会因为系统库版本不一致而翻车。Systemd 则负责进程级守护,开机自动拉起来、崩溃自动重启、日志统一收到 journald 里。两者配合,正好互补:Docker 解决“环境一致性”,Systemd 解决“进程生命周期”。
有人会问,Docker 自己不是有--restart=unless-stopped吗?为什么还要 Systemd?因为 Docker 的 restart 策略只在 Docker 守护进程活着的时候才生效,如果服务器重启过程里 Docker 依赖的底层网络或者存储没准备好,容器可能起得比你预期的早或晚。Systemd 的After=docker.service、Requires=docker.service能严格保证依赖顺序,还可以在节点异常退出时按你的节奏做更精细的重启退避。
1.3 部署前必须理解的角色边界
得先说清楚一个容易误会的点:Collator 并不是你想出块就能立刻出块。每条平行链都会有自己对 Collator 的准入机制,常见的是绑定该链的 Staking 代币、参与治理投票、由链团队或治理提案批准进入候选集。也就是说,节点部署只是第一步,真正“入列”还涉及链上的 Session Key 认证和注册流程。
我见过不少新手把节点跑起来,看到日志里有“Imported”就以为万事大吉,结果一个 Session 也没出块。原因往往是他在链上层面根本没有完成注册。后面我专门用一章讲注册链路,这里先记住一个结论:节点软件层面跑通只是“能联网、能同步”,链上层面认可你才是“有资格出块”,两者缺一不可。
2. 服务器环境准备:硬件、系统与 Docker 底座
2.1 硬件和网络的最低可上生产标准
Collator 的硬件要求跟平行链业务量、历史数据大小、同步模式都有关系。我这里给的是我实际跑过的底线,不是官方最低配置。
CPU 建议 8 核以上,最好主频高的,比如 AMD EPYC 或 Intel Xeon 的 3GHz 以上型号。内存我建议 32GB 起步,64GB 会更省心。磁盘是关键,SSD 是硬性要求,千万别拿机械硬盘跑节点,同步阶段能把人急死。容量的话,中继链同步加上平行链数据,几百 GB 到 1TB 都是常见情况,建议直接上 1TB 企业级 NVMe 或者至少预留足够余量。
网络方面最重要的是带宽稳定和延迟低。出块节点对公网带宽要求不算变态,但需要稳定的国际出口链路,因为你的对端可能分布在很多地区。P2P 端口不要被防火墙拦,这个比下载带宽更难排查。云服务器的话,安全组里要把 30333 这类 P2P 端口放行,否则节点日志里全是“No peers”,链都同步不了。
系统我用的基本都是 Ubuntu 22.04 LTS 或 Debian 12,CentOS/Rocky Linux 也能跑,只是 Docker 与内核模块在部分发行版上要额外做一些配置。不推荐在桌面系统上跑生产节点,Windows 加 Docker Desktop 的组合更适合本地测试,不适合长期挂机。
2.2 安装 Docker 以及几个容易翻车的起点
生产服务器上装 Docker,我建议直接用官方仓库,不推荐用发行版自带的旧版本。以 Ubuntu 为例,大概流程就是更新索引、装依赖、添加官方 GPG 和仓库、然后安装docker-ce。这一步有个常见问题是部分国内服务器拉取 Docker 官方源很慢,解决方案是配置一个合适的镜像加速器,但不要随便相信来路不明的第三方脚本。
装完之后先验证一下:
sudo systemctl enable --now docker sudo docker run --rm hello-world能正常打印出 Hello 信息,说明 Docker 守护进程没问题。如果你是在自己的 Windows 笔记本上装 Docker Desktop 做实验,遇到“virtualisation support not detected”这类提示,方向基本只有一个:BIOS 里的虚拟化功能没开,或者 WSL2 没装好。这个属于开发机问题,生产 Linux 服务器上很少遇到。
还有一个很关键的权限坑:不要图省事让所有用户都能直接跑 Docker。docker组等同于 root 权限,如果服务器还要跑其他业务,最好单独建一个node用户来管理节点容器,而不是把系统用户一股脑加进 docker 组。
2.3 给节点准备独立目录和基础参数
Collator 跑起来之后会持续写数据,这个目录最好独立规划。我习惯在/opt/collator下面建目录,数据放/opt/collator/data,链配置和密钥放/opt/collator/config。这样后续备份、迁移都很清晰,不会跟系统其他目录混在一起。
创建目录后,权限设置也很重要。如果 Systemd 里用node用户启动容器,宿主机上的数据目录需要让这个用户有读写权限,否则容器里挂载出来的数据写不进去,日志会刷权限错误。
sudo mkdir -p /opt/collator/{data,config} sudo useradd -r -s /usr/sbin/nologin node sudo chown -R node:node /opt/collator另外,启动节点前要准备好链的 chain spec。大部分 Polkadot 平行链官方仓库都会提供主网或测试网对应的 chain spec 文件,有的直接用内置的--chain参数即可。第一次同步时如果不确定,可以先跑一次--help看看当前版本支持的链参数,避免把旧文档的参数硬套到新版本上。
3. 用 Docker 把 Collator 节点跑起来
3.1 镜像选型:不用第三方打包镜像
镜像选择是很多人容易忽略但非常致命的一步。Collator 客户端的编译产物是直接跟链 Runtime 打交道的,如果用了一个被篡改或者过期的第三方镜像,很可能造成区块数据污染,甚至私钥泄露。我的原则很简单:只使用平行链官方发布的镜像,或者官方仓库里 Dockerfile 构建出来的产物。
以常见的 Polkadot 平行链客户端为例,官方仓库通常叫polkadot-parachain,会定期发布编译好的二进制和镜像。镜像 tag 一般对应版本号,比如某个 release 版本。选版本时不要盲目追最新,也不要一直停在旧版,重点看这个链官方公告里要求的是哪个版本,因为 Runtime 升级后旧版客户端很可能无法继续出块。
镜像拉取后用docker inspect看一眼创建时间和镜像 ID,心里有个数。生产环境里我还会保留一份对应版本的二进制文件做备用,万一容器镜像仓库临时抽风,还能用 Systemd 直接拉起二进制应急。
3.2 一条可对照的 docker run 命令
下面这条命令是基于 Linux 主机的实践,直接用 host 网络模式,方便 P2P 端口和多端口监听。不同平行链的参数有差异,我特意保留了注释习惯,你对照自己的链调整:
docker run -d \ --name collator-prod \ --restart unless-stopped \ --network host \ -v /opt/collator/data:/data \ -v /opt/collator/config:/config \ parity/polkadot-parachain:你确认的版本 \ --chain 你的链名或chain-spec路径 \ --collator \ --parachain-id 你的平行链ID \ --name 节点名称 \ --base-path /data \ --port 30333 \ --rpc-port 9933 \ --ws-port 9944 \ --execution wasm \ --state-cache-size 524288000使用--network host的理由是 Collator 涉及的端口很多,尤其是 P2P 端口如果靠-p映射,容器网络层会有额外开销,还可能因为 UDP/TCP 端口映射不全导致对端连不上。host 模式下容器直接复用宿主机网络栈,端口不用映射,性能更好,也少一层 NAT 问题。缺点也很明显:如果同机跑多个链节点,端口冲突需要自己协调。
--parachain-id这个参数必须跟你要服务的那条平行链匹配,填错了节点即使同步成功也不会参与出块。我遇到过有人把中继链的链 ID 填到平行链参数里,日志上看起来一切正常,但就是一直不出块。--execution wasm是保证按链上 Runtime 执行,新版本客户端即使升级也不能改变链上逻辑。
3.3 数据卷、端口映射和日志落盘的坑
有人喜欢把数据目录直接映射到容器里一个很小的默认路径,结果过几天磁盘就满了。我建议启动前先确认-v /opt/collator/data:/data里的宿主机目录是挂载在大容量磁盘上,并且文件系统是 ext4 或 xfs,别用某些云厂商默认压缩或限速的奇葩挂载。
P2P 端口在 host 网络模式下不需要映射,但宿主机防火墙还是要单独放行。云服务器的安全组和系统里 ufw/firewalld 都要检查,缺一个都可能导致节点看起来在线,实际上入站连接一直被拒。有个排查技巧是直接telnet 你的公网IP 30333测连通性,比看日志里“No peers”更直接。
容器日志默认走的是 Docker 的 json-file driver,时间长了会积出来很大的日志文件。对 Collator 这种长期进程,我习惯给 Dockerd 加一个日志轮转限制:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" } }配置写在/etc/docker/daemon.json,重启 Dockerd 生效。这里提醒一句,改动 daemon 配置会重启整个 Docker 服务,如果机器上还有别的容器,最好挑维护窗口操作。
4. 用 Systemd 把节点变成可托管的常驻服务
4.1 service 文件里真正决定生死的关键字段
直接跑docker run -d虽然省事,但进程管理还是太弱了。生产环境我会用 Systemd 统一管理容器生命周期。先写一个 service 文件,放在/etc/systemd/system/collator.service:
[Unit] Description=Polkadot Parachain Collator After=docker.service Requires=docker.service [Service] User=node Group=node Restart=always RestartSec=20 TimeoutStopSec=120 ExecStart=/usr/bin/docker start -a collator-prod ExecStop=/usr/bin/docker stop -t 60 collator-prod [Install] WantedBy=multi-user.target这个方案的前提是你已经用一个手动docker create或之前的docker run创建好了容器,名字固定为collator-prod。Systemd 启动时执行docker start -a,这个-a很关键,它会把容器的标准输出和标准错误接到 Systemd 的日志上,后续用journalctl就能直接看节点日志。
User=node这里不只是好看。容器默认以 root 跑会带来审计和安全问题,而数据目录已经给了node用户权限,所以用普通用户启动容器是合理的最低权限实践。TimeoutStopSec=120要够长,Collator 停止时可能需要把内存中的区块状态写回磁盘,给太短的停止时间会导致数据损坏。
4.2 日志统一进 journald,排查才不抓瞎
容器日志进了 Systemd 之后,最爽的一点是所有日志都能用journalctl查,不用再进容器里翻。常用命令:
sudo journalctl -u collator -f --since today sudo journalctl -u collator --since "1 hour ago" | grep -i error sudo journalctl -u collator -b -1 # 看上一次启动日志日志量大的节点,journald 默认可能限制容量,配置一下/etc/systemd/journald.conf里的SystemMaxUse=2G,避免日志把/var撑爆。配合前面的 Docker 日志轮转,双保险。
有一个细节:Systemd 在ExecStart里跑的是宿主机命令,如果命令路径不对会直接失败。务必用which docker确认你的 Docker 路径,我见过有人写成/usr/bin/docker结果实际装的是/usr/local/bin/docker,服务永远起不来。
4.3 Systemd 与 Docker 双守护的正确分工
容器如果设了--restart unless-stopped,同时 Systemd 又配置了Restart=always,看起来像双重守护,实际会打架。容器崩溃时 Docker 先拉起,Systemd 还没来得及感知;服务器重启时,两边都可能尝试恢复。更合理的做法是只让一边做主守护。
我生产上的习惯是这样:容器创建时不设置--restart,也就是容器自身不负责重启,完全交给 Systemd。Systemd 的Restart=always会在容器异常退出或宿主机重启后拉起容器;After=docker.service又保证 Docker 进程先起来。这样责任清晰,排查崩溃原因时也不会出现“谁把容器又拉起来了”的迷惑现场。
改完 service 文件后要重新加载:
sudo systemctl daemon-reload sudo systemctl enable --now collator sudo systemctl status collator5. Collator 注册与出块验证的真实链路
5.1 Session Key 生成和提交
节点跑起来只是“报名”,真正让链上认定你具备出块资格,需要完成 Session Key 的提交。Collator 内部会通过author_rotateKeys这个 RPC 方法生成一组会话密钥,密钥本身会跟你的节点账户绑定。
通常做法是先在本地生成账户,把账户的公钥地址导入到该平行链的官方应用里,绑定一定数量的 Staking 或满足链的准入门槛。然后再调 RPC 旋转密钥:
curl -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"author_rotateKeys","params":[]}' \ http://127.0.0.1:9933返回的0x开头字符串就是 session keys。拿到之后,通过链上应用或命令行提交session.setKeys。提交前务必确认发送人是你的节点账户,且账户里有足够支付手续费和保证金的代币。这一步如果提交错账户,后面链上校验永远对不上。
密钥生成是一个非常敏感的操作。不要在生产服务器上随便把助记词传给陌生工具,更不要用网上“帮你生成节点密钥”的网页。所有签名操作尽量在官方应用或可信离线环境里做,这是底线。
5.2 确认自己进入候选集并开始出块
Session Key 提交之后,并不代表下一个 Session 立刻就开始出块。链上一般按照 Session 轮换,有些链还有候选池排队机制。你需要观察的是,等待一个到几个 Session 后,链上是不是把你的节点账户加入了 Collator 候选集。
怎么确认?第一看链上官方应用的 Collator 列表,看你的账户是否在列表中;第二看节点日志,Collator 参与出块时通常会出现类似Proposing block、Produced block、Prepared block for proposing的记录;第三看平行链的区块浏览器,确认你产生的区块有没有被打进最终链里。
我第一次部署时,以为提交 Session Key 就完事了,结果日志里稳定出现“collator skip due to no accounts”之类提示。后来才发现是节点启动参数里必须指定--collator并且用带正确账户的 keystore 路径,或者通过 RPC 把节点账户加载进本地 keystore。每个链的指令不一样,这个地方一定要看链官方文档,别套用其他链的命令。
5.3 上线初期的节点健康度和出块稳定性评估
出块稳定比出块速度快更重要。上线头几天,我会盯着几个核心指标:Peer 数量是否稳定、区块同步是否有滞后、节点内存是否持续上涨、出块间隔是否均匀。Collator 如果隔几个 Session 才出一个块,通常问题不大,但如果不该出块的 Session 里频繁报错,就要查是不是密钥没配对或者链配置不对。
我给 Collator 这个角色一个非常直接的比喻:它像一条流水线上的工人,你的链上资格是“工位”,你的节点状态是“身体”。工位没排到,身体再好也没用;工位排到了,身体掉链子,就会被链上强制跳过。所以出块权由链上决定,但能不能把块出好,完全看节点运维水平。
Prometheus 监控建议从一开始就开起来。启动参数里加--prometheus-port=9615,然后用 Prometheus 采集节点指标。这个不算复杂,但对长期运营帮助巨大,能提前发现磁盘增长和内存泄漏。
6. 常见问题与排查实录
6.1 区块一直不同步,高度卡死的排查
节点卡同步是最高频问题。先判断是中继链不同步还是平行链不同步。日志里如果中继链高度不变,大概率是 P2P 网络问题。检查顺序是:防火墙是否放行 30333 入站、出站是否被限制、Peer 数是否为 0。有时候上游运营商屏蔽了 P2P 流量,那就只能换机房或换端口策略。
如果中继链已经同步,但平行链区块高度一直不动,多半是--parachain-id配置错误,或者链的 Runtime 版本跟节点客户端版本不匹配。还有一种情况是磁盘快满了,节点写不进去,日志里会反复出现磁盘 I/O 错误。先df -h看磁盘,再来回改配置,顺序反了会浪费很多时间。
强制恢复同步的办法是清空 base-path 重新同步,但代价很大。我会先用curl调 RPC 看当前节点高度:
curl -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"system_syncState","params":[]}' \ http://127.0.0.1:9933看currentBlock和highestBlock的差距,差距很大就是同步滞后,差距为零但不出块则是资格问题。
6.2 出块轮空、session 不轮换的常见原因
轮空的直接现象是:日志正常、没有报错、但浏览器里能看到你账户所在的 Session 没有对应区块产出。最常见的原因有三类。第一类,Session Key 没有真正写进 keystore,节点签名时找不到密钥,这时日志里会有签名失败或找不到账户的提示。第二类,链上 Staking 门槛或授权没有生效,链上的 Collator 集合里根本没有你的账户。第三类,多节点共用同一个账户和 Session Key,导致签名冲突。
排查时不要光盯节点日志,链上状态和节点状态要一起看。用官方应用查 Session Key 是否已注册,再用 RPC 调author_hasKey验证本地 keystore,两步对上才能说明签名链路没问题。
我踩过一次很隐蔽的坑:节点同步用了快照恢复,但 base-path 里的 keystore 文件还是旧账户的,Session Key 提交的是新账户,两者对不上。折腾半天才发现是恢复数据时把 keystore 一起覆盖了。
6.3 容器、日志、磁盘和重启策略的收尾经验
关于 Docker 容器本身,我强烈建议不要用docker exec进去改配置或者乱删数据。Collator 跑起来后所有关键数据都在卷目录里,容器随时可以用相同参数销毁重建。真需要维护时,停掉 service、备份数据目录,再重建容器,比在容器内部折腾安全得多。
日志维护上,最怕的是只在看到磁盘告警时才想起来清日志。我会在 node 账号下挂一个 crontab,定期检查/opt/collator/data的磁盘占用,超过阈值就报警。journalctl --vacuum-size也可以配合使用,但不要清太激进,否则错误日志也会被过早覆盖。
重启策略这块,我把 Docker 的 restart 交给 Systemd 之后发现问题反而少了。难点反而是“什么时候该主动重启节点”。我的经验是:链上 Runtime 升级后必须尽快重启到匹配版本;节点运行超过几天且内存明显爬高时,可以选一个 Session 空闲期滚动重启。不要在大 Session 轮换或链上有敏感操作时重启,容易造成漏块。
最后留一个很实用的习惯
我个人实际运营中受益最大的一个习惯是:每次部署完,建一个文档把容器启动参数、Systemd 配置、数据目录路径、Session Key 提交日期全记下来。节点运营中 80% 的坑在第二、第三次操作时都会重演,有记录就能少踩一遍。Collator 这套东西,部署本身并不难,难的是后续长期维护和排障的耐心。真正想跑好,先把基础设施的底子打好,再追求出块效率,顺序反了,后面全是补窟窿的活。