Docker 和 Containerd 有啥区别?

2025-09-17 Linux 运维

很多接触过 Docker 的人听说 containerd 时,第一反应是:"又多了一个容器技术?它和 Docker 到底什么关系?"

这篇文章从容器技术的分层讲起,把 Docker 和 containerd 的关系彻底说清楚。

一、一句话先总结

Docker 是一整套容器管理工具,containerd 是它底层的容器运行时引擎。

用汽车来比喻:

Docker       = 一辆完整的汽车(方向盘、油门、仪表盘、用户手册……)
Containerd   = 汽车发动机(只管"转",不管"怎么开")

Docker 让用户通过 CLI/API 操控容器;containerd 在幕后真正执行容器的创建、启动、停止等底层操作。

二、看一条命令的执行链路

当你运行:

docker run -d --name web -p 8080:80 nginx

背后发生了这些:

docker CLI
    │
    ▼
dockerd(Docker 守护进程)
    │
    ├── 解析镜像名,拉取 nginx 镜像
    ├── 创建容器配置(端口映射、网络、挂载……)
    │
    ▼
containerd          ← 这是 containerd!
    │
    ▼
containerd-shim     ← 每个容器一个 shim 进程
    │
    ▼
runc                ← OCI 运行时,真正创建容器的进程
    │
    ▼
nginx 容器(在独立的命名空间中运行)

Docker 负责"上层管理",containerd 负责"底层执行"。

三、从历史看关系

理解 Docker 和 containerd 的关键是知道它们是怎么来的。

阶段一:Docker 一家独大(2013-2016)

Docker 早期是一个巨大的单体守护进程(dockerd),什么都干:

  • 镜像构建
  • 镜像存储
  • 容器运行时
  • 网络管理
  • 卷管理
  • REST API
  • CLI

阶段二:OCI 标准分拆(2015-2017)

业界不想被 Docker 一家绑定,于是成立了 OCI(Open Container Initiative),制定了两套标准:

标准 规范内容
OCI Image Spec 镜像长什么样( layers、config、manifest)
OCI Runtime Spec 容器运行时怎么工作(namespace、cgroup、rootfs)

Docker 把底层的运行时组件捐给了 OCI,这就是 runc——一个轻量级容器运行时。

阶段三:containerd 独立(2017)

Docker 把 containerd 也捐给了 CNCF(云原生计算基金会),containerd 成为 CNCF 毕业项目。Docker 自己也开始使用 containerd 作为默认的底层运行时。

2013       2015        2017          现在
 │          │           │             │
 │   Docker 单体架构    │             │
 │  ┌──────────────┐    │             │
 │  │  dockerd     │    │             │
 │  │  (一体化)    │    │             │
 │  └──────────────┘    │             │
 │          │           │             │
 │          ▼           │             │
 │   OCI 标准诞生       │             │
 │   runc 独立          │             │
 │          │           │             │
 │          ▼           │             │
 │                containerd 独立     │
 │                Docker 改用         │
 │                containerd          │
 │          │           │             │
 │          ▼           │             │
 │                Docker = dockerd    │
 │                + containerd + runc │

四、Docker 和 containerd 的功能对比

维度 Docker Containerd
定位 完整的容器管理平台 轻量容器运行时
CLI docker 命令全家桶 ctr(调试用)、nerdctl(类 Docker CLI)
REST API Docker API(v1.47+) 自带 gRPC API(不对外暴露 REST)
镜像构建 ✅ Dockerfile + docker build ❌ 需配合 buildkit 或 Docker
镜像推送 ✅ docker push ✅ 通过 ctr/nerdctl
网络管理 ✅ 内置 bridge/host/overlay ❌ 需配合 CNI 插件
卷管理 ✅ 完整卷驱动 ❌ 需外部工具
容器编排 ✅ Docker Compose / Swarm ❌ 需配合 Kubernetes
Kubernetes 集成 ❌ 已弃用(通过 cri-dockerd 桥接) ✅ 原生 CRI 实现
资源占用 较大(含完整的上层逻辑) 极小(仅运行时的核心功能)

从资源占用角度看

# 看看进程数
ps aux | grep -E 'docker|containerd' | grep -v grep

# Docker 环境下的进程
root      dockerd              ← Docker 守护进程
root      containerd           ← containerd 守护进程
root      containerd-shim      ← 每个容器一个 shim
root      nginx                ← 容器进程本身

# 纯 containerd 环境(无 Docker)
root      containerd           ← containerd 守护进程
root      containerd-shim      ← 每个容器一个 shim
root      nginx                ← 容器进程本身

纯 containerd 省去了 dockerd 这个进程,以及它附带的所有上层逻辑。

五、Kubernetes 与两者的关系

这是 containerd 和 Docker 在生态位上的最大分水岭。

Kubernetes 的 CRI 抽象

