containerd 安装配置与运维实战:K8s 节点从 Docker 迁移踩坑指南
2026/9/16 21:59:35 网站建设 项目流程

containerd 这个角色,我最早是从 Docker 的进程树里认出来的。当时执行ps -ef | grep containerd,发现真正在底下承接容器生命周期的是 containerd 和 containerd-shim,Docker 的守护进程更像是站在前面收指令的那一层。后来在裸机上搭 Kubernetes,把节点上的 Docker 摘掉、让 kubelet 直接对接 containerd,这件事我前后做过十几遍:apt 源装过、官方二进制包装过、内网完全离线的手工部署也搓过,中间踩的坑足够写成一篇长文。这篇就把安装路径、配置项的取舍、以及我遇到过的每一条报错摊开讲清楚。不管你刚接触容器、还没搞明白 containerd 和 Docker 是什么关系,还是已经在生产节点上运维、被depends: containerd (>= 1.2.6-0ubuntu1~)这类依赖报错卡了半小时,应该都能从这里面对上号。

1. 先把 containerd 这个角色讲明白

1.1 它在容器技术栈里到底站哪个位置

很多人第一次接触这个概念会有点懵:明明装了 Docker,怎么又冒出来一个 containerd。用一句话概括,Docker 负责的是"用户体验",containerd 负责的是"干活"。当你敲下docker run的那一刻,Docker 守护进程解析参数、准备好镜像和挂载信息,然后把真正创建容器、拉起进程、管理生命周期这件事交给 containerd,containerd 再通过 shim 进程去调 runc,runc 按照 OCI 标准把容器进程 fork 出来,自己退出。整个链条是 Docker daemon → containerd → containerd-shim → runc → 容器进程。

这层结构带来的最大好处是解耦。Kubernetes 从 1.24 版本开始正式移除 dockershim,kubelet 直接通过 CRI 接口找 containerd 说话,中间少了一层翻译,链路更短、故障点更少、资源占用也更低。我实测过同一台 4 核 8G 的机器,只跑 containerd 比跑完整 Docker + containerd 组合,常驻内存能省下几百兆,容器启动延迟在密集调度场景下也能感觉到差别。

理解了这层关系,后面很多问题就顺了。比如为什么ctr命令能看到镜像、docker images却看不到——因为它们本来就是两套客户端在跟同一个后端的不同命名空间对话。

1.2 单独装 containerd 到底图什么

如果你的场景只是本地开发、跑几个容器玩一玩,老实说 Docker 更省心,装完就能用,命令也熟悉。真正值得单独部署 containerd 的情况大概有三类。

第一类是 Kubernetes 节点。既然 dockershim 已经移除,节点上装 Docker 纯属多此一举,还会带来 cgroup 驱动不一致、镜像存储冗余的问题。第二类是资源敏感的服务器,比如一台机器上要塞几十上百个容器,少跑一个守护进程就是实打实的资源。第三类是对版本可控性要求高的生产环境,包管理器里的版本通常滞后,你想用 1.7 的某些新特性,就得自己掌管安装包。

我自己的判断标准很简单:这台机器上有没有编排系统在调度容器。有,就用 containerd;没有,Docker 更顺手。

1.3 版本怎么选,留一条能回退的路

containerd 的版本节奏跟 Kubernetes 是绑定的,选版本不要看"最新",要看目标集群支持到哪。1.6 系列稳定但偏老,1.7 系列是目前大多数发行版和 K8s 1.28 到 1.31 的推荐搭配。我的习惯是:先确认 kubelet 版本,再往上查官方兼容矩阵,然后在这个范围内选一个次版本号最新的补丁版。

还有一个容易被忽视的点——config.toml的版本字段。containerd 1.7 默认生成的是version = 2的配置,而网上大量教程还停留在version = 1的语法,直接照抄会把服务搞挂。这个问题我在第 6 节会展开讲。

留后路的做法很朴素:装之前把旧版本的二进制和配置文件都备份到/opt/bak/下面,配好包管理器的版本锁定。真出事的时候,回退比重装快十倍。

2. 动手之前,这四件事先确认清楚

2.1 系统信息与内核依赖核查

第一步不是敲安装命令,是把环境摸清楚。我固定会跑这几条:

