12. 普通用户启动Docker_VM链

本章节介绍在宿主机上以非 root(普通用户) 身份,通过命令行工具启动一条启用 Docker 合约引擎(VM-GO 的长安链的全过程。若不涉及 cgroup 资源限制,普通用户部署与 root 用户部署的操作步骤基本一致,唯一区别是需要将部署用户加入 docker 组(详见 环境准备)。

前提假设

  1. 本文默认读者已经决定启用 Docker 合约引擎(即在 prepare.sh 中选择 enable vm go = YES)。若未启用 Docker 合约引擎,节点主进程本身对宿主机没有 root 依赖,普通用户直接执行 ./start.sh 即可,无需参考本文;

  2. 本文默认使用证书模式(prepare.sh)进行说明,PK / PWK 模式(prepare_pk.shprepare_pwk.sh)在权限相关的操作步骤上完全一致,仅证书 / 密钥生成脚本不同;

  3. 本章节仅涉及”如何用非root用户跑起一条链”,不涉及生产环境的账号规划与安全加固。生产环境请按照贵司安全规范另行加固。

操作用户约定:下文默认部署用户为 deploy(请按实际情况替换)。除非章节中明确标注”以 root / sudo 身份执行”,其余所有步骤均以 deploy 用户执行,因此部署目录、datalog 的属主天然为 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 goYES,启用 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.ymlmax_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 的命令行参数中即可传入),并确保防火墙放行相应端口。


12.7. 相关链接