简介:面向 Linux AMD64 架构的 containerd 1.7.23 CRI 集成压缩包,定位为 Kubernetes 节点容器运行时组件,适用于需要在离线环境部署、升级或替换 CRI 运行时的运维与集群管理人员。包内共 19 个文件、101.22MB,以 containerd、crictl、ctr、containerd-shim-runc-v1/v2 及 runc 等可执行文件为核心,同时提供 systemd service 模板、yaml 声明式配置和 env/sh 辅助脚本,可支撑服务托管、命令行调试、自动化初始化和离线安装等场景。尤其适合将节点接入 Kubernetes 前完成 containerd 基础调优,或在内网环境通过离线安装包统一各节点运行时版本。随包附带的弃用说明标记了当前版本后续迁移方向,便于使用者在生产环境规划升级路径。已有 261 人学习下载,读者可结合目录结构理解 CRI 插件的组成,并通过 crictl、ctr 等工具掌握镜像管理和排错方法;对于需要维护多节点 Kubernetes 集群的工程师,也能借助其中的配置模板统一运行时参数,为容器平台维护打牢基础。 接手新节点时看到cri-containerd-1.7.23-linux-amd64.tar这种安装包,很多人第一反应是“这不就是个 tar 包嘛,解压就行”,但真到了生产环境配置 kubelet、对接网络插件的时候,各种问题才冒出来。我在这上面踩过不少坑,所以今天把针对这个包的完整安装、配置、验证和排障过程梳理一遍,给做 Kubernetes 运维的朋友一个可直接参考的流程。这篇内容适合刚接触 containerd 的入门者,也适合已经在用 docker 运行时、准备切换到 containerd 的集群运维,按步骤走基本能避开 90% 的常见问题。
1. 安装前先搞懂:这个 cri-containerd 包里到底装了什么
1.1 它和 containerd、CRI 是什么关系
先说背景。Kubernetes 从 1.24 版本开始默认使用 containerd 作为容器运行时,这在业界已经是标准做法。但 Kubernetes 本身不会直接调用 containerd,而是通过 CRI(Container Runtime Interface)这套接口来统一管理容器生命周期。cri-containerd这个名称里的cri前缀,指的就是 containerd 中负责实现 CRI 插件的部分。在 containerd 1.0 时代,CRI 插件还是一个独立的二进制,叫cri-containerd,部署时要单独拉起进程;后来插件直接内置到了 containerd 主进程里,现在的 tar 包里已经看不到那个独立二进制了,所以看到一个包名带着cri-前缀,意味着这是官方专门为 Kubernetes 场景打好的发布包,里面包含了 containerd 主程序、shim 进程、crictl和ctr命令行工具,还会带上基础的 CNI 网络插件。
很多刚接触的人会把crictl和ctr搞混。简单说,ctr是 containerd 自己的调试工具,直接操作 containerd API,但它不认识 Kubernetes 那一套装镜像、起沙箱的流程;而crictl是 CRI 兼容的客户端,它通过 CRI 插件的 socket 和 containerd 通信,Kubernetes 也是走这条路。所以日常排查集群里 Pod 的容器状态,用crictl更贴近真实场景,ctr只适合做底层调试和离线镜像导入。
1.2 版本号和 linux-amd64 为什么必须看仔细
1.7.23是 containerd 1.x 系列的一个稳定版本。1.x 是当前生产环境最成熟的主线版本,1.7 又是 1.x 里的长期维护分支,所以选 1.7.x 作为集群运行时版本是稳妥的做法。有一点要提前知道:containerd 2.x 版本已经在推进,配置格式和默认行为会有变化,等未来要升级时,config.toml里的version字段、插件路径这些都得重新核对,不能想当然直接沿用 1.7 的配置。
linux-amd64指的是为 Linux 操作系统、x86_64 架构编译的二进制包。如果服务器不是这个架构,比如 ARM 架构的国产化服务器,那就得下linux-arm64版本。判断架构用一条命令:uname -m,输出是x86_64就用 amd64 包,输出是aarch64就用 arm64 包。选错架构的后果很直接:二进制要么报Exec format error,要么直接段错误,根本起不来。另外要注意,这个 tar 包是 gzip 压缩的,虽然文件名尾部是.tar,实际能用tar -zxvf解压,这和常见命名习惯略有差别,别到时候卡在解压这一关。
2. 完整安装流程:从 tar 解压到 systemd 服务启动
2.1 解压的正确姿势和目录结构
下载安装包之后,我习惯先在临时目录解压,看清楚文件结构再安装到系统目录,而不是直接一把tar -zxvf解到根目录覆盖。这样能避免不小心覆盖掉系统里已有的同名配置,尤其在服务器上已经装过旧版本 containerd 的情况下,先看再动非常重要。
mkdir -p /tmp/cri-pkg tar -zxvf cri-containerd-1.7.23-linux-amd64.tar -C /tmp/cri-pkg tree /tmp/cri-pkg解压后的典型结构大致是:
usr/local/bin/ ├── containerd ├── containerd-shim ├── containerd-shim-runc-v2 ├── crictl └── ctr etc/containerd/ └── config.toml etc/cni/net.d/ opt/cni/bin/不同小版本的发布包目录可能有些微调,但核心就两块:一是二进制文件,二是配置文件。确认结构没问题后,再把内容放到对应系统目录:
cp -r /tmp/cri-pkg/usr/local/bin/* /usr/local/bin/ mkdir -p /etc/containerd cp /tmp/cri-pkg/etc/containerd/config.toml /etc/containerd/config.toml二进制放到/usr/local/bin而不是/usr/bin,主要是为了和发行版自带的包管理路径区分开。containerd主进程、containerd-shim-runc-v2这个 shim 进程、crictl、ctr这五个文件必须齐全,缺了 shim 会导致启动容器时直接报 OCI 相关错误。CNI 插件目录可以后续按网络方案再补充,但如果包里有opt/cni/bin,也建议一起放到/opt/cni/bin,后面配置容器网络时会省很多事。
2.2 如何正确配置 systemd 托管
containerd 通常以 systemd 服务方式运行,方便开机自启和崩溃恢复。不要自己搞个nohup后台启动就算完事,生产环境必须用服务管理器。这里我直接给出一个经过实践验证的 unit 文件内容,存到/etc/systemd/system/containerd.service:
[Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target local-fs.target [Service] ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/local/bin/containerd Type=notify Delegate=yes KillMode=process Restart=always RestartSec=5 LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity OOMScoreAdjust=-999 [Install] WantedBy=multi-user.target这个配置里有几个关键点值得细说。ExecStartPre=-/sbin/modprobe overlay是提前加载 overlay 内核模块,overlayfs 是 containerd 默认的存储驱动,缺了它容器镜像层没法正常工作,前面那个减号表示即使模块已存在、命令以非零状态退出也不影响服务启动。Type=notify让 containerd 自己通过 sd_notify 通知 systemd 启动完成,而不是靠超时硬等。Delegate=yes非常关键,它把 cgroup 子树的控制权完全委托给 containerd,这样 kubelet 和容器才能正确管理 CPU、内存等资源限制。KillMode=process保证 systemd 停止服务时只杀 containerd 主进程,不会误杀正在运行的容器进程。
配置写好后按顺序执行:
systemctl daemon-reload systemctl enable --now containerd systemctl status containerd启动后先看状态是否active (running),再确认 socket 文件是否生成。默认情况下 containerd 会监听/run/containerd/containerd.sock,这个 socket 就是 kubelet 和 crictl 的接入点。如果服务起不来,立刻用journalctl -u containerd -n 50看日志,定位速度比瞎猜快得多。
3. 关键配置与镜像管理:让集群真正跑起来
3.1 config.toml 里必须改的几个参数
containerd 的默认配置文件可以直接用它自己生成的标准版本,再针对 Kubernetes 场景做修改。用containerd config default > /etc/containerd/config.toml重新生成一份是最稳妥的,不会出现缺字段、格式过时的问题。生成后重点关注 CRI 插件这一段。
config.toml是 TOML 格式,核心配置结构大致是这样的:
version = 2 root = "/var/lib/containerd" state = "/run/containerd" [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9" [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true第一个必须改的是sandbox_image。默认值指向registry.k8s.io/pause:3.9,但很多网络环境访问这个地址非常慢甚至超时。我常用的替代方案是registry.aliyuncs.com/google_containers/pause:3.9,这个 pause 镜像就是每个 Pod 的“沙箱”容器,它不承载业务,只负责维持 Pod 的网络命名空间,版本最好和集群期望一致,否则会出现沙箱反复重建的问题。
第二个必须关注的是SystemdCgroup。如果 kubelet 的 cgroup 驱动是systemd(kubeadm 默认推荐这个值),那这里必须设为true;如果设成false,kubelet 启动后要么报 cgroup driver 不匹配,要么节点状态异常。这个参数的位置在不同版本里略有变化,1.7 版本里放在 runc runtime 的 options 下面,核对配置时别找错地方。改完配置后执行systemctl restart containerd,重启后立刻journalctl -u containerd确认没有报错。
3.2 离线环境的镜像导入操作
很多生产环境是内网隔离的,没法直接拉公网镜像,这就绕不开离线镜像导入。常见做法是先在能联网的机器上用 Docker 把镜像打成 tar 包,再带到内网导入 containerd。注意这里导入的命名空间必须是k8s.io,因为 CRI 插件只从k8s.io这个命名空间读取镜像,如果你用ctr images import时不指定命名空间,镜像是导入了,但 kubelet 就是看不到。
docker save my-app:1.0 -o my-app-1.0.tar # 在目标节点上执行 ctr -n=k8s.io images import my-app-1.0.tar ctr -n=k8s.io images list如果后续还要给镜像打 tag,用ctr -n=k8s.io images tag即可。还有一种更省事的做法,是把整个sandbox_image对应的 pause 镜像也打成 tar 离线导入,这样内网节点从启动到就绪完全不需要外网。曾遇到一个客户环境,所有节点都没外网,我提前把 pause、flannel、coredns 等基础镜像全部离线导入,集群初始化一气呵成,这个习惯建议运维同学都保留下来。
3.3 kubelet 怎么和 containerd 对接
如果节点是 kubeadm 初始化或加入集群的,kubelet 默认就会走 containerd,因为 v1.24 之后--container-runtime参数默认值就是remote,--container-runtime-endpoint默认是unix:///run/containerd/containerd.sock。但手动部署 kubelet 时,一定要在启动参数里显式指定这两个参数,不然 kubelet 会尝试用 docker socket,导致节点一直 NotReady。
--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock为了日常排查方便,建议同时配置/etc/crictl.yaml:
runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 2 debug: false这个文件配好后,crictl ps、crictl images等命令不用反复带--runtime-endpoint参数,效率高很多。
4. 高频踩坑记录与排查方法
4.1 常见错误速查表
我在实际环境里把这些典型的故障现象和对应解法整理了一下,按出现频率排的:
| 现象 | 根因 | 处理方式 |
|---|---|---|
kubelet 报failed to run Kubelet: cgroup driver "cgroupfs" is not supported by systemd | containerd 的 SystemdCgroup 未开启 | 修改 config.toml 中对应字段为 true,重启 containerd |
节点 NotReady,日志出现sandbox image ... not ready | pause 镜像拉不下来 | 替换 sandbox_image 为可用加速地址,或离线导入 pause 镜像 |
Pod 创建卡在ContainerCreating,报network plugin is not ready | CNI 配置缺失或 CNI 插件文件不存在 | 安装 flannel/calico 的 CNI 插件,确保/opt/cni/bin有对应二进制、/etc/cni/net.d有配置文件 |
启动容器提示OCI runtime create failed: unable to start container process | runc 或 shim 缺失、版本过旧 | 确认/usr/local/bin/containerd-shim-runc-v2存在,必要时单独安装新版本 runc |
执行 containerd 命令报Exec format error或bad CPU type | 架构选错,下了 amd64 包用在 arm64 机器 | 用uname -m确认架构,换成 arm64 版本 |
Failed to load cni config,containerd 启动失败 | /etc/cni/net.d下有损坏配置或目录不存在 | 清空该目录或放入正确 conflist 文件 |
| 离线导入镜像后 kubelet 仍拉不到 | 导入时没指定k8s.io命名空间 | 用ctr -n=k8s.io images import重新导入 |
这张表基本覆盖了我在不同环境里遇到的所有高频问题,剩下的一些偶发问题大多和网络、DNS 相关,先看journalctl再顺着日志链路排查就好。
4.2 排查流程和避坑经验总结
排查 containerd 相关故障时,我习惯按这个顺序来:先确认进程,再确认 socket,最后看日志和端到端验证。
systemctl status containerd ls -l /run/containerd/containerd.sock journalctl -u containerd --since "5 minutes ago" crictl version crictl ps -a这几条命令如果都正常,那说明 containerd 层面基本没问题,再把排查重点转到 CNI、镜像拉取或 kubelet 参数上。如果crictl ps能列出沙箱容器但 Pod 网络不通,大概率是 CNI 插件配置与集群网络方案不匹配,可以去/etc/cni/net.d目录检查配置文件内容,确认没有遗留旧版本网络插件生成的文件。
还有几个容易被忽略的细节:修改过config.toml后必须重启 containerd,而不是用systemctl reload,因为 containerd 不支持运行时增量重载所有配置;安装过程用到cp覆盖二进制时,如果旧进程还在运行,最好先停服务再替换文件,避免text file busy或运行中二进制冲突;tar 包解压出来的文件属性一般是正确的,但如果你手动移动过文件,检查一下是否有可执行权限。
在生产环境多节点批量部署时,我曾吃过一个教训:把第一台机器的配置直接scp到所有节点,结果因为版本号、CPU 架构不完全一致,导致部分节点启动异常。后来我养成了在每个节点上都先用uname -m和containerd --version校验环境的习惯,再决定用哪个安装包、推哪份配置。虽然多花一点时间,但省掉了大量返工。
最后分享一个小技巧:升级 containerd 版本前,先用ctr version和crictl version记录当前版本,升级完成后对比确认;同时把旧版本的config.toml备份一份放到/etc/containerd/config.toml.bak。有一次我因为手滑把新版默认配置覆盖了旧配置,导致原有镜像加速配置全部丢失,节点拉镜像速度直线下降,还好有备份才能快速回滚。版本升级这种事,宁可慢一点,也要给自己留好退路。
本文还有配套的精品资源,点击获取