uname -r # 内核版本,5.x 以下很多特性受限 cat /etc/os-release # 发行版和版本号 systemctl status containerd # 是否已经装过 which runc containerd ctr # 有没有残留二进制 ls /etc/containerd/ # 有没有遗留配置 df -h /var/lib/containerd # 数据目录空间

内核这块,overlayfs 是必须的,绝大多数 4.x 以上内核都自带。要额外确认的是br_netfilteroverlay两个模块有没有加载,K8s 场景下还需要net.bridge.bridge-nf-call-iptables这类转发参数。这些不是 containerd 自己的要求,而是容器网络的基础,提前配好能省掉后面一堆"容器起来了但网络不通"的排查。

另外要注意的是,很多云厂商的镜像里预装了 Docker,而 Docker 又自带一个 containerd。这种状态下直接装新版本,会出现两套二进制、两套 systemd 单元的混乱局面,必须先把环境清干净再动手。

2.2 源里的 containerd 和 docker.io 到底是什么关系

这是踩坑最多的地方,热词里那条docker.io : depends: containerd (>= 1.2.6-0ubuntu1~)就是典型症状。

Ubuntu 官方源里的docker.io是一个打包好的 Docker 发行版,它声明了对containerd这个包的依赖,版本要求是>= 1.2.6-0ubuntu1~。当你后面又从 Docker 官方源装了containerd.io,两个包都提供 containerd 二进制,但包名不同,apt 的依赖解析就会打架。轻则安装失败报依赖不满足,重则两个包互相覆盖文件,dpkg数据库出现不一致。

我的处理原则是:全机器统一来源,绝不混装。要么全走发行版源(docker.io+containerd),要么全走 Docker 官方源(docker-ce+containerd.io)。混用的代价远大于省下的那点配置时间。

诊断命令很实用:

apt-cache policy containerd containerd.io docker.io dpkg -l | grep -E 'containerd|docker' apt-get check

apt-cache policy会把每个包的候选版本、已装版本、来自哪个源全部列出来,一眼就能看出是不是版本墙上冲突了。

2.3 目录规划与磁盘占用预估

containerd 的数据默认落在/var/lib/containerd,这里面装的是镜像层、快照、元数据。它的空间占用有个特点:镜像层是共享的,同一个基础镜像被多个镜像引用时只存一份,所以实际占用往往比docker images显示的累计大小要小。

但如果你的机器要跑几十个不同镜像,那就得认真估算了。我的经验值是:常规业务镜像 200MB 到 1GB 不等,加上构建缓存和临时层,一台业务节点预留 100G 以上比较从容。

还要注意快照器的选择。默认是overlayfs,性能好、层数少。老内核上可能退化到nativedevmapper,后者需要额外配置 thin pool,属于另一种复杂度。装完后我一般会跑一条containerd config dump | grep snapshotter确认真实生效的是哪个。

2.4 时间同步和证书这件事别忽略

这个坑比较隐蔽。容器镜像仓库、私有 Harbor、TLS 证书校验,全都依赖系统时间。机器时间漂了几分钟,拉镜像时就会报x509: certificate has expired or is not yet valid,报错看起来像证书坏了,其实是时钟歪了。

装之前先确认:

timedatectl status systemctl status systemd-timesyncd

离线内网环境尤其要注意,通常内网有自己的时间源。装完 containerd 再回头查这个问题,会浪费大量时间在不该怀疑的方向上。

3. 三条安装路线,按环境挑一条

3.1 路线一:包管理器安装,最快也最容易被源坑

这是新手上手最快的方式。Debian / Ubuntu 系直接用发行版源:

apt-get update apt-get install -y containerd systemctl enable --now containerd

CentOS / Rocky / Alma 系:

dnf install -y containerd.io

如果想用更新的版本,就换成 Docker 官方源,Ubuntu 下的完整步骤是:

apt-get update apt-get install -y ca-certificates curl gnupg install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \ > /etc/apt/sources.list.d/docker.list apt-get update apt-get install -y containerd.io

这条路线的优点是包管理器负责依赖、升级和卸载,异常省心。缺点也很明确:版本受源限制,不同源之间容易冲突,docker.iocontainerd.io打架就是从这里来的。

注意:如果机器上已经装了docker.io,再装containerd.io之前,先执行apt-get remove --purge docker.io,或者反过来保持只用一套。看到unmet dependencies别急着--force,先看清楚是谁在依赖谁。

