containerd 虚拟机集成测试指南:基于 Lima 在 Fedora/EL 上运行完整测试套件
2026/9/13 20:58:13 网站建设 项目流程

containerd 虚拟机集成测试指南:基于 Lima 在 Fedora/EL 上运行完整测试套件

【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd

本文以 script/vm/README.md 为核心,系统讲解 containerd 项目如何在虚拟机(Fedora 或 EL 发行版,默认 SELinux Enforcing)中运行集成测试,涵盖虚拟机准备、环境初始化(Go、runc、CNI 插件、cri-tools、containerd 本体)、三套测试套件(test-integration.shtest-cri-integration.shtest-cri.sh)的执行方式与全部可调环境变量。读完本文,你将掌握与 CI 中integration-vm任务完全一致的本机复现方法,并能深入理解每个脚本在源码层的实现细节。

一、这套 VM 脚本解决什么问题

containerd 的集成测试对运行环境有较高要求:需要内核模块、SELinux 策略、runc/crun 运行时、CNI 网络插件、CRI 工具链等一整套依赖。直接在宿主机上运行既不安全也不可复现。script/vm/目录下的脚本正是为此设计——它们在一个全新的虚拟机中完成全链路环境搭建与测试执行,保证测试结果与 CI 一致。

这些脚本被 .github/workflows/ci.yml 中的integration-vm任务使用,但不依赖 Lima 本身,同样可以配合其他 VM 环境(如 QEMU、VirtualBox 或云主机)使用。脚本约定的目标操作系统是Fedora 或 EL 发行版(RHEL/CentOS/AlmaLinux 等),默认在SELinux Enforcing模式下运行,这是它区别于一般测试脚本的最大特点——确保 containerd 在强制 SELinux 策略下依然行为正确。

script/vm/目录共包含 4 个脚本,职责划分如下:

