mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1589 字
5 分钟
containerd 架构
2026-05-05

如果说 runc 是容器的”引擎”,那 containerd 就是容器的”操作系统”——它管理镜像的拉取和解压、容器的创建和启动、任务的监控和重启、事件的发布和订阅。Docker 用它管理容器生命周期,Kubernetes 通过 CRI 接口与它交互,cloud-native 生态的每一个角落都有它的身影。

containerd 的设计哲学是简洁且可组合——它只做容器运行时该做的事,不做编排(那是 Kubernetes 的事),不做网络(那是 CNI 的事),不做存储(那是 CSI 的事)。这种”只做一件事并做好”的设计,让 containerd 成为了云原生基础设施的坚实底座。

containerd 的诞生源于 Docker 架构的一次关键拆分。早期的 Docker 是一个单体架构——docker daemon 同时负责镜像管理、容器生命周期、网络配置、存储挂载,所有功能耦合在一个进程中。2016 年,Docker 将容器生命周期管理拆分为独立的 containerd 项目,docker daemon 只保留上层 API 和构建功能。2017 年,containerd 加入 CNCF 孵化,2019 年成为 CNCF 毕业项目。从 Docker 的”内部组件”到”行业标准容器运行时”,containerd 完成了身份的蜕变——它不再只是 Docker 的附属,而是 Kubernetes、云平台、边缘计算等场景的默认选择。理解这段历史,有助于理解 containerd 的设计哲学:它”只做容器运行时该做的事”,因为它的诞生就是为了从 Docker 的”大而全”中剥离出”小而美”。

前置知识#

  • Ch06 runc 源码分析:containerd 调用 runc 创建容器,理解 runc 是理解 containerd 的前提
  • Ch05 OCI 规范详解:containerd 遵循 OCI 规范管理镜像和容器
  • gRPC 基础:containerd 的所有 API 通过 gRPC 暴露
Note

containerd 的设计哲学是”可组合”——它通过插件体系扩展功能,核心只做容器生命周期管理。这种设计让它既轻量又灵活。

本章将深入 containerd 的架构设计,理解它如何在 runc 之上构建工业级容器管理能力。

一、containerd 整体架构#

1.1 架构全景#

graph TB subgraph 客户端["客户端"] DOCKER["Docker CLI"] K8S["Kubernetes (CRI)"] CTR["ctr CLI"] end subgraph API层["gRPC API 层"] IMAGESVC["Image Service<br/>镜像管理"] CONTAINERSVC["Container Service<br/>容器管理"] TASKSVC["Task Service<br/>任务管理"] CONTENTSVC["Content Service<br/>内容存储"] DIFFSVC["Diff Service<br/>层差异计算"] EVENTSVC["Event Service<br/>事件订阅"] end subgraph 核心层["核心服务层"] META["Metadata Store<br/>元数据存储 (boltdb)"] CONTENT["Content Store<br/>内容存储 (文件系统)"] SNAPSHOT["Snapshot Service<br/>快照管理 (OverlayFS)"] RUNTIME["Runtime Service<br/>运行时管理 (shim+runc)"] end subgraph 运行时层["运行时层"] SHIM["containerd-shim"] RUNC["runc"] CONTAINER["容器进程"] end DOCKER --> IMAGESVC K8S --> IMAGESVC CTR --> IMAGESVC IMAGESVC --> META IMAGESVC --> CONTENT IMAGESVC --> SNAPSHOT TASKSVC --> RUNTIME RUNTIME --> SHIM SHIM --> RUNC RUNC --> CONTAINER CONTENTSVC --> CONTENT DIFFSVC --> SNAPSHOT style 客户端 fill:#bbdefb,stroke:#1565c0 style API层 fill:#c8e6c9,stroke:#2e7d32 style 核心层 fill:#fff3e0,stroke:#e65100 style 运行时层 fill:#e1bee7,stroke:#6a1b9a

1.2 核心组件#

组件职责存储后端
Content Store存储镜像层和配置的原始内容文件系统
Metadata Store存储镜像、容器、快照的元数据BoltDB
Snapshot Service管理文件系统快照(OverlayFS)overlayfs/btrfs/zfs
Diff Service计算文件系统层之间的差异
Runtime Service管理容器运行时(shim+runc)
Event Service发布/订阅容器事件

1.3 containerd 的数据流#