3.2 路线二:官方二进制包安装,可控性最高

生产节点我基本都走这条路。从 GitHub Releases 下载官方压缩包,解压到/usr/local,配置文件、systemd 单元全都自己掌舵,版本在文件路径里看得清清楚楚。

VERSION=1.7.20 wget https://github.com/containerd/containerd/releases/download/v${VERSION}/containerd-${VERSION}-linux-amd64.tar.gz tar Cxzvf /usr/local containerd-${VERSION}-linux-amd64.tar.gz

注意这个包里不包含 runc,需要单独装:

RUNC_VERSION=1.1.14 wget https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.amd64 install -m 755 runc.amd64 /usr/local/sbin/runc

runc 版本和 containerd 版本之间也有兼容性,我一般按官方文档给出的建议搭配,不盲目追新。

然后装 systemd 单元文件。官方仓库里有现成的containerd.service,下载到/usr/local/lib/systemd/system/下面:

wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service \ -O /usr/local/lib/systemd/system/containerd.service systemctl daemon-reload systemctl enable --now containerd systemctl status containerd --no-pager

这条路线的好处是任何一步出问题都能定位到具体文件,升级时直接替换二进制、systemctl restart就行。代价是要自己盯着 runc 的更新,因为 runc 出过的容器逃逸类问题不算少,运维上必须跟上补丁。

3.3 路线三:离线内网环境手工部署

内网机器上不了外网,这套流程就要提前在有网的机器上把物料备齐:containerd 压缩包、runc 二进制、systemd 单元文件、所需的 CNI 插件包、目标镜像的 OCI 归档。

打包过去之后:

tar Cxzvf /usr/local containerd-1.7.20-linux-amd64.tar.gz install -m 755 runc.amd64 /usr/local/sbin/runc cp containerd.service /usr/local/lib/systemd/system/ systemctl daemon-reload systemctl enable --now containerd

镜像的搬运有两种做法。一种是ctr images export导出成 tar,拷到目标机器ctr images import导入;另一种是在内网搭一个私有仓库,让节点直接拉。前者适合少量固定镜像,后者适合长期维护。我做过一个 30 节点规模的内网环境,最后选择了私有仓库方案,节点侧只需要配好hosts.toml指向内网地址,后续镜像更新不用再挨个机器拷文件。

3.4 三条路线的对比

维度包管理器安装官方二进制包离线手工部署
上手速度最快,一条命令中等,5 到 10 分钟慢,取决于物料准备
版本可控性受源限制完全可控完全可控
升级便利性一条命令搞定替换二进制重启需重新打包物料
依赖冲突风险高,容易混源
适合场景测试机、快速验证生产节点、K8s内网、隔离环境
常见坑点依赖打架、版本滞后忘记装 runc、单元文件缺失镜像搬运、证书信任

这张表是我在几个不同环境里实践下来的总结。选路线的时候先问自己一句:这台机器未来一年会不会频繁升级。会,就走二进制;不会,包管理器足够。

4. 配置文件的每一项都要能说出理由

4.1 先生成默认配置,再按需修改

不要从零手写config.toml,那是给自己找麻烦。正确做法是让 containerd 自己生成一份完整默认配置,再对着改:

mkdir -p /etc/containerd containerd config default > /etc/containerd/config.toml

生成之后先看一眼开头第一行的version字段。1.7 生成的是version = 2,1.6 及以前是version = 1。这两个版本的配置结构差异不小,尤其是 registry 相关的段落,在网上抄配置时必须先核对这一行,否则服务会直接起不来。

改配置之前先备份,这个习惯救过我很多次:

cp /etc/containerd/config.toml /etc/containerd/config.toml.bak.$(date +%F)

4.2 版本字段和 CRI 插件的关系

version = 2不只是个标记,它决定了 containerd 怎么解析整个 TOML 树。在 v2 里,所有插件配置都挂在plugins下面,比如plugins."io.containerd.grpc.v1.cri"。而在 v1 里,CRI 配置的路径写法不一样。

如果你在 v2 的配置里塞了一段 v1 时代的 registry 写法,containerd 启动时会报failed to load plugin io.containerd.grpc.v1.cri之类的错误,日志里还会带上unknown field的提示。这类报错的排查顺序永远是:先看version,再看段落的层级结构,最后才怀疑语法。

