containerd 2.1.8 超深度分析 — Runtime + Task + Shim + ttrpc + Client SDK

源码:core/runtime/ (554行) + internal/shim/ (1566行) + cmd/containerd-shim-runc-v2/ (~8000行) + client/ (6744行) + pkg/shim/ (1566行)


一、Runtime 抽象层

1.1 PlatformRuntime 接口

type PlatformRuntime interface {
    ID() string
    Create(ctx context.Context, taskID string, opts CreateOpts) (Task, error)
    Get(ctx context.Context, taskID string) (Task, error)
    Tasks(ctx context.Context, all bool) ([]Task, error)
    Delete(ctx context.Context, taskID string) (*Exit, error)
}

1.2 Task 接口

type Task interface {
    ID() string
    Namespace() string
    PID() uint32
    Start(ctx context.Context) error
    Delete(ctx context.Context) (*Exit, error)
    Kill(ctx context.Context, signal syscall.Signal, opts ...KillOpt) error
    Wait(ctx context.Context) (*Exit, error)
    Exec(ctx context.Context, id string, opts ExecOpts) (Process, error)
    Pids(ctx context.Context) ([]ProcessInfo, error)
    Resize(ctx context.Context, w, h uint32) error
    IO() *IO
    Status(ctx context.Context) (Status, error)
    Checkpoint(ctx context.Context, opts ...CheckpointOpt) (Descriptor, error)
    Update(ctx context.Context, opts ...UpdateTaskOpts) error
}

Task 状态

Create()

Start()

Delete()

Pause()

exit / Kill(SIGKILL)

Resume()

Delete()

Delete()

Created

Running

Stopped

Paused


二、Shim 架构 — containerd-shim-runc-v2

2.1 为什么需要 Shim?

问题 Shim 解决方案
containerd 崩溃 → 容器退出 Shim 独立于 containerd,containerd 重启不影响
信号转发 Shim 接收信号 → 转发给容器 init 进程
容器退出状态收集 Shim wait() 收集退出码 → 通知 containerd
reexec 安全 Shim 是独立进程,不共享文件描述符表

2.2 Shim v2 架构

容器进程

containerd-shim-runc-v2 进程

containerd daemon

gRPC

ttrpc (Unix socket)

Tasks Service (gRPC)

Shim Manager

Task Service (ttrpc)

Container Manager

runc create/run (container-1)

runc create/run (container-2)

runc exec (process-3)

init process (container-1)

init process (container-2)

exec process (process-3)

v2 核心变化:一个 shim 进程管理多个容器(而非 v1 的一容器一 shim)。

2.3 Shim 生命周期

容器 init 进程 runc containerd-shim containerd 容器 init 进程 runc containerd-shim containerd 创建 Shim 初始化 ttrpc server + socket 创建容器 启动容器 等待退出 清理 退出 exec containerd-shim-runc-v2 start 返回 socket 地址 ttrpc Create(containerID, bundle, rootfs, runtime) runc create --bundle <bundle> PID CreateResponse{PID} ttrpc Start(containerID) runc start <id> execve(用户进程) StartResponse{PID} ttrPC Wait(containerID) exit(status) WaitResponse{ExitStatus} ttrpc Delete(containerID) runc delete <id> DeleteResponse{ExitStatus} ttrpc Shutdown()

2.4 Shim Manager — 进程管理

type Manager struct {
    runtime    string              // runtime 名称 (io.containerd.runc.v2)
    address    string              // shim socket 地址
    containers map[string]*shim    // 已管理的 shim 实例
    tasks      *nsmap.Namespaced[shimTask]  // 按 namespace 管理的 task
}

Shim 启动策略

策略 条件 行为
复用现有 Shim 同 namespace + 同 runtime 连接已有 shim socket
新建 Shim 不同 runtime 或首次 exec shim 二进制
Sandbox Shim Pod 沙箱模式 一个 shim 管理 Pod 所有容器

三、ttrpc — 轻量级 RPC 协议

3.1 为什么用 ttrpc 而非 gRPC?