脚本职责
provision.sh在客户机内初始化环境(软件包、Go、runc、CNI、cri-tools、containerd、gotestsum、failpoint 二进制)
test-integration.sh运行 containerd 核心集成测试(make integration
test-cri-integration.sh运行 CRI 插件集成测试(cri-integration.test
test-cri.sh以 systemd 单元方式启动 containerd,运行critest一致性测试

二、使用 Lima 在本机复现

Lima 是 macOS/Linux 上常用的虚拟机管理工具。下面的流程摘自 script/vm/README.md,可完整复现 CI 行为。

1. 启动一台干净的虚拟机

# Boot a plain VM. template:almalinux-8, -9, and -10 are tested too. # --plain keeps the guest pristine (no guest agent, no mounts, no port forwards). limactl start --plain --name=default --cpus=2 --memory=4 --disk=60 template:fedora-44

关键点说明:

  • --plain:保持客户机"原始"状态——不安装 guest agent、不做目录挂载、不做端口转发。这正是测试所需的隔离环境。
  • --cpus=2 --memory=4 --disk=60:CI 中同样采用此配置(见 ci.yml)。
  • 官方测试过的模板:template:fedora-44template:almalinux-8-9-10。其中 AlmaLinux 8 保留了旧版 glibc、cgroup 与内核,用于验证兼容性(CI 注释明确说明 "We have to keep EL8 to test old glibc, cgroup, kernel, etc.",见 ci.yml)。

2. 拷贝源码树到客户机

# Copy the source tree into the guest. limactl cp -r . default:containerd lima bash -c 'sudo mkdir -p /go/src/github.com/containerd && sudo mv ~/containerd /go/src/github.com/containerd/containerd' export LIMA_WORKDIR=/go/src/github.com/containerd/containerd

把源码放到/go/src/github.com/containerd/containerd并非随意为之——provision 脚本会建立$GOPATH/src/github.com/containerd/containerd的软链接(见下文 provision.sh 分析),使仓库位于 Go 模块的标准导入路径上。LIMA_WORKDIR变量供后续lima命令切换到该目录使用。

3. 初始化客户机

# Provision the guest (packages, Go, runc, CNI plugins, cri-tools, containerd, ...). lima sudo script/vm/provision.sh

该步骤耗时较长,需要联网下载软件包、Go 工具链、runc 源码与各测试工具,详见第三节。

4. 运行测试套件

# Run the test suites. lima sudo script/vm/test-integration.sh lima sudo CGROUP_DRIVER=systemd script/vm/test-cri-integration.sh lima sudo CGROUP_DRIVER=systemd script/vm/test-cri.sh

三行命令依次对应三类测试:核心集成测试、CRI 集成测试、CRI 一致性测试(critest)。CGROUP_DRIVER=systemd表示以 systemd cgroup 驱动运行——在采用 systemd 作为 init 的发行版上,这是生产环境的典型配置,与 cgroupfs 驱动分别覆盖两条路径(CI 矩阵对两者都会测试,见 ci.yml)。

5. 清理

limactl delete -f default

三、环境变量一览

原文档给出了各脚本支持的全部环境变量,此处完整保留并补充默认值(均来自脚本源码):

脚本环境变量取值/默认值作用
provision.shGO_VERSION默认1.27.1要安装的 Go 工具链版本
RUNC_FLAVORrunccrun,默认runc安装哪种低层运行时(crun 会被安装为runc同名二进制)
SELINUXEnforcing/Permissive/Disabled,默认Enforcing客户机的 SELinux 模式
INSTALL_PACKAGES默认空追加安装的额外软件包(空格分隔,会拼入 dnf install 命令)
test-integration.shRUNC_FLAVORrunccrun,默认runc指定测试使用的运行时 flavor
test-cri-integration.shCGROUP_DRIVER空(cgroupfs)或systemdCRI 插件的 cgroup 驱动
RUNC_FLAVORrunccrun,默认runc指定测试使用的运行时 flavor
test-cri.shCGROUP_DRIVER空(cgroupfs)或systemdCRI 插件的 cgroup 驱动
REPORT_DIR默认空非空时critest会额外生成测试报告到该目录

另有几个脚本内部使用的变量(源码中: "${VAR:=default}"形式声明):GOTEST(默认go test,test-integration.sh 与 test-cri-integration.sh)、GOPATH(默认/go)、GITHUB_WORKSPACE(test-cri-integration.sh 中转发给内部脚本,默认空)。

四、深入 provision.sh:客户机初始化全流程

provision.sh 是整套流程的基础,必须在客户机内以root身份执行(脚本开头显式检查id -u,非 root 直接报错退出,见 provision.sh)。它依次完成以下工作:

1. 安装系统软件包

通过dnf安装测试所需依赖(provision.sh):

dnf -y install \ container-selinux \ curl gcc git iptables \ libseccomp-devel libselinux-devel \ lsof make strace which \ "kernel-modules-extra-$(uname -r)" \ ${INSTALL_PACKAGES} modprobe xt_comment

几个值得注意的细节:

  • container-selinux提供容器运行所需的 SELinux 策略模块;libseccomp-devellibselinux-devel是编译 containerd(seccomp/selinux build tag)的开发头文件。
  • kernel-modules-extra-$(uname -r)按当前内核版本精确匹配安装,为测试提供额外内核模块。
  • modprobe xt_comment加载 iptables 的 comment 匹配模块,供 CNI 网络插件使用。
  • ${INSTALL_PACKAGES}未加引号展开(# shellcheck disable=SC2086注释了该意图),便于传入以空格分隔的多个包名。

2. 安装 Go 工具链

GOARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')" curl -fsSL "https://dl.google.com/go/go${GO_VERSION}.linux-${GOARCH}.tar.gz" | tar Cxz /usr/local

uname -m映射出GOARCH(x86_64→amd64,aarch64→arm64),下载对应架构的官方二进制包解压到/usr/local

3. 处理源码树属主与 SELinux 标签

源码是从宿主机拷贝进来的,属主可能是非 root 用户,且文件可能带有宿主机目录的 SELinux 上下文(provision.sh):

git config --system --add safe.directory "${containerd_dir}" if type -p restorecon > /dev/null; then restorecon -R "${containerd_dir}" fi
  • git config --system --add safe.directory:把源码目录标记为 git 安全目录,避免"dubious ownership"错误。
  • restorecon -R:递归恢复文件的默认 SELinux 上下文,防止后续执行时被策略拦截。

4. 建立 GOPATH 布局

mkdir -p "${GOPATH}/src/github.com/containerd" if [[ "${containerd_dir}" != "${GOPATH}/src/github.com/containerd/containerd" ]]; then ln -fnsv "${containerd_dir}" "${GOPATH}/src/github.com/containerd/containerd" fi

若源码不在标准位置,则在/go/src/github.com/containerd/containerd建立指向实际源码的软链接,保证 Go 构建工具能找到正确的导入路径。

5. 安装低层运行时(runc/crun)

RUNC_FLAVOR="${RUNC_FLAVOR}" script/setup/install-runc type runc runc --version type -ap runc | sort -u | xargs chcon -v -t container_runtime_exec_t

install-runc 根据RUNC_FLAVOR二选一:

  • runc:从runc-version文件读取固定版本(脚本头部注释说明该版本与 go.mod 中 runc 模块保持一致),git clone 后以BUILDTAGS='seccomp'编译并make install
  • crun:直接下载对应版本的预编译 crun 二进制,保存为/usr/local/sbin/runc(即以 runc 之名充当 runc 使用)。

安装后执行chcon -t container_runtime_exec_t为二进制打上容器运行时执行标签,这是 SELinux Enforcing 下容器能够启动的前提之一。

6. 安装 CNI 插件与 cri-tools

script/setup/install-cni CNI_BINARIES="bridge dhcp flannel host-device host-local ipvlan loopback macvlan portmap ptp tuning vlan" PATH="/opt/cni/bin:${PATH}" type ${CNI_BINARIES} || true GOBIN=/usr/local/bin script/setup/install-critools type crictl critest critest --version
  • CNI 插件安装到/opt/cni/bintype逐一确认 11 个标准插件存在(|| true允许校验不中断脚本)。
  • cri-tools 提供crictl(CRI 调试客户端)与critest(CRI 一致性测试工具),critest --version确认版本。

7. 编译安装 containerd 本体

make BUILDTAGS="seccomp selinux no_btrfs no_devmapper no_zfs" binaries install type containerd containerd --version chcon -v -t container_runtime_exec_t /usr/local/bin/{containerd,containerd-shim*}
  • 采用seccomp selinux开启安全特性,用no_btrfs no_devmapper no_zfs关闭不需要的快照器,缩减构建面。
  • containerd及所有containerd-shim*二进制设置container_runtime_exec_tSELinux 标签。

8. 安装 gotestsum 与 failpoint 二进制

script/setup/install-gotestsum cp "${GOPATH}/bin/gotestsum" /usr/local/bin/ script/setup/install-failpoint-binaries type -ap containerd-shim-runc-fp-v1 | sort -u | xargs chcon -v -t container_runtime_exec_t containerd-shim-runc-fp-v1 -v

gotestsum用于格式化测试输出;containerd-shim-runc-fp-v1是带 failpoint 注入能力的 shim,供故障注入类集成测试使用(对应integration/failpoint/目录)。

9. 配置 SELinux 与 containerd

SELINUX="${SELINUX}" script/setup/config-selinux script/setup/config-containerd

config-selinux 按SELINUX变量配置模式:

  • Disabled:若/sys/fs/selinux已挂载则setenforce 0并卸载;
  • Enforcing:挂载 selinuxfs 并setenforce 1
  • Permissive:挂载 selinuxfs 并setenforce 0
  • 其他值直接报错退出。

config-containerd 生成/etc/containerd/config.toml

version = 2 [plugins."io.containerd.snapshotter.v1.overlayfs"] slow_chown = true [plugins] [plugins."io.containerd.grpc.v1.cri"] enable_selinux = true # 仅当 SELinux 非 Disabled 时为 true

两个配置点均有明确目的:

  • slow_chown = true:规避内核 < 5.19 时 overlayfs 快照器不支持 idmap mount 的问题(报错信息为 "snapshotter "overlayfs" doesn't support idmap mounts on this host, configureslow_chownto allow a slower and expensive fallback"),对内核 ≥ 5.19 可安全忽略(该注释与关联 issue 见 config-containerd)。
  • enable_selinux:动态由getenforce的结果决定,SELinux 处于 Enforcing/Permissive 时为true,Disabled 时为false

五、三套测试脚本的源码级解析

1. test-integration.sh:核心集成测试

test-integration.sh 执行两步(L37-L40):

rm -rf /var/lib/containerd-test /run/containerd-test cd "${containerd_dir}" go test -v -count=1 -race ./core/metrics/cgroups make integration EXTRA_TESTFLAGS="-timeout 15m -no-criu -test.v" TEST_RUNTIME=io.containerd.runc.v2 RUNC_FLAVOR="${RUNC_FLAVOR}"
  • 先清理可能残留的测试数据目录,保证干净起点。
  • 单独运行go test -racecore/metrics/cgroups包做竞态检测,覆盖 cgroups 指标采集路径。
  • make integration是主入口,EXTRA_TESTFLAGS传入-timeout 15m(超时上限)、-no-criu(跳过 CRIU 检查点/恢复相关测试)、-test.v(详细输出);TEST_RUNTIME=io.containerd.runc.v2指定使用 runc v2 shim 运行时。

2. test-cri-integration.sh:CRI 插件集成测试

test-cri-integration.sh 聚焦 containerd 内置 CRI 插件(L43-L48):

cleanup cd "${containerd_dir}" make BUILDTAGS="seccomp selinux no_btrfs no_devmapper no_zfs" binaries bin/cri-integration.test chcon -v -t container_runtime_exec_t ./bin/{containerd,containerd-shim*} CONTAINERD_RUNTIME=io.containerd.runc.v2 ./script/test/cri-integration.sh cleanup
  • bin/cri-integration.test是编译出的 CRI 集成测试二进制;脚本注释特别说明该测试从./bin目录执行 containerd,而不是从$PATH(test-cri-integration.sh),因此必须先在仓库内完成make binaries
  • cleanup()函数会清空/var/lib/containerd*/run/containerd*/tmp/containerd*/tmp/test*/tmp/failpoint*/tmp/nri*,确保测试间互不污染(L39-L41)。

3. test-cri.sh:CRI 一致性测试(critest)

test-cri.sh 把 containerd 作为systemd 单元启动,再运行critest(L37-L71):

systemctl disable --now containerd || true rm -rf /var/lib/containerd /run/containerd ... systemctl enable --now "${containerd_dir}/containerd.service" ... critest --parallel=$(($(nproc) + 2)) --ginkgo.skip="${skip_test_args}"

值得注意的实现细节:

  • systemctl disable --now containerd停掉系统自带的 containerd,再用仓库根目录的 containerd.service 启动测试版本,保证测试对象是本次构建的二进制。
  • SELinux 处理:getenforce为 Enforcing 时,先setenforce 0启动服务、再恢复setenforce 1,让服务进程能在启动过程中正常初始化(L46-L53)。
  • trap cleanup EXIT:无论成败,退出前都会通过journalctl -u containerd导出完整日志到/tmp/containerd.log并打印,便于排障(L40-L44)。
  • 跳过列表(skip_tests,L57-L62):HostIpc is true始终跳过;当CGROUP_DRIVER=systemd时额外跳过 "should terminate with exitCode 137 and reason OOMKilled"(systemd cgroup 驱动下 OOM 行为与 cgroupfs 存在差异)。
  • 并行度设为nproc + 2,最大化利用客户机 CPU;REPORT_DIR非空时通过--report-dir输出测试报告。

六、CI 中的矩阵化使用

.github/workflows/ci.yml中的integration-vm任务(ci.yml)展示了这些脚本的完整组合方式。矩阵维度包括:

  • 模板fedora-44(主测版本)、almalinux-8(验证旧 glibc/cgroup/内核)、almalinux-9almalinux-10
  • cgroup 驱动cgroupfssystemd两条路径全覆盖;
  • 运行时runccrun

任务流水线与本地流程一一对应:limactl start --plain启动 →limactl cp -r .拷贝源码 → 按LIMA_TEMPLATECGROUP_DRIVERRUNC_FLAVOR三个环境变量注入矩阵值 → 依次执行provision.shtest-integration.shtest-cri-integration.shtest-cri.sh,最后无论成败都执行dmesg -T -f kern导出内核日志用于诊断(ci.yml)。

七、常见问题与使用建议

  • 必须以 root 执行:四个脚本开头都有id -u != 0检查。在 Lima 下用lima sudo调用;在其他 VM 环境请先切换为 root。
  • SELinux 相关报错:若容器无法启动,优先检查二进制 SELinux 标签(chcon -t container_runtime_exec_t)与enable_selinux配置;临时弱化可设SELINUX=PermissiveSELINUX=Disabled重新运行 provision。
  • 更换运行时:设置RUNC_FLAVOR=crun即可用 crun 替代 runc 验证另一条 OCI 运行时路径;注意 crun 会被安装为/usr/local/sbin/runc名称。
  • 更换 Go 版本:通过GO_VERSION环境变量控制,当前默认值为1.27.1(provision.sh)。
  • 追加系统包INSTALL_PACKAGES="pkg1 pkg2"会被直接拼入dnf install,适合按需补充内核模块或调试工具。
  • 获取测试日志test-cri.sh的 containerd 日志始终被导出到/tmp/containerd.log;集成测试失败时配合 CI 的dmesg -T -f kern可快速定位内核层面问题。

八、小结

script/vm/这套脚本把 containerd 集成测试的"环境准备 + 测试执行"完整自动化,从零开始依次完成软件包安装、Go 工具链、runc/crun、CNI 插件、cri-tools、containerd 构建安装、SELinux 与 containerd 配置,再到核心集成测试、CRI 集成测试与critest一致性测试的串行执行。得益于与 CIintegration-vm任务完全一致的脚本与参数,开发者可以在本地 Lima 虚拟机中 1:1 复现 CI 测试矩阵,是提交 PR 前自测与排查 CI 失败的有力工具。若需调整环境细节,环境变量一览 一节给出了全部可调入口。

【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询