CRI 插件本身也要确认处于启用状态。默认配置里它是开着的,但如果你的配置文件里出现了disabled_plugins = ["cri"],那 kubelet 就连不上,表现为节点一直NotReady。这个问题的隐蔽性在于 containerd 服务本身是健康的,systemctl status一切正常,只有从 kubelet 视角才能看出问题。

4.3 SystemdCgroup 这一项决定了节点稳不稳

这是我最想强调的一项。在config.toml里找到:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = false

把它改成true

sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

为什么必须改?因为 kubelet 默认使用 systemd 作为 cgroup 驱动,而 containerd 默认用 cgroupfs。两边驱动不一致时,会出现资源统计错乱、Pod 被反复重启、节点在高负载下失联等一堆看起来毫无关联的故障。我在一个测试集群上就遇到过:kubectl top node显示的内存用量和实际差了一倍多,排查了很久才定位到是 cgroup 驱动没对齐。

同一段配置里还有sandbox_image,这个是 Pod 沙箱容器用的 pause 镜像。老配置里写的是已被弃用的地址,新环境要换成registry.k8s.io/pause:3.9这类可用地址:

sed -i 's#sandbox_image = ".*"#sandbox_image = "registry.k8s.io/pause:3.9"#' /etc/containerd/config.toml

改完这两处,systemctl restart containerd,再往下的兼容性问题基本就堵住了。

4.4 镜像加速和私有仓库怎么配才不踩空

containerd 1.7 之后,registry 配置推荐走config_path方式,比在config.toml里堆一长串 mirrors 清爽得多:

[plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d"

然后在/etc/containerd/certs.d/docker.io/hosts.toml里写:

server = "https://registry-1.docker.io" [host."https://your-mirror.example.com"] capabilities = ["pull", "resolve"]

目录名必须和仓库域名严格对应,比如要加速registry.example.com,目录就得叫/etc/containerd/certs.d/registry.example.com/。这个细节我一开始没注意,配了半天不生效,ctr拉镜像还是走原来的地址,最后才发现是路径名对不上。

私有仓库走 HTTPS 但用的是自签证书,就在同一个目录下放ca.crt;如果是 HTTP 仓库,则要在hosts.toml里额外声明skip_verify = true或把地址写进server并标注对应能力。生产环境我强烈建议上正经证书,跳过校验的做法只用来临时验证。

4.5 拉起服务并确认真的在工作

配置完成后:

systemctl daemon-reload systemctl restart containerd systemctl enable containerd systemctl status containerd --no-pager journalctl -u containerd -n 50 --no-pager

确认三件事:socket 文件存在、日志里没有error级别的报错、ctr version能正常返回。

ls -l /run/containerd/containerd.sock ctr version

socket 路径这一点要注意,/run/var/run在大多数发行版里是软链接关系,指向同一个位置,但有些工具配置文件里写的是绝对路径,两边对应不上就会报连接失败。我个人的写法是统一用/run/containerd/containerd.sock,因为它是 systemd 单元里实际声明的路径。

5. 日常运维命令,ctr、nerdctl、crictl 怎么分工

5.1 ctr 的命名空间是第一个坑

ctr是 containerd 自带的官方 CLI,功能全但用起来生硬。最大的门槛是命名空间概念。ctr images ls默认查的是default命名空间,而 Kubernetes 用的是k8s.io,所以很多人查完发现是空的,以为镜像丢了。

ctr namespace ls ctr -n k8s.io images ls ctr -n k8s.io containers ls ctr -n k8s.io tasks ls

我习惯在~/.bashrc里加一个别名省事:

alias ctrk='ctr -n k8s.io'

另一个坑是ctr run。它和docker run的语义差别很大,需要在后台运行、需要指定 task,很多参数写法也不同。日常调试容器我基本不用它,宁可上 nerdctl。

还有一点,ctr直接调用 containerd 的 API,不经过 CRI 层。这意味着你用ctr run创建的容器,kubelet 是不认识的,反之亦然。两边看到的镜像共享同一份 content store,但容器的管理是分开的,这个边界要分清楚,不然会困惑于"为什么我创建的容器在 K8s 里看不到"。

5.2 nerdctl 补齐 Docker 的使用手感

nerdctl 是一个兼容 Docker 命令风格的客户端,nerdctl runnerdctl psnerdctl imagesnerdctl compose这些都能用,迁移成本几乎为零。

VERSION=1.7.6 wget https://github.com/containerd/nerdctl/releases/download/v${VERSION}/nerdctl-${VERSION}-linux-amd64.tar.gz tar Cxzvf /usr/local/bin nerdctl-${VERSION}-linux-amd64.tar.gz nerdctl version

它默认也是连/run/containerd/containerd.sock,不用额外配置。有几个细节和 Docker 不同:默认命名空间是default,要操作 K8s 的镜像得加-n k8s.io;Compose 支持需要单独装nerdctl-full或者额外组件;构建功能用的是 BuildKit,首次用需要把 buildkitd 跑起来。

我用它最多的场景是排查。nerdctl -n k8s.io imagesctr的输出友好太多,nerdctl exec进容器也比crictl exec顺手。它是那种"平时不一定用,但一上手就回不去"的工具。

5.3 crictl 与 kubelet 对接的正确姿势

在 K8s 节点上,crictl是排查 Pod 问题的正主,因为它走的是和 kubelet 完全相同的 CRI 接口,看到的东西和 kubelet 看到的一致。

先写配置文件:

cat > /etc/crictl.yaml <<'EOF' runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF

然后常用命令:

crictl pods crictl ps -a crictl images crictl logs <container-id> crictl inspect <container-id> crictl rmi --prune

我平时排查 Pod 起不来的顺序是:crictl pods看沙箱在不在,crictl ps -a看容器状态,状态是Exited就直接crictl logs捞日志,日志为空就crictl inspect看退出码和配置。这套流程比从kubectl describe开始往下挖快得多,因为它是从节点侧看的,跳过了 API Server 那一层。

有个细节:crictl也有命名空间的概念,但它默认就绑在 k8s 的上下文里,不需要像ctr那样手动指定,这点比ctr省心。

6. 踩坑实录,我遇到过的报错和排查过程

6.1 依赖冲突那条报错到底怎么解

docker.io : depends: containerd (>= 1.2.6-0ubuntu1~)这条报错我在测试机上撞过三次,每次的成因都不完全一样。

第一次是机器上装了 Docker 官方源的containerd.io,后来又去装发行版源的docker.io,两个包都要提供 containerd 实现,apt 解析不动。第二次是发行版源里的containerd包版本太老,低于docker.io声明的最低要求。第三次最坑,是之前手动删过/usr/lib/systemd/system/containerd.service,包状态是"已安装但文件不完整",dpkg校验失败。

排查顺序我整理成这样:

dpkg -l | grep -E 'containerd|docker' # 看包状态 apt-cache policy containerd containerd.io # 看候选版本和来源 apt-get check # 看依赖树是否自洽 dpkg --audit # 看有没有半装状态的包

对应的解法:来源混装就统一到一个源,把另一套apt-get remove --purge干净;版本不够就升级源里的containerd;包状态损坏就apt-get install --reinstall containerd修复。绝对不要用--force-yes跳过依赖检查,我见过这么干之后系统日志里全是二进制版本不匹配的报错,排查成本翻好几倍。

6.2 服务起不来,日志会告诉你答案

systemctl start containerd之后状态是failed,先看日志:

journalctl -u containerd -n 100 --no-pager

按报错内容分几类。出现toml: line X: expected ...就是配置文件语法错了,通常是缩进或者引号问题,TOML 对字符串里的反斜杠特别敏感,Windows 路径风格的写法会直接炸。出现unknown field说明配置字段在当前版本不存在,多半是把其他版本的写法抄进来了。出现failed to load plugin就去看那一段插件的层级对不对。

还有一种情况是服务显示active但 socket 不存在。这通常是 socket 路径被改过,或者containerd.service里的ExecStart参数和你的预期不一致。systemctl cat containerd可以把实际生效的单元文件完整打出来,比猜快。

端口和权限问题偶尔也会碰到。自定义 socket 路径落在没有写权限的目录下,containerd 启动会失败但日志不够直白,这时候检查一下目录权限和 SELinux 状态。

6.3 拉镜像失败,先分清是哪一类

镜像拉不下来,报错信息看着都差不多,但成因差别大。

第一类是网络和 DNS。报错常见dial tcp: i/o timeoutno such host。先ping仓库域名,再curl -I试试连通性,再看/etc/resolv.conf。内网环境要确认 DNS 能不能解析到私有仓库。

第二类是证书。报错带x509字样,先查系统时间,再查 CA 证书有没有导入到正确位置。自签证书要放在对应仓库目录下的ca.crt里,放错目录等于没放。

第三类是仓库地址或命名空间写法问题。镜像名里带端口号、带多层路径的时候,目录结构的对应关系容易搞错。规范写法是仓库域名:端口/项目名/镜像名:标签,containerd 会按第一段去certs.d下面找同名目录。

排查的时候我会直接上ctr绕过 CRI 层:

ctr -n k8s.io images pull registry.example.com/app/api:v1.2.3

这样报错信息更底层、更具体。如果ctr能拉下来但 kubelet 拉不下来,那就是 CRI 配置的问题,方向立刻清晰。

6.4 shim 和 runc 那些奇怪的报错

有一类报错看起来和安装没关系,但根子上是安装时的疏漏。最典型的是:

failed to start shim: exec: "runc": executable file not found in $PATH

多半是走二进制路线时忘了装 runc,或者装到了/usr/local/bin而 systemd 单元的PATH里没有这个目录。确认方式:

which runc runc --version systemctl show containerd -p Environment

还有一种是 shim 进程残留。节点上跑过很多容器之后,ps -ef | grep containerd-shim能看到一堆僵尸 shim,正常情况 containerd 会自动回收,但如果某个容器状态异常,shim 会一直挂着占资源。处理方式是先确认没有业务容器受影响,再清理对应进程。生产环境上做这个操作必须格外谨慎,我在测试环境验证过才敢在线上执行。

6.5 问题速查表

现象常见成因排查命令处理方式
装包报依赖不满足混用不同来源的包apt-cache policy containerd containerd.io统一到一个源,卸载冲突包
服务启动失败配置文件语法或字段错误journalctl -u containerd -n 100对照version字段修正层级
socket 不存在路径不一致或权限问题systemctl cat containerd核对单元文件里的 socket 路径
ctr 看不到镜像命名空间不对ctr namespace ls-n k8s.io
拉镜像超时DNS 或网络不通curl -I <registry>检查 DNS、代理、加速配置
证书校验失败系统时间漂移或 CA 未导入timedatectlopenssl x509 -noout -dates同步时间、补 CA 证书
节点 NotReadyCRI 插件被禁用或 cgroup 驱动不一致systemctl status kubelet检查配置里的插件与SystemdCgroup
shim 找不到 runcrunc 未安装或不在 PATHwhich runc安装 runc 并确认路径
资源统计异常cgroup 驱动不一致containerd config dump打开SystemdCgroup = true
磁盘被写满镜像与日志累积df -hctr images ls清理无用镜像,限制日志轮转

这张表是我用最久的排查清单,几乎覆盖了节点运维中九成以上的常见故障。

7. 一些不太写进官方文档的经验

安装这件事,文档能告诉你怎么做,但告诉不了你哪里会绊倒你。我把自己反复踩过、也反复教给同事的几条记在这里。

配置改完先跑一遍 dry check。containerd 没有专门的--check参数,但可以用containerd config dump来验证配置能否被正确解析,它能输出就说明语法至少没错。这个动作能做在restart之前,省掉一次服务中断。

升级 containerd 之前先确认节点上没有正在跑的构建任务。BuildKit 这类组件对后端的版本比较敏感,升级过程中如果刚好在构建镜像,容易留下状态不一致的历史层,清理起来很麻烦。

日志轮转要在装的时候就配好。containerd 的日志默认进 journald,节点上容器一多,journal 的体积增长很快。我给生产节点的做法是限制 journal 的最大占用,同时对/var/lib/containerd单独挂载或者做容量监控,避免哪天突然把根分区写满。

备份配置这件事,别只备份config.tomlcerts.d目录下的仓库证书和hosts.toml同样重要,一旦机器重装,重新配一遍认证比恢复一个文件慢太多。我的做法是把/etc/containerd整个目录纳入配置管理,改之前先提交一次。

最后还有一条关于心态的。containerd 的报错信息普遍比 Docker 简略,新手容易慌。真遇到搞不定的,把日志级别调成debug再看一遍,信息量会大得多:

sed -i 's/^level = .*/level = "debug"/' /etc/containerd/config.toml systemctl restart containerd

调完记得改回来,debug 级别的日志在繁忙节点上写入量相当可观。

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

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

立即咨询