K8s 不想绑定任何特定的容器运行时,因此定义了 CRI(Container Runtime Interface)——一个标准 gRPC 接口,只要实现了 CRI 的运行时都能被 K8s 使用。

Kubernetes
    │
    ├── CRI(标准接口)
    │       │
    │       ├── containerd    ← 原生 CRI 实现(推荐)
    │       │
    │       ├── CRI-O         ← 另一个轻量 CRI 实现
    │       │
    │       └── cri-dockerd   ← Docker 的 CRI 桥接(已弃用)
    │
    ▼
runc / crun / kata(OCI 运行时)

Docker 在 K8s 中的退场

Kubernetes v1.24+ 正式移除了 dockershim(内置的 Docker CRI 桥接)。这意味着:

  • K8s 不再原生支持 Docker 作为容器运行时
  • 如果你想用 Docker,需要额外安装 cri-dockerd(社区维护的桥接器)
  • containerd 是 K8s 官方推荐的默认运行时

为什么 K8s 要弃用 Docker?

不是因为 Docker 不好,而是因为:

  1. 多余的抽象层:K8s 只需要运行容器,Docker 提供了太多 K8s 用不到的功能(如 Docker Compose、Swarm、镜像构建)
  2. 性能损耗:每个操作都要经过 dockerd → containerd → runc 三层,而 containerd 可以直接到 runc
  3. 稳定性:containerd 直接实现 CRI,少了一层桥接就少了一个故障点

六、Docker 依然有用的场景

虽然 containerd 在 K8s 中取代了 Docker,但这不意味着 Docker 过时了。

Docker 不可替代的场景

场景 原因
本地开发 docker build / docker compose 是最顺手的工具链
CI/CD 流水线 构建镜像、推送镜像离不开 Docker
单机部署 不跑 K8s 的环境,Docker Compose 比 containerd + nerdctl 方便得多
镜像构建 containerd 没有内置镜像构建能力(需 buildkit)
生态完善度 Docker Hub、Docker Extension、大量第三方工具集成

哪些场景更适合 containerd?

场景 原因
生产环境 K8s containerd 是 K8s 原生 CRI 实现,更轻量更稳定
边缘计算 资源受限的设备上,去掉 dockerd 能省不少内存
IoT 设备 只需跑几个固定容器,不需要完整的 Docker 工具链

七、实际体验对比

Docker 环境

# 安装
apt install docker.io

# 启动容器
docker run -d --name nginx nginx

# 构建镜像
docker build -t myapp .

# 查看容器
docker ps

# 查看日志
docker logs nginx

纯 containerd 环境

# 安装
apt install containerd

# 启动容器(需 nerdctl)
nerdctl run -d --name nginx nginx

# 查看容器
nerdctl ps

# 查看日志
nerdctl logs nginx

# 或者用 ctr(containerd 自带 CLI,更底层的调试工具)
ctr images pull docker.io/library/nginx:alpine
ctr run docker.io/library/nginx:alpine nginx

nerdctl 是一个兼容 Docker CLI 语法的 containerd 客户端,由 containerd 社区维护。如果你从 Docker 迁移到 containerd,nerdctl 几乎可以无缝替换 docker。

八、总结

Docker 和 containerd 不是"二选一"的关系,而是"层次分工"的关系。
                    Docker 用户视角
  ┌─────────────────────────────────────────┐
  │  docker build / docker run / docker ps  │  ← 用户接触的 CLI
  ├─────────────────────────────────────────┤
  │                dockerd                  │  ← Docker 守护进程(管理镜像、网络、卷……)
  ├─────────────────────────────────────────┤
  │              containerd                 │  ← 容器运行时核心(真正的"干活"的)
  ├─────────────────────────────────────────┤
  │          containerd-shim + runc         │  ← OCI 运行时(创建容器进程)
  ├─────────────────────────────────────────┤
  │               nginx 容器                │  ← 最终运行的容器
  └─────────────────────────────────────────┘
                    K8s 用户视角
  ┌─────────────────────────────────────────┐
  │          kubectl run / apply            │  ← K8s CLI
  ├─────────────────────────────────────────┤
  │            kubelet + CRI                │  ← K8s 节点代理
  ├─────────────────────────────────────────┤
  │              containerd                 │  ← CRI 实现(原生,无桥接)
  ├─────────────────────────────────────────┤
  │          containerd-shim + runc         │  ← OCI 运行时
  ├─────────────────────────────────────────┤
  │               nginx 容器                │  ← 最终运行的容器
  └─────────────────────────────────────────┘

一句话最终版:

Docker 是"开车的人"——方向盘、油门、仪表盘一应俱全,适合开发者和单机场景。containerd 是"发动机"——只管高效稳定地运转,适合 K8s 和生产环境。它们本是同一件事的不同抽象层次,不是竞争对手。