# 普通用户启动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.sh`、`prepare_pwk.sh`)在**权限相关的操作步骤上完全一致**,仅证书 / 密钥生成脚本不同; 3. 本章节仅涉及"如何用非root用户跑起一条链",不涉及生产环境的账号规划与安全加固。生产环境请按照贵司安全规范另行加固。 **操作用户约定**:下文默认部署用户为 `deploy`(请按实际情况替换)。**除非章节中明确标注"以 root / sudo 身份执行",其余所有步骤均以 `deploy` 用户执行**,因此部署目录、`data`、`log` 的属主天然为 `deploy`,无需额外 `chown`。 --- ## 环境准备 ### 操作系统与依赖软件 - 操作系统:Linux(推荐 Ubuntu 20.04+、CentOS 7+); - 软件依赖:`docker`(≥ 20.10)、`golang`(1.24.13+)、`gcc`(7.3+)、`git`。 Docker 安装完成后,请**将部署用户加入 `docker` 用户组**,否则该用户执行 `docker` 命令会因 `/var/run/docker.sock` 权限不足而失败: ```shell # 需要 root 权限执行 sudo usermod -aG docker ``` **注意**:`usermod` 生效前需要**重新登录**该用户的会话(`exit` 后重新 `ssh` 或重开一个终端),执行 `id` 命令确认输出中包含 `docker` 用户组后再继续。 ### 拉取合约引擎镜像 拉取官方 Docker 合约引擎镜像(版本号请按实际使用的长安链版本替换): ```shell 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`(若不存在则新建): ```json { "registry-mirrors": ["https://hub-dev.cnbn.org.cn"] } ``` 配置完成后需要重启 Docker 服务:`sudo systemctl restart docker` `hub-dev.cnbn.org.cn` 是长安链团队自建的镜像源,也可以直接访问该地址,通过左上角搜索获取目前支持的镜像列表。 配置完成后重新执行拉取命令即可。 ### 部署目录规划 ```shell mkdir -p /home/deploy/chainmaker cd /home/deploy/chainmaker ``` 后续生成的 `release` 包 / 数据目录 / 日志目录都放置在该目录下。 --- ## 下载源码、编译并制作安装包 ### 下载源码 本文以 chainmaker-go v2.3.10、chainmaker-cryptogen v2.3.7 为例进行说明,全文所有步骤均基于该版本。如需部署更高版本,请在[版本迭代说明](../instructions/版本迭代说明.md)中查询对应的合约引擎版本和证书生成工具版本,并替换下方命令中的分支号。 ```shell # 下载 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 ``` ### 编译证书生成工具 ```shell cd chainmaker-cryptogen make cd .. ``` ### 软链接 cryptogen 到 tools 目录 ```shell cd chainmaker-go/tools ln -s ../../chainmaker-cryptogen/ . cd .. ``` ### 生成证书与配置 进入源码目录 `chainmaker-go/scripts`,执行 `prepare.sh` 生成 4 节点集群的证书和 `chainmaker.yml`: ```shell 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 目录,容器内外的用户身份需一致)。 ### 编译并生成安装包 ```shell ./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 ``` --- ## 部署与启动 根据实际情况选择以下两种方式之一。 ### 方式一:在当前服务器上直接启动(推荐) 如果编译和部署在**同一台服务器**上完成,可直接使用 `cluster_quick_start.sh` 脚本一键完成**解压 + 启动**,无需手动解压: ```shell cd chainmaker-go/scripts ./cluster_quick_start.sh normal ``` 该脚本会自动解压 `build/release/` 下的所有 tar 包,并依次调用每个节点的 `bin/start.sh` 启动节点和合约引擎容器。 启动成功后,将 tar 包备份,以免下次重新启动时文件被覆盖: ```shell mkdir -p ../build/bak mv ../build/release/*.tar.gz ../build/bak ``` ### 方式二:在另一台服务器上准备好后拷贝部署 如果编译在**开发机**上完成,需要将安装包拷贝到部署机后再启动。 在部署机上解压单个节点的安装包: ```shell 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/` 目录执行启动单个节点: ```shell cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin ./start.sh -f full ``` 参数说明: - `full`:同时启动 chainmaker 节点进程和 `VM-GO-*` 合约引擎容器; - `-f`:如果对应容器已存在(停止状态),强制删除后重建; - `alone`:仅启动 chainmaker 节点,不拉起合约引擎(适用于合约引擎独立部署场景)。 验证启动结果: - 查看节点进程: ```shell ps -ef | grep chainmaker | grep -v grep # 预期能看到 ./chainmaker start -c ../config/wx-org1.chainmaker.org/chainmaker.yml ``` - 查看合约引擎容器: ```shell docker ps | grep VM-GO # 预期能看到 VM-GO-wx-org1.chainmaker.org-name-<时间戳> 处于 Up 状态 # 例如:VM-GO-wx-org1.chainmaker.org-name-20241016154829 ``` - 查看节点与合约引擎是否建立通信: ```shell less /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/log/system.log | grep -i "all connection established" ``` 按上述流程依次启动剩余节点。 --- ## 停止 ### 方式一:停止整个集群 ```shell cd chainmaker-go/scripts ./cluster_quick_stop.sh # 仅停止节点和合约引擎容器,保留 data / log ./cluster_quick_stop.sh clean # 停止后额外清理各节点的 data 目录和 log 目录 ``` ### 方式二:停止单个节点 ```shell 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 节点进程,不停止合约引擎容器; - 不带参数:交互式询问是否同时停止合约引擎容器(默认询问)。 --- ## 开启 cgroup 的启动流程 cgroup 功能默认关闭。如需对合约进程的 CPU / 内存使用进行资源隔离和限制,可按本节步骤手动开启。 开启 cgroup 后,合约引擎需要写宿主机 `/sys/fs/cgroup/` 目录对合约进程做 CPU / 内存限制,因此需要先由 root 一次性执行 `cgroup_prepare.sh` 完成预配置(创建子目录并 chown 给部署用户),之后 `deploy` 用户运行 `start.sh` 时会自动完成 cgroup 挂载。 ### 开启 cgroup 的配置修改 打开 `config/wx-org1.chainmaker.org/chainmaker.yml`,将合约引擎 cgroup 配置修改为: ```yaml 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。 ### 由管理员执行一次 cgroup 预配置 在**首次部署前**(或者宿主机重启后 cgroup 状态被清空时),由具备 root 权限的管理员执行: ```shell # 以 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`。 **同一台机器上部署多个节点时可一次性传入所有容器名**,示例: ```shell 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 个容器)` 的提示。 ### 以非 root 用户启动 预配置完成后,`deploy` 用户即可直接启动。此方式会自动处理所有节点的启动,无需逐个操作。提供两种启动方式: #### 方式一:使用集群管理脚本启动(推荐) ```shell cd chainmaker-go/scripts ./cluster_quick_start.sh normal ``` #### 方式二:手动启动 ```shell cd /home/deploy/chainmaker/chainmaker-v2.3.10-wx-org1.chainmaker.org/bin ./start.sh -f full ``` 参数说明: - `full`:同时启动 chainmaker 节点进程和 `VM-GO-*` 合约引擎容器; - `-f`:如果对应容器已存在(停止状态),强制删除后重建; - `alone`:仅启动 chainmaker 节点,不拉起合约引擎(适用于合约引擎独立部署场景)。 ### 验证 cgroup 是否生效 启动后,可以通过如下步骤验证 cgroup 已经生效: - 检查子目录是否属于 `deploy` 用户: ```shell # 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 已经触发: ```shell sudo dmesg -T | grep -E 'killed as a result of limit of|memory: usage' | tail -20 ``` --- ## 常见问题 ### 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` 用户组。 处理: ```shell sudo usermod -aG docker deploy # 退出当前会话并重新登录 ``` ### 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 预配置](#由管理员执行一次-cgroup-预配置) 的预配置脚本。 ### 宿主机重启后 cgroup 目录消失 `/sys/fs/cgroup/` 下的子目录属于内核 cgroupfs,其状态**不会跨重启持久化**。宿主机重启后,请重新执行一次 `cgroup_prepare.sh` 恢复预配置: ```shell sudo bash cgroup_prepare.sh deploy VM-GO-wx-org1.chainmaker.org-name-<时间戳> ... ``` 如需开机自动预配置,可将该脚本写入宿主机的 systemd unit 或 rc.local 中。 ### 合约调用总是"运行慢"或偶发失败,`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 的配置修改](#开启-cgroup-的配置修改))后,通过 `stop.sh + start.sh` 重启节点即可生效。 ### 清理 data/ 目录时权限不足 `data/contract-bins/` 中的文件属主为容器内 root,普通部署用户执行 `rm -rf data` 或 `./cluster_quick_stop.sh clean` 时会报 `Permission denied`。使用 sudo 清理即可: ```shell # 单节点 sudo rm -rf data log/* # 集群(chainmaker-go/scripts 目录下) sudo rm -rf ../build/release/*/data ../build/release/*/log/* ``` ### cgroup v2 环境下执行 cgroup_prepare.sh 报错 现象:脚本报错 `无法写入 /sys/fs/cgroup/cgroup.subtree_control`。 原因:向 `/sys/fs/cgroup/cgroup.subtree_control` 写入需要保证宿主机根 cgroup 下没有运行进程。一般是宿主机上尚未通过 systemd 迁移根 cgroup 中的进程。 处理:可先重启宿主机或参考发行版文档处理后再执行本脚本。 ### 端口冲突或防火墙拦截 启用 Docker 合约引擎后,节点与合约引擎之间通过两个 TCP 端口通信(`tcp` 协议下): - `contract_engine.port`(默认 `22351`):**由 `VM-GO-*` 容器监听**,节点主进程作为客户端连入,用于"节点 → 合约引擎"的合约调用请求。该端口需要通过 `docker run -p` 暴露到宿主机; - `runtime_server.port`(默认 `32351`):**由节点主进程监听在宿主机上**,合约引擎沙箱作为客户端连回,用于"合约引擎沙箱 → 节点"的状态读写等回调。该端口不属于容器,但容器必须能访问到宿主机的这个端口(`prepare.sh` 生成配置时已默认使用 `host.docker.internal` 或宿主机 IP)。 多节点同机部署时请为每个节点指定不同的端口(在 `prepare.sh` 的命令行参数中即可传入),并确保防火墙放行相应端口。 --- ## 相关链接 - [启动支持Docker_VM的链](../instructions/启动支持Docker_VM的链.md) - [长安链配置管理](长安链配置管理.md) - [自拉起服务](../instructions/自拉起服务.md)