containerd 实战:从 Docker 底层运行时到生产级容器管理

当你在服务器上敲下 docker run 时,真正拉起容器进程的并不是 Docker 守护进程本身,而是藏在它背后的 containerd——一个工业级的容器运行时(container runtime)。理解 containerd,是写好 Dockerfile 之后通往生产级容器管理的下一站:它既是 Docker 的底层引擎,也是 Kubernetes 通过 CRI 调用的事实标准运行时。本文从原理到命令,带你把 containerd 用在生产环境。

一、containerd 是什么:为什么 Docker 背后是它

容器生态里长期存在”运行时”的分层。早年的 Docker 引擎大而全,包含了镜像构建、网络、编排等大量功能;而 containerd 是从 Docker 中抽离出来的核心运行时,专注于”管好镜像、管好容器生命周期”这一件事。2017 年 Docker 将 containerd 捐献给 CNCF,如今它已成为云原生领域最基础的组件之一。

用一句话区分三者关系:Docker CLI 调用 dockerd,dockerd 调用 containerd,containerd 再通过 runc 这样的底层运行时真正创建容器进程。在 Kubernetes 世界里,kubelet 则通过 CRI(Container Runtime Interface)插件直接对接 containerd,完全绕开 dockerd。这也是为什么很多生产集群选择”只装 containerd 不装 Docker”。

二、安装与版本对齐

containerd 的版本必须和 Kubernetes 版本匹配,错配会导致 CRI 接口不兼容。下面以 Ubuntu 22.04 为例,使用官方二进制方式安装,避免被发行版旧包坑到。

# 下载并解压 containerd 二进制(以 v1.7.x 为例)
wget https://github.com/containerd/containerd/releases/download/v1.7.20/containerd-1.7.20-linux-amd64.tar.gz
tar -C /usr/local -xzf containerd-1.7.20-linux-amd64.tar.gz

# 安装 runc(底层 OCI 运行时)
wget https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64
install -m 755 runc.amd64 /usr/local/sbin/runc

# 生成默认配置并启动
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
systemctl enable --now containerd
systemctl status containerd --no-pager

如果你更习惯用 Docker Compose 一键编排多服务,可以保留 Docker 不动——Docker 24+ 默认底层就是 containerd,两者并不冲突。而在 k3s 轻量级 Kubernetes 集群 中,containerd 已是默认内置运行时。

三、核心概念:五个你必须分清的对象

操作 containerd 前,先理清它的对象模型,否则很容易把”镜像”和”容器”搞混:

对象作用类比
Namespace资源隔离边界,默认 default,K8s 用 k8s.io租户/环境
Image拉取后存放的只读镜像安装包
Container被创建的容器元数据(尚未运行)已配置的实例
Task真正运行的进程(容器跑起来后才有)运行中的进程
Snapshot容器可写层/镜像只读层的存储文件系统层

关键区别:Container 只是”定义”,Task 才是”运行”。一个 Container 同一时刻只能有一个 Task,停掉 Task 后 Container 还在,可以再次 start 拉起新 Task。这一点和 Docker 的 docker create / docker start 一一对应。

四、日常管理:ctr 与 nerdctl

containerd 自带命令行 ctr,但它偏底层、不带镜像加速等友好特性,更适合排障。日常推荐用 nerdctl(兼容 Docker CLI 语法的 containerd 客户端)。

# ctr:指定命名空间查看镜像(注意必须带 -n 命名空间)
ctr -n k8s.io images ls
ctr images pull docker.io/library/nginx:1.27
ctr containers create docker.io/library/nginx:1.27 nginx-demo
ctr tasks start -d nginx-demo
ctr tasks ls

# nerdctl:语法几乎和 docker 一致,体验更顺手
nerdctl pull nginx:1.27
nerdctl run -d --name nginx-demo -p 8080:80 nginx:1.27
nerdctl ps
nerdctl images

五、配置镜像加速与私有仓库

国内拉取 Docker Hub 经常超时,需要在 /etc/containerd/config.toml 中配置镜像加速。注意 containerd 1.7 的配置结构和旧版差异较大,下面是最常用的写法:

# /etc/containerd/config.toml 关键片段
version = 2
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
    endpoint = ["https://registry.cn-hangzhou.aliyuncs.com", "https://docker.m.daocloud.io"]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.example.com"]
    endpoint = ["https://registry.example.com"]

