12. 普通用户启动Docker_VM链
本章节介绍在宿主机上以非 root(普通用户) 身份,通过命令行工具启动一条启用 Docker 合约引擎(VM-GO) 的长安链的全过程。若不涉及 cgroup 资源限制,普通用户部署与 root 用户部署的操作步骤基本一致,唯一区别是需要将部署用户加入 docker 组(详见 环境准备)。
前提假设:
本文默认读者已经决定启用 Docker 合约引擎(即在
prepare.sh中选择enable vm go = YES)。若未启用 Docker 合约引擎,节点主进程本身对宿主机没有 root 依赖,普通用户直接执行./start.sh即可,无需参考本文;本文默认使用证书模式(
prepare.sh)进行说明,PK / PWK 模式(prepare_pk.sh、prepare_pwk.sh)在权限相关的操作步骤上完全一致,仅证书 / 密钥生成脚本不同;本章节仅涉及”如何用非root用户跑起一条链”,不涉及生产环境的账号规划与安全加固。生产环境请按照贵司安全规范另行加固。
操作用户约定:下文默认部署用户为 deploy(请按实际情况替换)。除非章节中明确标注”以 root / sudo 身份执行”,其余所有步骤均以 deploy 用户执行,因此部署目录、data、log 的属主天然为 deploy,无需额外 chown。
12.1. 环境准备
12.1.1. 操作系统与依赖软件
操作系统:Linux(推荐 Ubuntu 20.04+、CentOS 7+);
软件依赖:
docker(≥ 20.10)、golang(1.24.13+)、gcc(7.3+)、git。
Docker 安装完成后,请将部署用户加入 docker 用户组,否则该用户执行 docker 命令会因 /var/run/docker.sock 权限不足而失败:
# 需要 root 权限执行
sudo usermod -aG docker <deploy_user>
注意:usermod 生效前需要重新登录该用户的会话(exit 后重新 ssh 或重开一个终端),执行 id 命令确认输出中包含 docker 用户组后再继续。
12.1.2. 拉取合约引擎镜像
拉取官方 Docker 合约引擎镜像(版本号请按实际使用的长安链版本替换):
docker pull chainmakerofficial/chainmaker-vm-engine:v2.3.9
由于国内对 Docker Hub 的访问限制,可能出现无法拉取镜像的情况:
Unable to find image 'chainmakerofficial/chainmaker-vm-engine:v2.3.9' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).
针对这种情况,可以替换为长安链团队自建的 Docker 镜像源,修改 Docker 配置文件 /etc/docker/daemon.json(若不存在则新建):
{
"registry-mirrors": ["https://hub-dev.cnbn.org.cn"]
}
配置完成后需要重启 Docker 服务:sudo systemctl restart docker
hub-dev.cnbn.org.cn 是长安链团队自建的镜像源,也可以直接访问该地址,通过左上角搜索获取目前支持的镜像列表。
配置完成后重新执行拉取命令即可。
12.1.3. 部署目录规划
mkdir -p /home/deploy/chainmaker
cd /home/deploy/chainmaker
后续生成的 release 包 / 数据目录 / 日志目录都放置在该目录下。
12.2. 下载源码、编译并制作安装包
12.2.1. 下载源码
本文以 chainmaker-go v2.3.10、chainmaker-cryptogen v2.3.7 为例进行说明,全文所有步骤均基于该版本。如需部署更高版本,请在版本迭代说明中查询对应的合约引擎版本和证书生成工具版本,并替换下方命令中的分支号。
# 下载 chainmaker-go 源码
git clone -b v2.3.10 --depth=1 https://git.chainmaker.org.cn/chainmaker/chainmaker-go.git
# 下载证书生成工具源码
git clone -b v2.3.7 --depth=1 https://git.chainmaker.org.cn/chainmaker/chainmaker-cryptogen.git
12.2.2. 编译证书生成工具
cd chainmaker-cryptogen
make
cd ..
12.2.3. 软链接 cryptogen 到 tools 目录
cd chainmaker-go/tools
ln -s ../../chainmaker-cryptogen/ .
cd ..
12.2.4. 生成证书与配置
进入源码目录 chainmaker-go/scripts,执行 prepare.sh 生成 4 节点集群的证书和 chainmaker.yml:
cd scripts
./prepare.sh 4 1
按提示交互(关键项如下,其他保持默认):
input consensus type (0-SOLO,1-TBFT(default),3-HOTSTUFF,4-RAFT,5-DPOS):
input log level (DEBUG|INFO(default)|WARN|ERROR):
enable vm go (YES|NO(default)): YES
vm go transport protocol (uds|tcp(default)): tcp
enable vm go:填YES,启用 Docker 合约引擎;vm go transport protocol:非 root 场景建议使用tcp(uds 需要挂载 socket 目录,容器内外的用户身份需一致)。
12.2.5. 编译并生成安装包
./build_release.sh
执行成功后可在 chainmaker-go/build/release/ 下看到 4 个节点的 tar 包:
../build/release/
├── chainmaker-v2.3.10-wx-org1.chainmaker.org-YYYYMMDDhhmmss-x86_64.tar.gz
├── chainmaker-v2.3.10-wx-org2.chainmaker.org-YYYYMMDDhhmmss-x86_64.tar.gz
├── chainmaker-v2.3.10-wx-org3.chainmaker.org-YYYYMMDDhhmmss-x86_64.tar.gz
└── chainmaker-v2.3.10-wx-org4.chainmaker.org-YYYYMMDDhhmmss-x86_64.tar.gz
12.3. 部署与启动
根据实际情况选择以下两种方式之一。
12.3.1. 方式一:在当前服务器上直接启动(推荐)
如果编译和部署在同一台服务器上完成,可直接使用 cluster_quick_start.sh 脚本一键完成解压 + 启动,无需手动解压:
cd chainmaker-go/scripts
./cluster_quick_start.sh normal
该脚本会自动解压 build/release/ 下的所有 tar 包,并依次调用每个节点的 bin/start.sh 启动节点和合约引擎容器。
启动成功后,将 tar 包备份,以免下次重新启动时文件被覆盖:
mkdir -p ../build/bak
mv ../build/release/*.tar.gz ../build/bak
12.3.2. 方式二:在另一台服务器上准备好后拷贝部署
如果编译在开发机上完成,需要将安装包拷贝到部署机后再启动。
在部署机上解压单个节点的安装包:
cd /home/deploy/chainmaker
tar -zxvf chainmaker-v2.3.10-wx-org1.chainmaker.org-*.tar.gz
解压得到的 chainmaker-v2.3.10-wx-org1.chainmaker.org/ 目录结构大致如下:
chainmaker-v2.3.10-wx-org1.chainmaker.org/
├── bin/ # start.sh / stop.sh / cgroup_setup.sh / cgroup_prepare.sh 等
├── config/
├── data/
├── lib/
└── log/
进入 bin/ 目录执行启动单个节点:
cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin
./start.sh -f full
参数说明:
full:同时启动 chainmaker 节点进程和VM-GO-*合约引擎容器;-f:如果对应容器已存在(停止状态),强制删除后重建;alone:仅启动 chainmaker 节点,不拉起合约引擎(适用于合约引擎独立部署场景)。
验证启动结果:
查看节点进程:
ps -ef | grep chainmaker | grep -v grep
# 预期能看到 ./chainmaker start -c ../config/wx-org1.chainmaker.org/chainmaker.yml
查看合约引擎容器:
docker ps | grep VM-GO
# 预期能看到 VM-GO-wx-org1.chainmaker.org-name-<时间戳> 处于 Up 状态
# 例如:VM-GO-wx-org1.chainmaker.org-name-20241016154829
查看节点与合约引擎是否建立通信:
less /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/log/system.log | grep -i "all connection established"
按上述流程依次启动剩余节点。
12.4. 停止
12.4.1. 方式一:停止整个集群
cd chainmaker-go/scripts
./cluster_quick_stop.sh # 仅停止节点和合约引擎容器,保留 data / log
./cluster_quick_stop.sh clean # 停止后额外清理各节点的 data 目录和 log 目录
12.4.2. 方式二:停止单个节点
cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin
./stop.sh full
stop.sh 会同时停止 chainmaker 节点进程和该节点关联的 VM-GO-* 容器。
参数说明:
full:同时停止 chainmaker 节点进程和VM-GO-*合约引擎容器;alone:仅停止 chainmaker 节点进程,不停止合约引擎容器;不带参数:交互式询问是否同时停止合约引擎容器(默认询问)。
12.5. 开启 cgroup 的启动流程
cgroup 功能默认关闭。如需对合约进程的 CPU / 内存使用进行资源隔离和限制,可按本节步骤手动开启。
开启 cgroup 后,合约引擎需要写宿主机 /sys/fs/cgroup/ 目录对合约进程做 CPU / 内存限制,因此需要先由 root 一次性执行 cgroup_prepare.sh 完成预配置(创建子目录并 chown 给部署用户),之后 deploy 用户运行 start.sh 时会自动完成 cgroup 挂载。
12.5.1. 开启 cgroup 的配置修改
打开 config/wx-org1.chainmaker.org/chainmaker.yml,将合约引擎 cgroup 配置修改为:
vm:
common:
contract_engine:
cgroup:
# 开启 cgroup
disable: false
# 单个合约进程最大内存(MiB),-1 表示不限制
max_mem_size_per_process: 512
# 单个合约进程最大 CPU 百分比(相对于全部核心的总和),-1 表示不限制
# 例如 8 核机器上填 0.5,表示单个合约进程最多可用 4 核算力
max_cpu_percent_per_process: 0.5
# 设备白名单 / 黑名单(可选,格式参考 Linux devices cgroup 语法)
devices_allow: ""
devices_deny: ""
配置项说明:
| 字段 | 含义 |
|---|---|
disable |
是否禁用 cgroup。false 表示启用 |
max_mem_size_per_process |
单个合约进程内存上限(MiB)。-1 表示不限制 |
max_cpu_percent_per_process |
单个合约进程 CPU 使用上限(占宿主机 CPU 总量的百分比,取值 0~1)。-1 表示不限制 |
devices_allow / devices_deny |
设备访问白 / 黑名单,按 Linux devices cgroup 语法书写 |
⚠️ max_mem_size_per_process 建议不低于 32MiB,否则合约进程可能在启动阶段即被内核 OOM Kill。
12.5.2. 由管理员执行一次 cgroup 预配置
在首次部署前(或者宿主机重启后 cgroup 状态被清空时),由具备 root 权限的管理员执行:
# 以 root 或 sudo 身份执行
cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin
sudo bash cgroup_prepare.sh deploy VM-GO-wx-org1.chainmaker.org-name-<时间戳>
参数说明:
第 1 个参数
deploy:部署用户名,与usermod -aG docker时使用的用户一致;第 2 个及后续参数:一个或多个容器名,与
start.sh中最终使用的容器名保持一致。容器名的格式为VM-GO-{org_id}-name-{时间戳},例如VM-GO-wx-org1.chainmaker.org-name-20241016154829。
同一台机器上部署多个节点时可一次性传入所有容器名,示例:
sudo bash cgroup_prepare.sh deploy \
VM-GO-wx-org1.chainmaker.org-name-20241016154829 \
VM-GO-wx-org2.chainmaker.org-name-20241016154829 \
VM-GO-wx-org3.chainmaker.org-name-20241016154829 \
VM-GO-wx-org4.chainmaker.org-name-20241016154829
脚本会自动检测 cgroup 版本并完成对应子目录的创建与 chown。执行成功后能看到形如 ✅ cgroup 预配置完成(共 4 个容器) 的提示。
12.5.3. 以非 root 用户启动
预配置完成后,deploy 用户即可直接启动。此方式会自动处理所有节点的启动,无需逐个操作。提供两种启动方式:
12.5.3.1. 方式一:使用集群管理脚本启动(推荐)
cd chainmaker-go/scripts
./cluster_quick_start.sh normal
12.5.3.2. 方式二:手动启动
cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin
./start.sh -f full
参数说明:
full:同时启动 chainmaker 节点进程和VM-GO-*合约引擎容器;-f:如果对应容器已存在(停止状态),强制删除后重建;alone:仅启动 chainmaker 节点,不拉起合约引擎(适用于合约引擎独立部署场景)。
12.5.4. 验证 cgroup 是否生效
启动后,可以通过如下步骤验证 cgroup 已经生效:
检查子目录是否属于
deploy用户:
# cgroup v2
ls -l /sys/fs/cgroup/chainmaker-vm-VM-GO-wx-org1.chainmaker.org-name-20241016154829
# cgroup v1
ls -l /sys/fs/cgroup/memory/chainmaker-vm-VM-GO-wx-org1.chainmaker.org-name-20241016154829
ls -l /sys/fs/cgroup/cpu/chainmaker-vm-VM-GO-wx-org1.chainmaker.org-name-20241016154829
触发一次合约调用后,可在上述目录下看到以合约进程 PID 命名的子目录(内含
memory.limit_in_bytes等文件),其值应与chainmaker.yml中配置一致。若合约在运行时主动申请超过
max_mem_size_per_process的内存,可通过内核日志确认 OOM Kill 已经触发:
sudo dmesg -T | grep -E 'killed as a result of limit of|memory: usage' | tail -20
12.6. 常见问题
12.6.1. permission denied while trying to connect to the Docker daemon socket
现象:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
原因:deploy 用户尚未加入 docker 用户组。
处理:
sudo usermod -aG docker deploy
# 退出当前会话并重新登录
12.6.2. mkdir: cannot create directory ‘/sys/fs/cgroup/chainmaker-vm-…’: Permission denied
现象:start.sh 输出 ERROR: failed to create cgroup v2 directory: /sys/fs/cgroup/chainmaker-vm-...。
原因:以非 root 用户启动,但未事先执行 cgroup_prepare.sh 完成预配置。
处理:由管理员执行一次 由管理员执行一次 cgroup 预配置 的预配置脚本。
12.6.3. 宿主机重启后 cgroup 目录消失
/sys/fs/cgroup/ 下的子目录属于内核 cgroupfs,其状态不会跨重启持久化。宿主机重启后,请重新执行一次 cgroup_prepare.sh 恢复预配置:
sudo bash cgroup_prepare.sh deploy VM-GO-wx-org1.chainmaker.org-name-<时间戳> ...
如需开机自动预配置,可将该脚本写入宿主机的 systemd unit 或 rc.local 中。
12.6.4. 合约调用总是”运行慢”或偶发失败,dmesg 中出现 Task in /docker/... killed as a result of limit of ...
现象:cgroup 内存限制配置过低,合约进程在 Go runtime 初始化阶段就被 OOM Kill。
处理:将 chainmaker.yml 中 max_mem_size_per_process 上调到合理值(建议不低于 32MiB,详见 开启 cgroup 的配置修改)后,通过 stop.sh + start.sh 重启节点即可生效。
12.6.5. 清理 data/ 目录时权限不足
data/contract-bins/ 中的文件属主为容器内 root,普通部署用户执行 rm -rf data 或 ./cluster_quick_stop.sh clean 时会报 Permission denied。使用 sudo 清理即可:
# 单节点
sudo rm -rf data log/*
# 集群(chainmaker-go/scripts 目录下)
sudo rm -rf ../build/release/*/data ../build/release/*/log/*
12.6.6. cgroup v2 环境下执行 cgroup_prepare.sh 报错
现象:脚本报错 无法写入 /sys/fs/cgroup/cgroup.subtree_control。
原因:向 /sys/fs/cgroup/cgroup.subtree_control 写入需要保证宿主机根 cgroup 下没有运行进程。一般是宿主机上尚未通过 systemd 迁移根 cgroup 中的进程。
处理:可先重启宿主机或参考发行版文档处理后再执行本脚本。
12.6.7. 端口冲突或防火墙拦截
启用 Docker 合约引擎后,节点与合约引擎之间通过两个 TCP 端口通信(tcp 协议下):
contract_engine.port(默认22351):由VM-GO-*容器监听,节点主进程作为客户端连入,用于”节点 → 合约引擎”的合约调用请求。该端口需要通过docker run -p暴露到宿主机;runtime_server.port(默认32351):由节点主进程监听在宿主机上,合约引擎沙箱作为客户端连回,用于”合约引擎沙箱 → 节点”的状态读写等回调。该端口不属于容器,但容器必须能访问到宿主机的这个端口(prepare.sh生成配置时已默认使用host.docker.internal或宿主机 IP)。
多节点同机部署时请为每个节点指定不同的端口(在 prepare.sh 的命令行参数中即可传入),并确保防火墙放行相应端口。