特性 gRPC ttrpc
协议 HTTP/2 + protobuf 自定义帧 + protobuf
依赖 grpc-go (大量依赖) 极小依赖
Unix socket 性能 HTTP/2 开销大 帧协议高效
适用场景 外部客户端 shim 间通信

设计原因:shim 是短小精悍的进程,不能依赖 gRPC 的庞大运行时。

3.2 ttrpc 帧格式

+----------+--------+---------+
| Length   | StreamID| Flags  |  Header (9 bytes)
+----------+--------+---------+
| Payload (protobuf)          |  Body (Length bytes)
+----------------------------+

四、Client SDK — Go 客户端

4.1 Client 结构体

type Client struct {
    conn          *grpc.ClientConn       // gRPC 连接
    defaultns     string                 // 默认 namespace
    runtime       *runtimeOpts           // 运行时选项
    services      *serviceDescriptors    // 服务代理集合

    // 服务代理
    containers    containersapi.ContainersClient
    content       types.ContentClient
    diff          diffapi.DiffClient
    events        types.EventClient
    images        imagesapi.ImagesClient
    leases        leasesapi.LeasesClient
    namespaces    namespacesapi.NamespacesClient
    sandboxs      sandboxsapi.ControllerClient
    snapshots     snapshotsapi.SnapshotsClient
    tasks         tasksapi.TasksClient
    transfer      transferapi.TransferClient
    version       versionservice.VersionClient
}

4.2 Client.New — 连接建立

func New(address string, opts ...Opt) (*Client, error) {
    var copts clientOpts
    for _, o := range opts {
        o(&copts)
    }

    c := &Client{defaultns: copts.defaultns}

    // 建立 gRPC 连接
    conn, err := grpc.NewClient(
        dialer.DialAddress(address),
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithContextDialer(dialer.ContextDialer),
    )
    c.conn = conn

    // 初始化所有服务代理
    c.containers = containersapi.NewContainersClient(conn)
    c.content = types.NewContentClient(conn)
    c.diff = diffapi.NewDiffClient(conn)
    c.images = imagesapi.NewImagesClient(conn)
    c.tasks = tasksapi.NewTasksClient(conn)
    // ...

    return c, nil
}

4.3 Container 操作

// 创建容器
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
    // 1. 构建 container spec
    // 2. gRPC Create call
    // 3. 返回 Container 对象
}

type Container interface {
    ID() string
    Info(ctx context.Context, ...InfoOpts) (containers.Container, error)
    Delete(ctx context.Context, ...DeleteOpts) error
    NewTask(ctx context.Context, ioCreate cio.Creator, ...NewTaskOpts) (Task, error)
    Spec(ctx context.Context) (*specs.Spec, error)
    Image(ctx context.Context) (Image, error)
    Labels(ctx context.Context) (map[string]string, error)
    SetLabels(ctx context.Context, labels map[string]string) (map[string]string, error)
    Extensions(ctx context.Context) (map[string]interface{}, error)
    Update(ctx context.Context, ...UpdateContainerOpts) error
}

4.4 Task 操作

// 创建任务
func (c *Container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (Task, error) {
    // 1. 构建 Create request
    // 2. gRPC Tasks.Create
    // 3. 返回 Task 对象
}

// Task 接口 (client 侧)
type Task interface {
    ID() string
    PID() uint32
    Start(ctx context.Context) error
    Delete(ctx context.Context, ...DeleteOpts) (*ExitStatus, error)
    Kill(ctx context.Context, signal syscall.Signal, ...KillOpts) error
    Wait(ctx context.Context) (<-chan ExitStatus, error)
    Exec(ctx context.Context, id string, opts ...ExecOpts) (Process, error)
    Resize(ctx context.Context, w, h uint32) error
    IO() *cio.IO
    Status(ctx context.Context) (Status, error)
}

4.5 Pull 镜像

func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) {
    // 1. 解析 ref
    // 2. 创建 resolver (docker.Resolver)
    // 3. 调用 Transfer 服务
    //    transfer.Pull(ctx, ref, resolver, unpacker)
    // 4. 返回 Image 对象
}