# 私有仓库若用自签证书,需在此声明 ca 与明文(insecure)跳过
[plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com".tls]
  insecure_skip_verify = true

# 改完务必重启
systemctl restart containerd
场景配置位置常见坑
Docker Hub 加速registry.mirrors.”docker.io”只改了 endpoint 没重启
私有仓库registry.configs.”域名”.tls自签证书未信任导致拉取 401/x509
K8s 镜像默认走 k8s.io 命名空间用 ctr 忘加 -n k8s.io 看不到

六、与 Kubernetes 集成:CRI 与 runtimeClass

在 Kubernetes 的 Pod、Deployment、Service 体系中,kubelet 通过 CRI 调用 containerd。只需在 kubelet 启动参数里指向它即可:

# kubelet 关键参数(kubeadm 通常已自动配好)
--container-runtime=remote
--container-runtime-endpoint=unix:///run/containerd/containerd.sock

# 用 runtimeClass 让特定 Pod 走不同运行时(例如 runsc/gVisor)
kubectl get runtimeclasses
# 若集群同时装了 runc 与 runsc,可在 Pod spec 指定
# spec.runtimeClassName: runsc

当节点上 containerd 异常时,Cilium 基于 eBPF 的网络策略 往往仍在工作,但新 Pod 会卡在 ContainerCreating——这时第一时间 systemctl status containerd 和 ctr -n k8s.io tasks ls 排障最快。

七、生产级调优

默认配置在单机跑跑demo足够,但生产环境建议调以下几处,避免镜像多、并发高时把节点拖垮:

调优项建议值原因
io.concurrent 下载并发10→20加速大镜像拉取
snapshotteroverlayfs(默认)比 native 更快,需内核支持
registry 并发限制按需放开避免瞬间打满带宽
日志大小限制max-size=100m, max-file=5防止容器日志吃满磁盘
# 日志轮转配置片段(config.toml)
[plugins."io.containerd.grpc.v1.cri".containerd]
  default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true   # 配合 systemd 的 cgroup v2

# 容器日志上限(关键!否则 /var/log 会被撑爆)
[plugins."io.containerd.grpc.v1.cri".containerd]
  max_container_log_line_size = 16384

配合 Prometheus + Grafana 监控面板,把 containerd 的 container_blkio、任务数、镜像数等指标纳入看板,能在磁盘或内存异常前提前告警。

八、常见误区与排障清单

现象根因解法
ctr images ls 看不到镜像忘了 -n k8s.io加命名空间参数
拉取私有仓库报 x509自签证书未配置configs.”域名”.tls 信任或 insecure
Pod 卡 ContainerCreatingcontainerd 未就绪systemctl restart containerd + 查 sock
磁盘被镜像占满未做垃圾回收ctr images prune 或 crictl rmi

清理悬空镜像与快照是运维日常:ctr -n k8s.io images prune 可释放大量空间;若节点磁盘告警,优先排查 /var/lib/containerd 下的 overlay 快照而非盲目重启。

九、生产落地清单

检查项是否完成
containerd + runc 版本与 K8s 对齐☐
配置国内镜像加速☐
私有仓库证书/ insecure 已声明☐
SystemdCgroup = true(cgroup v2)☐
容器日志 max-size 限制☐
接入监控 + 磁盘告警☐

十、containerd 与 Docker:生产环境如何取舍

一个常见误区是”上了 containerd 就必须卸载 Docker”,事实并非如此。Docker 提供了完整的开发者体验:镜像构建、Compose 编排、友好的 CLI,这些 containerd 并不擅长。真正的变化发生在 Kubernetes 侧——自 1.24 版本起,K8s 移除了内置的 dockershim,kubelet 不再能直接驱动 Docker,节点运行时因此普遍转向 containerd 或 CRI-O。换句话说,开发机用 Docker 编写镜像、生产节点用 containerd 运行镜像,两者通过 OCI 标准镜像无缝衔接,才是当前主流的最佳实践。

维度Dockercontainerd
定位开发者全功能平台生产级运行时
镜像构建原生支持 build需配合 buildkit / nerdctl
CLI 友好度极高ctr 偏底层,nerdctl 接近
K8s 集成经 dockershim(已弃用)原生 CRI 对接
资源占用较重轻量

如果你的团队还没有全面拥抱 Kubernetes,或者重度依赖 Docker Compose 一键编排,保留 Docker 完全没问题;只要把”镜像制作”和”运行时”的职责分开,就能同时享受 Docker 的易用与 containerd 的高效。另外别忘了,镜像拉取下来不清理会持续蚕食磁盘,建议在 config.toml 中配置 content 与 snapshot 的垃圾回收阈值,或写一条定时任务执行 ctr -n k8s.io images prune,把节点磁盘健康交给自动化而不是人工巡检。

小结:containerd 把”容器运行时”这件大事做得足够纯粹——它不抢 Docker 的编排饭碗,也不越界做 K8s 的调度,而是稳稳站在两者之下。掌握它的命名空间模型、镜像配置与 CRI 集成方式,你就能在 Nginx 反向代理 与 GitHub Actions CI/CD 流水线 之外,把容器底座真正握在自己手里。当你下次遇到 Pod 起不来,第一反应不再是”重启 Docker”,而是直奔 containerd 看 Task 与快照——这就是生产级运维的差别。

上一篇 Spring Modulith 实战:用模块化单体替代微服务复杂度
下一篇 代码评审实战:从提单到合并的高效协作指南
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]