sequenceDiagram participant Client as 客户端 participant API as gRPC API participant Content as Content Store participant Snapshot as Snapshot Service participant Runtime as Runtime Service participant Shim as containerd-shim participant Runc as runc Note over Client,Runc: 镜像拉取流程 Client->>API: Pull Image API->>Content: 存储 Manifest API->>Content: 存储 Config API->>Content: 存储 Layers (blob) API->>Snapshot: Unpack Layers → 创建快照 API-->>Client: Image Ready Note over Client,Runc: 容器创建流程 Client->>API: Create Container API->>Snapshot: 创建可写快照 (upperdir) API->>Runtime: 启动 shim 进程 Runtime->>Shim: fork + exec Shim->>Runc: runc create Runc-->>Shim: 容器 PID Shim-->>Runtime: 容器已创建 API-->>Client: Container Created Note over Client,Runc: 容器启动流程 Client->>API: Start Container API->>Runtime: runc start Runtime->>Shim: runc start Shim->>Runc: runc start Runc-->>Shim: 容器运行中 API-->>Client: Container Running

二、gRPC API#

2.1 API 服务列表#

containerd 通过 gRPC 暴露以下核心服务:

服务功能关键方法
Images镜像管理List, Get, Create, Delete
Containers容器管理List, Get, Create, Delete, Update
Tasks任务管理Create, Start, Kill, Delete, Wait, Exec
Content内容管理List, Info, Read, Write, Delete
Snapshots快照管理Prepare, Commit, Mounts, Remove
Diff差异计算Compare, Apply
Leases租约管理Create, Delete, List
Events事件订阅Subscribe
Introspection自省Plugins, ServerInfo

2.2 镜像管理 API#