4.6 Client → gRPC → Plugin 调用链

Plugins

gRPC Services

Client SDK

gRPC

gRPC

gRPC

gRPC

client.NewContainer()

container.NewTask()

client.Pull()

task.Kill()

Containers Service

Tasks Service

Transfer Service

Metadata Plugin

Runtime V2 Plugin

Transfer Plugin


五、Sandbox — Pod 级沙箱

5.1 Sandbox 接口

type Sandbox interface {
    ID() string
    Metadata() SandboxMetadata
    Labels() map[string]string
    Create(ctx context.Context, ...SandboxCreateOpt) error
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    Wait(ctx context.Context) (<-chan ExitStatus, error)
    Shutdown(ctx context.Context) error
    Status(ctx context.Context) (SandboxStatus, error)
}

5.2 Sandbox Controller 接口

type Controller interface {
    Create(ctx context.Context, sandboxID string, opts ...CreateOpt) (Sandbox, error)
    Start(ctx context.Context, sandboxID string) (Sandbox, error)
    Stop(ctx context.Context, sandboxID string) (Sandbox, error)
    Wait(ctx context.Context, sandboxID string) (<-chan ExitStatus, error)
    Shutdown(ctx context.Context, sandboxID string) error
}

两种 Sandbox 模式

模式 Controller 描述
PodSandbox podsandbox pause 容器作为沙箱 (CRI 兼容)
Custom 第三方实现 自定义沙箱控制器 (例如 Firecracker)

六、CRI Plugin — Kubernetes CRI 兼容

6.1 CRI 服务接口

type CRIService interface {
    RuntimeServiceServer   // CRI RuntimeService
    ImageServiceServer     // CRI ImageService
}

CRI RuntimeService 核心方法

方法 对应 containerd 操作
RunPodSandbox Sandbox.Create + Sandbox.Start
StopPodSandbox Sandbox.Stop
RemovePodSandbox Sandbox.Shutdown + Container.Delete
CreateContainer Container.NewContainer
StartContainer Task.Start
StopContainer Task.Kill + Task.Wait
RemoveContainer Task.Delete + Container.Delete
ExecSync Task.Exec + Wait
Exec Task.Exec (streaming)

CRI ImageService 核心方法

方法 对应 containerd 操作
PullImage Transfer.Pull
ListImages ImageStore.List
ImageStatus ImageStore.Get
RemoveImage Image.Delete

七、Events — 异步事件总线

7.1 事件类型

事件 Topic 触发时机
ContainerCreate /containers/create 容器创建
ContainerUpdate /containers/update 容器更新
ContainerDelete /containers/delete 容器删除
TaskCreate /tasks/{id}/create 任务创建
TaskStart /tasks/{id}/start 任务启动
TaskExit /tasks/{id}/exit 任务退出
TaskDelete /tasks/{id}/delete 任务删除
ImageCreate /images/create 镜像创建
ImageUpdate /images/update 镜像更新
ImageDelete /images/delete 镜像删除
SnapshotPrepare /snapshots/prepare 快照准备
SnapshotCommit /snapshots/commit 快照提交

事件发布-订阅

订阅者

Event Bus

发布者

Container Create

Task Exit

Image Pull

Publish()

Subscribe(filter)

CRI Plugin

Restart Monitor

User Client


八、设计模式总结

# 模式 体现
1 Shim 隔离 独立进程,containerd 重启不影响
2 ttrpc 轻量协议 替代 gRPC 用于 shim 通信
3 多容器一 Shim v2 架构,减少进程数
4 Proxy 模式 Client → gRPC → Plugin → Shim
5 Sandbox 抽象 Pod 级管理,支持自定义控制器
6 CRI 适配 CRI Service → containerd API 转换
7 事件总线 Publish/Subscribe 解耦
8 IO Creator cio.NewCreator/NewPipe 策略
9 Wait Channel Task.Wait → <-chan ExitStatus
10 Namespace 隔离 gRPC metadata → context namespace
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