// containerd 镜像管理示例
package main
import (
"context"
"fmt"
"log"
containerd "github.com/containerd/containerd/v2/client"
)
func main() {
// 1. 连接 containerd
client, err := containerd.New("/run/containerd/containerd.sock")
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
// 2. 拉取镜像
image, err := client.Pull(ctx, "docker.io/library/nginx:latest",
containerd.WithPullUnpack, // 自动解压
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Pulled image: %s\n", image.Name())
// 3. 创建容器
container, err := client.NewContainer(ctx, "my-nginx",
containerd.WithImage(image),
containerd.WithNewSnapshot("my-nginx-snapshot", image),
containerd.WithNewSpec(
containerd.WithImageConfig(image),
containerd.WithHostNamespace(containerd.NetworkNamespace),
),
)
if err != nil {
log.Fatal(err)
}
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
// 4. 创建并启动任务
task, err := container.NewTask(ctx, containerd.NewIO(os.Stdin, os.Stdout, os.Stderr))
if err != nil {
log.Fatal(err)
}
defer task.Delete(ctx)
if err := task.Start(ctx); err != nil {
log.Fatal(err)
}
// 5. 等待任务退出
status, err := task.Wait(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Task exited with status: %d\n", <-status)
}

2.3 容器管理 API#

# 使用 ctr 命令行操作 containerd
# 镜像操作
ctr images pull docker.io/library/nginx:latest
ctr images list
ctr images inspect docker.io/library/nginx:latest
# 容器操作
ctr containers create docker.io/library/nginx:latest mynginx
ctr containers list
# 任务操作
ctr tasks start mynginx
ctr tasks list
ctr tasks kill mynginx
ctr tasks attach mynginx
# 快照操作
ctr snapshots list
ctr snapshots prepare my-snapshot
ctr snapshots commit my-snapshot my-committed-snapshot

三、镜像管理#

3.1 镜像拉取流程#

containerd 拉取镜像的完整流程:

  1. 解析引用:将 nginx:latest 解析为完整的 registry 路径
  2. 获取 Manifest:从 Registry 拉取镜像 Manifest
  3. 下载 Blob:按需下载 Config 和 Layer Blob
  4. 存储 Content:将 Blob 存储到 Content Store
  5. 解压 Layer:将 Layer 解压到 Snapshot Service
  6. 注册 Image:在 Metadata Store 中注册镜像
# 查看 containerd 的内容存储
ls /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256/
# 查看已下载的 blob
ctr content list
# 查看快照
ctr snapshots list

3.2 Snapshot Service#

Snapshot Service 管理容器的文件系统快照,基于 OverlayFS 实现:

操作说明OverlayFS 对应
Prepare创建可写快照创建 upperdir + merged
Commit提交可写快照为只读将 upperdir 转为 lowerdir
Mounts获取挂载点返回 overlay mount 选项
Remove删除快照删除 upperdir/lowerdir
# 查看 containerd 的 OverlayFS 快照
ls /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/
# 查看快照的元数据
ctr snapshots list
# 查看容器的 OverlayFS 挂载
mount | grep overlay

3.3 镜像层与快照的映射#

graph TB subgraph OCI镜像["OCI 镜像层"] L1["Layer 1: Ubuntu base<br/>sha256:abc123"] L2["Layer 2: nginx 包<br/>sha256:def456"] L3["Layer 3: 配置文件<br/>sha256:ghi789"] end subgraph ContentStore["Content Store"] B1["blob abc123 (tar.gz)"] B2["blob def456 (tar.gz)"] B3["blob ghi789 (tar.gz)"] end subgraph SnapshotStore["Snapshot Store (OverlayFS)"] S1["Snapshot 1 (只读)<br/>lowerdir: base"] S2["Snapshot 2 (只读)<br/>lowerdir: base+nginx"] S3["Snapshot 3 (只读)<br/>lowerdir: base+nginx+config"] S4["Snapshot 4 (可写)<br/>upperdir: 容器修改"] end L1 --> B1 L2 --> B2 L3 --> B3 B1 -->|"unpack"| S1 B2 -->|"unpack + apply"| S2 B3 -->|"unpack + apply"| S3 S3 -->|"prepare (创建容器)"| S4 style OCI镜像 fill:#bbdefb,stroke:#1565c0 style ContentStore fill:#c8e6c9,stroke:#2e7d32 style SnapshotStore fill:#fff3e0,stroke:#e65100

四、任务管理#

4.1 Container vs Task#

containerd 区分 Container 和 Task 两个概念:

概念说明生命周期
Container容器的配置和元数据持久化(直到删除)
Task容器的运行实例临时(进程退出即结束)
# Container 可以没有 Task(已停止的容器)
ctr containers list
# NAME IMAGE
# mynginx nginx:latest
ctr tasks list
# TASK PID STATUS
# (空——容器未运行)
# 启动 Task
ctr tasks start mynginx
ctr tasks list
# TASK PID STATUS
# mynginx 12345 RUNNING

4.2 Task 的创建流程#

// containerd 的 Task 创建流程(简化)
func (c *container) NewTask(ctx context.Context, ioCreator IOCreator) (Task, error) {
// 1. 获取容器的 rootfs 挂载点
mounts, err := c.snapshotter.Mounts(ctx, c.snapshotID)
// 2. 准备 OCI Bundle
bundle := &Bundle{
ID: c.id,
Path: c.bundlePath,
Rootfs: mounts,
Spec: c.spec,
}
// 3. 启动 containerd-shim
shim, err := shim.Start(ctx, bundle, c.runtime)
if err != nil {
return nil, err
}
// 4. 通过 shim 创建 Task
task, err := shim.Create(ctx, &task.CreateRequest{
ID: c.id,
Bundle: bundle.Path,
Rootfs: mounts,
Terminal: true,
Stdin: "/dev/null",
Stdout: "/dev/null",
Stderr: "/dev/null",
})
return task, nil
}

五、插件体系#

5.1 containerd 的插件架构#

containerd 采用插件架构,所有核心功能都是插件:

# 查看 containerd 的插件列表
ctr plugins list
# 输出示例:
# TYPE ID PLATFORMS STATUS
# io.containerd.content.v1 content - ok
# io.containerd.snapshotter.v1 overlayfs linux/amd64 ok
# io.containerd.diff.v1 walking linux/amd64 ok
# io.containerd.runtime.v2 task/shim - ok
# io.containerd.service.v1 diff - ok
# io.containerd.service.v1 images - ok
# io.containerd.service.v1 containers - ok
# io.containerd.service.v1 tasks - ok
# io.containerd.grpc.v1 containers - ok
# io.containerd.grpc.v1 content - ok
# io.containerd.grpc.v1 diff - ok
# io.containerd.grpc.v1 images - ok
# io.containerd.grpc.v1 introspection - ok
# io.containerd.grpc.v1 leases - ok
# io.containerd.grpc.v1 namespaces - ok
# io.containerd.grpc.v1 snapshots - ok
# io.containerd.grpc.v1 tasks - ok
# io.containerd.cri.v1 cri - ok

5.2 插件类型#

插件类型示例功能
Snapshotteroverlayfs, btrfs, zfs, native文件系统快照管理
Diffwalking, plugin层差异计算
Runtimetask/shim, io.containerd.runc.v2容器运行时
Serviceimages, containers, tasks, content核心服务
GRPCcontainers, content, diff, imagesgRPC API

5.3 自定义 Snapshotter 示例#

// 自定义 Snapshotter 插件(简化骨架)
package mysnapshotter
import (
"context"
"github.com/containerd/containerd/v2/plugins/snapshots"
)
type mySnapshotter struct {
root string
}
func NewSnapshotter(root string) snapshots.Snapshotter {
return &mySnapshotter{root: root}
}
func (s *mySnapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
// 创建可写快照
return nil, nil
}
func (s *mySnapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
// 提交快照为只读
return nil
}
func (s *mySnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
// 返回挂载点
return nil, nil
}
func (s *mySnapshotter) Remove(ctx context.Context, key string) error {
// 删除快照
return nil
}

六、事件系统#

6.1 事件类型#

containerd 通过事件系统发布容器生命周期事件:

事件触发时机
ImageCreate镜像创建
ImageDelete镜像删除
ContainerCreate容器创建
ContainerDelete容器删除
TaskCreate任务创建
TaskStart任务启动
TaskExit任务退出
TaskOOM任务 OOM
SnapshotPrepare快照准备
SnapshotCommit快照提交
# 订阅 containerd 事件
ctr events
# 输出示例:
# ENVELOPE TIMESTAMP TOPIC EVENT
# 1 2026-08-14T10:00:00Z /containers/create {"id":"mynginx",...}
# 2 2026-08-14T10:00:01Z /tasks/create {"id":"mynginx",...}
# 3 2026-08-14T10:00:01Z /tasks/start {"id":"mynginx",...}

6.2 事件在 Kubernetes 中的应用#

Kubernetes 通过 CRI 接口订阅 containerd 事件,感知容器状态变化:

// CRI 事件处理(简化)
func (c *criService) handleEvent(event *eventtypes.Event) {
switch event.Topic {
case "/tasks/exit":
// 容器退出,更新 Pod 状态
c.handleContainerExit(event)
case "/tasks/oom":
// 容器 OOM,记录事件
c.handleContainerOOM(event)
}
}

七、CRI 插件#

7.1 CRI 接口#

Kubernetes 通过 CRI(Container Runtime Interface)与 containerd 交互:

/etc/containerd/config.toml
# containerd 的 CRI 配置
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/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"
runtime_engine = ""
runtime_root = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1" # gVisor
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
runtime_type = "io.containerd.kata.v2" # Kata Containers

7.2 CRI 与 containerd 的交互#

sequenceDiagram participant Kubelet as Kubelet participant CRI as CRI Plugin participant CTNRD as containerd Core participant Shim as containerd-shim participant Runc as runc Kubelet->>CRI: RunPodSandbox CRI->>CTNRD: Create Container (pause) CTNRD->>Shim: Start shim Shim->>Runc: runc create + start Kubelet->>CRI: CreateContainer CRI->>CTNRD: Create Container (app) CTNRD->>Shim: Start shim Shim->>Runc: runc create + start Kubelet->>CRI: StartContainer CRI->>CTNRD: Start Task CTNRD->>Shim: runc start Kubelet->>CRI: StopContainer CRI->>CTNRD: Kill Task CTNRD->>Shim: runc kill
Warning

containerd 的 gRPC socket(/run/containerd/containerd.sock)默认没有认证机制。任何能访问该 socket 的进程都可以创建、删除容器和拉取镜像。在生产环境中,务必通过文件权限限制 socket 的访问,或启用 TLS 认证。Kubernetes 节点上,只有 kubelet 和 root 用户应该有权访问该 socket。

八、动手实践#

8.1 用 Go 客户端操作 containerd#

package main
import (
"context"
"fmt"
"log"
"os"
"syscall"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/namespaces"
)
func main() {
ctx := namespaces.WithNamespace(context.Background(), "default")
client, err := containerd.New("/run/containerd/containerd.sock")
if err != nil {
log.Fatal(err)
}
defer client.Close()
// 拉取镜像
image, err := client.Pull(ctx, "docker.io/library/redis:alpine",
containerd.WithPullUnpack,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Pulled: %s\n", image.Name())
// 创建容器
container, err := client.NewContainer(ctx, "my-redis",
containerd.WithImage(image),
containerd.WithNewSnapshot("redis-snap", image),
containerd.WithNewSpec(
containerd.WithImageConfig(image),
),
)
if err != nil {
log.Fatal(err)
}
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
// 创建任务
task, err := container.NewTask(ctx, containerd.NewIO(os.Stdin, os.Stdout, os.Stderr))
if err != nil {
log.Fatal(err)
}
defer task.Delete(ctx)
// 启动任务
if err := task.Start(ctx); err != nil {
log.Fatal(err)
}
// 等待退出
statusC, err := task.Wait(ctx)
if err != nil {
log.Fatal(err)
}
// 发送 SIGTERM
task.Kill(ctx, syscall.SIGTERM)
status := <-statusC
fmt.Printf("Redis exited: %v\n", status)
}

8.2 containerd 调试技巧#

# 查看 containerd 日志
journalctl -u containerd -f
# 查看 containerd 的 gRPC 调试端点
curl --unix-socket /run/containerd/containerd.sock http://localhost/v2/
# 查看容器详情
ctr containers inspect mynginx
# 查看任务详情
ctr tasks inspect mynginx
# 查看镜像层
ctr images inspect docker.io/library/nginx:latest | jq .rootfs
# 查看快照信息
ctr snapshots inspect mynginx-snap
# 查看内容信息
ctr content inspect sha256:abc123...

九、本章小结#

上一章理解了runc 源码与容器创建流程。

组件职责关键接口
Content Store存储镜像 blobcontent API
Metadata Store存储元数据BoltDB
Snapshot Service文件系统快照snapshotter API
Runtime Service容器运行时task API
Event Service事件发布订阅events API
CRI PluginKubernetes 集成CRI API
Note

containerd 的插件架构使其非常灵活——你可以替换 Snapshotter(如使用 stargz 延迟拉取)、替换 Runtime(如使用 gVisor/Kata)、添加自定义插件。这种可组合性是 containerd 被广泛采用的关键。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

containerd 架构
https://blog.souloss.com/posts/container-runtime/containerd-architecture/
作者
Souloss
发布于
2026-05-05
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
1
containerd-shim
容器运行时 containerd-shim 是 containerd 与容器进程之间的解耦层——它让 containerd 升级不影响运行中的容器,让容器进程有独立的父进程监督,让 runc 可以在创建容器后退出。全面剖析 shim 的设计动机、进程关系、API 接口、升级安全机制,以及 shim v2 的改进。
2
镜像构建:BuildKit
容器运行时 BuildKit 是 Docker 的新一代镜像构建引擎,支持并行构建、缓存导入导出、多阶段构建优化、secret 挂载等高级特性。从零讲透 BuildKit 的架构设计、Dockerfile 指令的执行原理、多阶段构建、缓存策略、以及如何编写高效的 Dockerfile——从「会写 Dockerfile」到「理解每一条指令的构建机制」。
3
系列导读
容器运行时 本系列从 Linux 内核的 Namespace、Cgroup、OverlayFS 出发,深入 OCI 规范、runc 源码、containerd 架构,再到容器安全、沙箱运行时、网络、存储、镜像构建、Wasm 容器,最后综合实战构建一个迷你容器运行时——从「会用 Docker」到「理解容器运行时的每一行代码」,每章配有可运行的代码示例与架构图,让你从容器用户进阶到容器运行时工程师。
4
Wasm 容器:WasmEdge/WASI
容器运行时 WebAssembly(Wasm)正在从浏览器走向服务器——WASI(WebAssembly System Interface)定义了 Wasm 访问操作系统的标准接口,WasmEdge/runwasi 等运行时让 Wasm 模块可以作为容器运行。详细解读 Wasm 容器的架构、WASI 接口、与 OCI 的集成、与 Linux 容器的对比——从「Wasm 只能在浏览器运行」到「Wasm 是下一代容器运行时」。
5
OCI 规范详解
容器运行时 OCI(Open Container Initiative)定义了容器镜像格式和运行时接口的开放标准。从零讲透 OCI 的三大规范——Image Spec(镜像格式、manifest、config、layer)、Runtime Spec(OCI Bundle、config.json、生命周期)、Distribution Spec(Registry API),理解容器生态标准化的基石,为阅读 runc 源码和理解 containerd 架构奠定基础。