Containerd 深度解析与企业级落地

387 阅读17分钟

目录

  1. 引言
  2. 背景与发展
    1. Containerd 的前世今生
    2. 与 Docker、Kubernetes 的关系
  3. 架构设计
    1. 整体架构概览
    2. 插件化设计与模块划分
    3. 关键插件与组件
  4. 关键模块深度解析
    1. Content Plugin:镜像内容管理
    2. Snapshot Plugin:文件系统快照与存储
    3. Metadata Plugin:元数据存储与检索
    4. Runtime Plugin:容器运行时
    5. CRI Plugin:与 Kubernetes 的对接
  5. 企业级落地实战
    1. 系统环境与前置依赖
    2. 安装方式对比与实践
    3. 配置与调优
    4. 与 Kubernetes 集成:CRI-Overshadow
    5. 日志与监控
  6. 性能分析与优化
    1. Benchmarks 对比(Bucketbench、Microbench)
    2. 调优思路与最佳实践
  7. 常见问题与故障排查
    1. 服务无法启动
    2. 容器无法拉取镜像
    3. 容器启动失败
    4. 性能瓶颈与高内存占用
  8. 总结与展望
  9. 参考链接

引言

🚀 随着云原生时代的到来,容器化技术已成为企业级应用部署与解耦的重要利器。在众多容器技术栈中,Containerd 作为一个轻量、稳定且高性能的容器运行时,正被 Kubernetes、云平台以及各大互联网公司所青睐。
📦 本文将从容器运行时的演进与需求出发,深度剖析 Containerd 的核心架构与关键模块,分享在企业级场景下的实战部署经验,并结合实际性能对比与调优思路,帮助中高级技术人员快速掌握 Containerd 在生产环境中的落地要点。

目标读者

  • Kubernetes 用户、容器平台搭建者
  • 企业 DevOps 工程师、架构师、云平台研发人员
  • 关注云原生与容器运行时机制的中高级技术研究员
  • 已具备一定工程实践背景的技术使用者(非小白)

背景与发展

Containerd 的前世今生

“在 Docker Engine 中,Containerd 曾经是 Docker 1.11 及之前版本的容器核心;如今,它已经独立成为 CNCF(Cloud Native Computing Foundation)下的顶级开源项目,为上层编排系统提供了更专注、更稳定的容器运行时基础设施。”

  1. 起源与演进

    • 2013 年:Docker 公司推出 Docker 引擎,其中包含了名为 libcontainer 的容器管理核心。
    • 2015 年libcontainer 捐献给 Open Container Initiative (OCI),并更名为 runc,作为标准化的 OCI 运行时。
    • 2016 年 3 月:Docker 1.11 发布,将 containerd 从 Docker Engine 中剥离,并将其作为独立守护进程(daemon),提供 gRPC 接口供上层系统调用。
    • 2017 年:Docker 官方将 Containerd 正式捐赠给 CNCF,成为独立的顶级项目,专注于容器生命周期管理、镜像拉取与存储、网络与存储插件等功能。
    • 2020 年:Kubernetes 1.20 宣布逐步移除对 Docker Shim 的支持,而推荐使用 containerd/runc 组合作为容器运行时。
  2. 社区与生态

    • CNCF 顶级项目:与 Kubernetes、Prometheus、Envoy 并列,拥有庞大的社区支持与活跃的版本迭代。
    • 厂商支持:Google、RedHat、IBM、阿里、华为等多家企业参与开发与贡献,保证了 Containerd 在云原生领域的主流地位。
    • 生态兼容:支持所有符合 OCI 标准的镜像与运行时,上层系统(如 Kubernetes、Mesos、Swarm 等)通过 CRI(Container Runtime Interface)或 gRPC 接口与 containerd 对接。

与 Docker、Kubernetes 的关系

  • Docker vs Containerd

    • Docker Engine 是一个完整的 PaaS 平台,集成了镜像构建、仓库管理、镜像分发、容器编排(Swarm)等功能,而 Containerd 仅聚焦于“容器运行时管理”。
    • 将 Containerd 从 Docker Engine 中独立出来,既保证了 Docker 引擎的上层用户体验,也让 Containerd 有了更专注的发展空间。
  • Kubernetes vs Containerd

    • 早期 Kubernetes 通过 “Docker Shim” 将 CRI 请求翻译为 Docker 的 API,进而借助 Docker Daemon 来管理容器。随着社区标准化需求日益增长,Docker Shim 的维护成本加大。
    • Containerd 原生支持 CRI,通过 cri-containerd 插件直接与 Kubelet 通信,实现镜像拉取、容器创建、日志收集等功能。2020 年后,Kubernetes 官方推荐将 Containerd 与 runc 作为主要运行时。

🎯 小结:Containerd 从 Docker Engine 中脱胎而出,继承了 Docker 对容器生命周期管理的设计思想,同时将重心放在“轻量化、稳定性、高性能”上,为云原生生态提供了理想的底层运行引擎。


架构设计

整体架构概览

Containerd 采用 C/S 架构,上层系统(如 Kubernetes、Docker Engine)通过 gRPC 接口调用 containerd 以实现对容器的管理。

  • Server(守护进程):以 containerd 进程形式运行,负责接收上层请求、管理镜像、存储、网络、容器生命周期等。
  • Client(CLI / SDK):通过 ctrcrictl、Docker Engine 等客户端进行交互,调用 gRPC 接口执行操作。

flowchart LR
  subgraph "上层系统"
    A[Kubernetes Kubelet] 
    B[Docker Engine]
    C[Mesos/Swarm 等]
  end

  subgraph "Containerd 服务端"
    direction TB
    D[containerd Daemon] 
    D --> E[Content Plugin]
    D --> F[Snapshot Plugin]
    D --> G[Metadata Plugin]
    D --> H[Runtime Plugin]
    D --> I[CRI Plugin]
    D --> J[Metrics Plugin]
    D --> K[GC Plugin]
  end

  A -->|gRPC CRI | D
  B -->|gRPC API | D
  C -->|gRPC API | D
  
  • 🍃 Containerd 将不同职责划分为多个模块(插件),各插件之间通过核心框架协调运作。
  • 🌟 插件以 “插件名称 + 版本号” 的形式注册到 Containerd 中,启动时自动加载并初始化。

插件化设计与模块划分

Containerd 主要分为以下三大子系统(层):

  1. Storage(存储层)

    • Content Plugin:管理镜像层二进制内容(如 tarball、OCI Layer)。
    • Snapshot Plugin:对镜像层进行解压、挂载、卸载操作,将镜像与容器文件系统进行绑定。
  2. Metadata(元数据层)

    • Metadata Plugin:基于 BoltDB 存储容器、镜像、快照、租约(Lease)、命名空间(Namespace)等元数据信息。
    • Garbage Collection (GC) Plugin:定期清理无用的快照、内容、租约,维护存储空间。
  3. Runtime(运行时层)

    • Runtime Plugin(v1/v2):负责调用 runc 或其他符合 OCI 规范的 runtime(如 kata-runtimegvisor)创建、启动、停止容器进程。
    • Service Plugin(tasks-service、containers-service、events-service 等):暴露 gRPC 服务供上层调用,实现容器创建、启动、监控等功能。
    • CRI Plugin:实现 Kubernetes CRI(Container Runtime Interface)规范,桥接 Kubelet 与 Containerd。
    • Metrics Plugin:暴露 Prometheus 格式的监控指标,用于上层集群监控系统。
flowchart TB
  subgraph Storage 存储层
    CP[Content Plugin]
    SP[Snapshot Plugin]
  end

  subgraph Metadata 元数据层
    MP[Metadata Plugin]
    GC[GC Plugin]
  end

  subgraph Runtime 运行时层
    RP_v1[Runtime Plugin v1]
    RP_v2[Runtime Plugin v2]
    CRI[CRI Plugin]
    M[Metrics Plugin]
    SS[Tasks-Service]
    CS[Containers-Service]
    ES[Events-Service]
  end

  CP --> MP
  SP --> MP
  MP --> GC
  SS --> RP_v2
  CS --> RP_v2
  CRI --> SS
  CRI --> CS
  RP_v2 --> SP
  RP_v2 --> CP

关键插件与组件

插件名称作用
Content Plugin存储与管理镜像层的二进制内容,如 OCI Layer,支持拉取、导入、导出。
Snapshot Plugin对镜像层进行解压、挂载、复制快照。支持多种存储后端(overlayfs、btrfs、zfs)。
Metadata Plugin使用 BoltDB 存储容器、镜像、快照等元数据信息。
Garbage Collection (GC) Plugin定期清理不再使用的镜像层、快照、租约,释放磁盘空间。
Runtime Plugin (v1/v2)负责调用 runc(或其他 OCI Runtime)创建、启动、停止容器。
CRI Plugin实现 Kubernetes CRI 接口,将 Kubelet 请求翻译为 Containerd 操作。
Tasks-Service、Containers-Service、Events-Service提供容器与任务的管理、查询、事件上报等服务。
Metrics Plugin暴露 Prometheus 风格的监控指标,如镜像拉取速率、容器启动耗时等。
Diff-Service处理增量文件系统差异(walking)。

关键模块深度解析

Content Plugin:镜像内容管理

  1. 功能定位

    • 管理镜像中所有不可变的二进制内容,如镜像层(layer)、配置(config)、清单(manifest)等。
    • 支持多种操作:Pull(拉取)、Push(推送)、Import(导入)、Export(导出)等。
  2. 核心概念:Content Store

    • Containerd 维护一个 Content Store,负责存储镜像各个层的数据。每份内容通过 Digest(sha256) 进行唯一标识,保证内容可寻址与一致性。
    • 物理存储路径默认在 /var/lib/containerd/io.containerd.content.v1.content 下,以子目录分片存放。
  3. 工作流程

    • ctr images pull:首先获取镜像清单(manifest),遍历所需的 layer Digest,依次下载到本地 Content Store;下载完毕后校验完整性,将 Digest 及尺寸信息写入元数据(Metadata Store)。
    • ctr images push:根据本地 Content Store 中已有的层,打包上传到远端 Registry。
    • 导入/导出:利用 ctr images import/export 命令,将镜像保存为 tar 归档文件,支持跨集群迁移。
  4. 示例:拉取镜像

    # 拉取 nginx:alpine(支持多平台)
    sudo ctr images pull --all-platforms docker.io/library/nginx:alpine
    
    # 指定平台拉取(仅 linux/amd64)
    sudo ctr images pull --platform linux/amd64 docker.io/library/nginx:alpine
    
    # 查看 Content Store 路径
    ls /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256
    
  5. 内容校验与租约(Lease)

    • Containerd 引入 租约(Lease) 概念,用于在特定时间段内保护内容不被 GC 删除,例如在备份或导出过程中。
    • 通过 ctr lease 命令管理租约,保证内容在使用期间不会被回收。

Snapshot Plugin:文件系统快照与存储

  1. Snapshotter 概述

    • Snapshotter 负责将镜像层(layer)解压到本地文件系统,并在容器启动时将各层合并为一个联合文件系统,实现对容器 RootFS 的管理。

    • 支持多种后端 Snapshotter

      • overlayfs(默认,最常见,适用于大多数 Linux 发行版)
      • btrfs(支持写时复制,需要底层文件系统支持 btrfs)
      • zfs(写时复制,需要底层文件系统支持 zfs)
      • devmapperthindaufs 等(根据平台与场景选择)
  2. Union Mount(联合挂载)

    • 采用 联合挂载 技术,将多个只读层(immutable layer)和一个读写层(rw layer)合并到一起,为容器提供一个完整的根文件系统。
    • 读写分离:上层读写层位于 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs(以 overlayfs 为例),只记录容器运行时的变更。
  3. Snapshot 生命期管理

    • Image Snapshot:当拉取镜像后,会将各个 layer 解压为只读快照(readonly snapshot),这些快照共享于多个容器。
    • Container Snapshot:在基于镜像创建容器时,会为该容器创建一个新的读写快照(rw snapshot),并将只读层以联合挂载形式置于上层。
    • 删除与 GC:容器停止并删除后,对应的读写层快照被删除,关联的只有当没有任何容器依赖时,只读层才会被 GC 清理。
  4. 示例:overlayfs Snapshot 操作

    # 列出所有 snapshot
    sudo ctr snapshots ls
    # 输出类似:
    # NAME                              KIND                PARENT
    # sha256:aeae...                    active              sha256:bbbb...
    # sha256:bbbe...                    view                sha256:cccc...
    # ...
    
    # 手动创建一个镜像快照(view)
    sudo ctr snapshots view sha256:aeae... /mnt/nginx-layer
    
    # 删除快照
    sudo ctr snapshots rm sha256:aeae...
    
  5. 存储优化与调优

    • 选择合适的 Snapshotter:大多数场景推荐使用 overlayfs,兼容性强、性能佳;对写时复制要求高、希望隔离的场景可考虑 btrfszfs
    • 底层文件系统参数调优:如 overlayfs 中可以通过 mount 选项设置 metacopy=onxor 等参数以提升性能。
    • 多级缓存:对于高并发场景,可结合分布式存储或缓存代理减少单机 I/O 压力。

Metadata Plugin:元数据存储与检索

  1. Metadata Store 概述

    • 使用 BoltDB(嵌入式 KV 数据库)存储所有元数据信息,包括:

      • 镜像清单(Manifest)与层(Layer)信息
      • 容器对象(Container)与任务(Task)信息
      • Snapshot(快照)与 Lease(租约)
      • Namespace(命名空间)概念
  2. Namespace 用法

    • Containerd 支持多命名空间(Namespace)隔离,不同 Namespace 之间的镜像、容器、快照等资源相互独立。

    • 通过 --namespace 参数或环境变量 $CONTAINERD_NAMESPACE 指定操作数据所归属的命名空间。

    • 示例:

      # 切换命名空间为 dev
      export CONTAINERD_NAMESPACE=dev
      # 拉取镜像到 dev 命名空间
      sudo ctr images pull docker.io/library/nginx:latest
      # 在 default 命名空间下看不到该镜像
      sudo ctr -n default images ls
      
  3. Metadata Schema 演进

    • Containerd v1.x 使用 BoltDB v1 版本,存储在 /var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db
    • v1.5+ 开始支持分离 Metadata 存储路径,配置文件中可通过 plugins."io.containerd.metadata.v1.bolt".directory = "/data/containerd/metadata" 自定义。
  4. 事务与并发

    • Containerd 的 Metadata 插件实现了读写分离与 MVCC(多版本并发控制),保证高并发场景下操作的可见性与一致性。
    • 所有元数据操作(如创建镜像记录、写入快照信息)均在事务(Transaction)中执行,保证回滚与原子性。

Runtime Plugin:容器运行时

  1. Runtime Plugin v2(io.containerd.runtime.v2)

    • Containerd v1.1+ 引入 Runtime v2 插件,采用新的任务(Task)模型,直接调用 runc 的 JSON Spec,性能与稳定性更优。
    • 核心二进制:containerd-shim-runc-v2,作为 shim 进程负责将容器进程置于独立命名空间内。
  2. Runtime Plugin v1(io.containerd.runtime.v1)

    • 兼容早期版本的 Runtime API,使用 containerd-shimrunc 结合,适用于老系统。
    • v1 与 v2 在命令行与 gRPC 接口上有部分差异,建议新项目优先选用 v2。
  3. Shim 机制

    • Shim 进程用于隔离 Containerd 守护进程与容器进程,保证容器进程不会因为 Containerd 重启而被回收。

    • 当执行 ctr runctr task start 时:

      • Containerd Daemon 通过 gRPC 调用 Runtime Plugin,创建 shim 进程(如 containerd-shim-runc-v2)。
      • Shim 进程再调用 runc 启动容器进程。
      • 容器进程的 StdIO(stdin/stdout/stderr)通过 FIFO 与 Client 进行通信,由 shim 进行转发。
  4. Runtime Config(OCI Spec)

    • Containerd 支持自定义 OCI 运行时配置,如 CPU、内存、挂载、Seccomp、AppArmor 等。

    • 可以通过 --config 参数指定自定义 JSON 文件:

      # 示例:自定义运行时 Spec
      cat <<EOF > myruntime.json
      {
        "ociVersion": "1.0.2",
        "process": {
          "terminal": true,
          "user": {
            "uid": 1000,
            "gid": 1000
          },
          "args": ["sh"],
          "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"],
          "cwd": "/"
        },
        "root": {
          "path": "rootfs"
        },
        "hostname": "containerd-test",
        "mounts": [
          {
            "destination": "/proc",
            "type": "proc",
            "source": "proc"
          },
          {
            "destination": "/dev",
            "type": "tmpfs",
            "source": "tmpfs",
            "options": ["nosuid", "strictatime", "mode=755", "size=65536k"]
          }
        ]
      }
      EOF
      
      # 运行容器时指定
      sudo ctr run --runtime io.containerd.runc.v2 --config myruntime.json myimage:latest mycontainer
      
  5. 多 Runtime 支持

    • Containerd 支持为不同镜像或容器指定不同的 Runtime,例如 runckata-runtimegvisor 等。

    • 在配置文件(/etc/containerd/config.toml)中,定义多个 runtime:

      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
        runtime_type = "io.containerd.kata.v2"
        runtime_engine = "/usr/bin/kata-runtime"
        runtime_root = "/run/kata"
      

CRI Plugin:与 Kubernetes 的对接

  1. Container Runtime Interface(CRI)概览

    • CRI 是 Kubernetes 提供的一套标准接口,用于与容器运行时(Container Runtime)通信。
    • 通过 CRI,Kubelet 可以执行镜像拉取、容器创建、启动、停止、删除、日志获取等操作。
  2. cri-containerd 插件

    • Containerd 官方提供了 cri 插件(plugins."io.containerd.grpc.v1.cri"),完全实现了 CRI v1.0 规范。
    • config.toml 中启用 cri 插件后,Containerd 会启动一个 CRI gRPC 服务,监听 unix:///run/containerd/containerd.sock,供 Kubelet 调用。
  3. 关键配置示例

    [plugins."io.containerd.grpc.v1.cri"]
      sandbox_image = "k8s.gcr.io/pause:3.6"
      systemd_cgroup = false
      stream_idle_timeout = "4h"
      max_container_log_line_size = 16384
    
      [plugins."io.containerd.grpc.v1.cri".containerd]
        snapshotter = "overlayfs"
        default_runtime_name = "runc"
    
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          runtime_type = "io.containerd.runc.v2"
          runtime_engine = ""
          runtime_root = ""
          privileged_without_host_devices = false
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            SystemdCgroup = true
    
      [plugins."io.containerd.grpc.v1.cri".cni]
        bin_dir = "/opt/cni/bin"
        conf_dir = "/etc/cni/net.d"
    
      [plugins."io.containerd.grpc.v1.cri".registry]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://registry.aliyuncs.com"]
    
  4. CRI 对接流程

    1. Kubelet 启动时,通过 --container-runtime=remote--container-runtime-endpoint=unix:///run/containerd/containerd.sock 指定 CRI Socket。
    2. Kubelet 调用 CRI.PullImage,Containerd 从指定 Registry 拉取镜像,并存储到 Content Store。
    3. Kubelet 调用 CRI.CreateContainer,Containerd 根据 Pod Sandbox(Pause 容器)创建 Namespace、网络、挂载,并在此基础上创建应用容器。
    4. Kubelet 调用 CRI.StartContainer,Containerd 通过 Runtime 插件启动 shim 与 runc,正式运行容器进程。
    5. 容器运行期间,Kubelet 可通过 CRI.UpdateContainerResourcesCRI.KillContainer 等接口对容器进行动态管理。

🚀 小结:通过 cri-containerd 插件,Containerd 完美对接 Kubernetes,省去了 Docker Shim 的额外开销,提高了性能与稳定性。


企业级落地实战

系统环境与前置依赖

  • 操作系统:CentOS 7.x、RHEL 7.x、Ubuntu 18.04+、Debian 10+ 均可支持。

  • 内核版本:建议 ≥ 4.14,以获得更好性能与兼容性,尤其是 OverlayFS、Seccomp、Cgroupv2 等。

  • 硬件资源:每台节点 ≥ 2 核 CPU、4GB 内存、100GB 磁盘空间(推荐 SSD)。

  • 网络环境:需要能够访问外部 Docker Registry(如 docker.iogcr.io 等)或本地私有镜像仓库(Harbor 等)。

  • 依赖工具

    • iptablesiproute2
    • runc(对于二进制方式安装需要额外安装)
    • CNI 插件包(如 Flannel、Calico、Weave 等)
    • crictl(可选,用于 CLI 方式管理 CRI 镜像与容器)

安装方式对比与实践

企业生产环境常见两种安装方式:YUM(APT)包管理安装二进制包离线安装

  • YUM/DEB 安装:优点是依赖自动解决、升级方便。缺点是版本可能滞后、对网络要求较高。
  • 二进制离线安装:优点是版本可控、适用于无网络或镜像需要严格一致的场景;缺点是需要手动管理依赖与配置。

YUM 安装(适用于 CentOS/RHEL)

  1. 添加 Docker 官方仓库或第三方镜像源

    # 添加阿里云 Docker CE 仓库(示例)
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  2. 查看可用 containerd 版本

    sudo yum list containerd.io --showduplicates
    
  3. 安装 containerd.io

    sudo yum install -y containerd.io
    
  4. 启动并设置开机自启动

    sudo systemctl enable containerd
    sudo systemctl start containerd
    
  5. 验证安装

    # 查看版本
    sudo ctr version
    
    # 示例输出
    # Client:
    #   Version:  1.6.0
    #   Revision: 39259a8f35919a0d02c9ecc2871ddd6ccf6a7c6e
    # Server:
    #   Version:  1.6.0
    #   Revision: 39259a8f35919a0d02c9ecc2871ddd6ccf6a7c6e
    

Tip:YUM 安装方式会自动拉取并安装 runc、containerd、ctr、crictl 等相关二进制,对大多数常见场景足够。

二进制包安装(适用于离线环境与 Kubernetes 集群)

  1. 下载 containerd 二进制包

    • 访问 Containerd Releases,下载对应平台的 cri-containerd-cni-<version>-linux-amd64.tar.gz 包,该包已包含 runc 与 CNI 插件。
    • 也可仅下载 containerd-<version>-linux-amd64.tar.gz,此包需额外手动下载并安装 runc
  2. 解压并复制二进制文件

    # 假设包名为 cri-containerd-cni-1.6.0-linux-amd64.tar.gz
    tar zxvf cri-containerd-cni-1.6.0-linux-amd64.tar.gz -C /tmp
    
    # 复制 containerd 二进制文件
    sudo cp /tmp/usr/local/bin/containerd /usr/local/bin/
    sudo cp /tmp/usr/local/bin/ctr /usr/bin/
    sudo cp /tmp/usr/local/bin/containerd-shim* /usr/bin/
    sudo cp /tmp/usr/local/bin/runc /usr/sbin/    # 如果包中自带 runc,则复制到 /usr/sbin
    sudo chmod +x /usr/sbin/runc
    
  3. 部署 systemd 单元文件

    # 创建目录
    sudo mkdir -p /etc/systemd/system
    
    # 将容器包中的 systemd 服务文件复制过来
    sudo cp /tmp/etc/systemd/system/containerd.service /etc/systemd/system/
    
    # 重新加载 systemd 配置
    sudo systemctl daemon-reload
    
  4. 初始化默认配置文件

    # 创建配置目录
    sudo mkdir -p /etc/containerd
    
    # 生成默认配置信息到 /etc/containerd/config.toml
    sudo containerd config default > /etc/containerd/config.toml
    
  5. 替换或优化配置

    • 生产环境下,建议在默认配置基础上,调整镜像加速、CNI 插件路径、Snapshot 驱动、Runtime 配置等。
    sudo vim /etc/containerd/config.toml
    # 重点修改:snapshotter、runtime、registry mirrors、cni 配置等
    
  6. 启动 containerd

    sudo systemctl enable containerd
    sudo systemctl start containerd
    
    # 验证
    sudo systemctl status containerd
    sudo ctr version
    

Tip:若仅需单机测试,可下载 containerd-<version>-linux-amd64.tar.gz,手动安装 runc;若打算部署在 Kubernetes 集群,推荐使用 cri-containerd-cni 包,一次性获取所有组件。

配置与调优

在企业级场景下,除了基础安装外,我们要关注以下几个方面的优化与定制:

基础配置文件解读

root = "/var/lib/containerd"
state = "/run/containerd"
oom_score = -999

[grpc]
  address = "/run/containerd/containerd.sock"
  uid = 0
  gid = 0

[debug]
  level = "info"

[metrics]
  address = "127.0.0.1:1338"
  grpc_histogram = true

[cgroup]
  path = ""

[plugins]
  [plugins."io.containerd.grpc.v1.cri"]
    sandbox_image = "k8s.gcr.io/pause:3.6"
    systemd_cgroup = true
    stream_server_address = "127.0.0.1"
    stream_server_port = "0"
    max_container_log_line_size = 16384

    [plugins."io.containerd.grpc.v1.cri".containerd]
      snapshotter = "overlayfs"
      default_runtime_name = "runc"

      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
        runtime_type = "io.containerd.runc.v2"
        # 可选择配置 runtime_engine、runtime_root、privileged_without_host_devices 等

    [plugins."io.containerd.grpc.v1.cri".cni]
      bin_dir = "/opt/cni/bin"
      conf_dir = "/etc/cni/net.d"
      conf_template = ""

    [plugins."io.containerd.grpc.v1.cri".registry]
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
        endpoint = ["https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com"]
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
        endpoint = ["https://gcr.mirrors.ustc.edu.cn/google-containers/"]
      # 若使用企业内私有仓库(如 Harbor),直接添加:
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.mycompany.com"]
        endpoint = ["https://harbor.mycompany.com"]

  [plugins."io.containerd.snapshotter.v1.overlayfs"]
    root_path = "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs"
    upperdir_label = false
  • rootstate:分别指定 containerd 数据存储根目录与运行时临时目录。
  • oom_score:调整 containerd 进程在 OOM 情况下的优先级,-999 意味着最低可能被 OOM 杀死。
  • metrics.address:用于 Prometheus 抓取监控数据,可根据实际部署网络开放端口。
  • sandbox_image:Kubernetes Pod 的沙盒 Pause 镜像(负责网络 namespace)地址,若网络不通国际仓库,需改为国内镜像或私有镜像。
  • systemd_cgroup:若集群使用 systemd cgroup driver,需设为 true
  • snapshotter:根据环境选择 overlayfs(大多数推荐),也可配置 btrfszfs
  • registry.mirrors:为不同 Registry 配置镜像加速或私有仓库地址,显著提升镜像拉取速度。

针对企业场景的镜像加速与私有仓库配置

  1. 添加公共镜像加速器

    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://registry-aliyun.example.com"]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
      endpoint = ["https://gcr.mirrors.ustc.edu.cn"]
    
  2. 配置私有仓库(Harbor)

    • 步骤 1:主机名解析

      # 在所有 containerd 节点 /etc/hosts 添加:
      10.0.0.10    harbor.mycompany.com
      
    • 步骤 2:修改 config.toml

      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.mycompany.com"]
        endpoint = ["https://harbor.mycompany.com"]
      
    • 步骤 3:重启 containerd

      sudo systemctl restart containerd
      
    • 步骤 4:验证拉取

      # 登录 Harbor
      sudo ctr images pull docker.io/library/nginx:latest
      sudo ctr images tag docker.io/library/nginx:latest harbor.mycompany.com/project/nginx:latest
      sudo ctr images push harbor.mycompany.com/project/nginx:latest
      

📈 Tip:对于私有仓库,如果启用了 TLS 证书,请确保证书链正确配置在系统信任列表中;对于 HTTP 协议,还需在 config.toml 中添加 insecure_skip_verify = trueplain_http = true(视版本而定)。

安全性与权限控制

  1. Seccomp 与 AppArmor

    • 默认启用了 Seccomp,若有特殊需求可自定义 seccomp.json 配置。
    • 对于 Ubuntu 系列,可结合 AppArmor 进行白名单控制。
  2. Cgroup 权限隔离

    • 建议启用 systemd_cgroup = true,将容器进程以 systemd 单元的方式管理,便于监控与限速。
    • 可通过 ctr run --cgroup 指定容器的 cgroup 路径,进行资源限制。
  3. TLS 与认证

    • Containerd 的 gRPC 接口支持 TLS 验证,可在 --address 配置中添加 --tls-cert--tls-key
    • 在受限网络环境,可将 gRPC Socket 设为私有 Unix Domain Socket,并设置文件权限。

与 Kubernetes 集成:CRI-Overshadow

Kubelet 配置示例

在 Kubernetes 节点上,需修改 Kubelet 的启动参数,使其使用 containerd 作为运行时。

编辑 /var/lib/kubelet/kubeadm-flags.env(或 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf):

KUBELET_KUBEADM_ARGS="--container-runtime=remote \
  --container-runtime-endpoint=unix:///run/containerd/containerd.sock \
  --runtime-request-timeout=15m \
  --image-service-endpoint=unix:///run/containerd/containerd.sock \
  --pod-infra-container-image=registry.aliyun.com/google_containers/pause:3.6"
  • --container-runtime-endpoint:指定 CRI Socket 地址,默认为 /run/containerd/containerd.sock
  • --pod-infra-container-image:Pause 镜像地址,需要与 config.toml 中的 sandbox_image 一致。

重载并重启 kubelet:

sudo systemctl daemon-reload
sudo systemctl restart kubelet

CNI 与网络插件对接

  1. 安装 CNI 插件

    # 假设放在 /opt/cni/bin
    sudo mkdir -p /opt/cni/bin
    # 下载安装 Flannel/Calico CNI
    tar zxvf calico-amd64-v3.19.1.tgz -C /opt/cni/bin
    
  2. 配置 CNI 网络

    • 将 CNI 配置文件放在 /etc/cni/net.d/ 下,例如 /etc/cni/net.d/10-calico.conflist

      {
        "name": "k8s-pod-network",
        "cniVersion": "0.3.1",
        "plugins": [
          {
            "type": "calico",
            "log_level": "info",
            "datastore_type": "kubernetes",
            "k8s_auth_token": "__SERVICEACCOUNT_TOKEN",
            "k8s_auth_authority": "https://10.96.0.1:443",
            "k8s_client_certificate": "/etc/cni/net.d/calico-kubeconfig",
            "ipam": {
              "type": "calico-ipam"
            },
            "policy": {
              "type": "k8s"
            },
            "kubernetes": {
              "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
            }
          },
          {
            "type": "portmap",
            "snat": true,
            "capabilities": {"portMappings": true}
          }
        ]
      }
      
    • 确保 config.toml 中的 CNI 路径一致:

      [plugins."io.containerd.grpc.v1.cri".cni]
        bin_dir = "/opt/cni/bin"
        conf_dir = "/etc/cni/net.d"
      
  3. 验证网络插件生效

    # 查看节点上是否创建了 cni0、flannel.1 等网络接口
    ip a show cni0
    ip a show flannel.1
    
    # 在 Kubernetes 中创建一个测试 Pod,验证 Pod 间通信
    kubectl run test-pod --rm -it --restart=Never --image=busybox -- sh
    # 在容器内部 ping 另一个 Pod IP
    

示例:在 K8s 中使用 Containerd 部署应用

  1. 部署 nginx 应用

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-demo
    spec:
      containers:
      - name: nginx
        image: nginx:1.21-alpine
        ports:
        - containerPort: 80
    
    kubectl apply -f nginx-pod.yaml
    
  2. 查看容器创建过程

    • Kubelet 调用 CRI.PullImage

      sudo ctr -n k8s.io images ls | grep nginx
      
    • Kubelet 调用 CRI.CreateContainerCRI.StartContainer

      # 查看 containerd 运行的容器
      sudo ctr containers ls | grep nginx-demo
      # 查看 containerd 运行的任务
      sudo ctr tasks ls | grep nginx-demo
      
  3. 调试与排障

    • 查看 Pod 事件:

      kubectl describe pod nginx-demo
      
    • 查看 Kubelet 日志:

      sudo journalctl -u kubelet -f
      
    • 查看 Containerd 日志:

      sudo journalctl -u containerd -f
      

🚀 Tip:在高并发场景下,建议使用更轻量的镜像(如 distroless、Alpine)以加速镜像拉取与启动,同时在集群内部署 Registry Cache 节点,减少跨网络拉取延迟。


日志与监控

Prometheus 监控指标

Containerd 自带 Metrics 插件,将监控数据以 Prometheus 格式暴露在指定端口,便于采集与可视化。

  1. config.toml 中启用 Metrics

    [metrics]
      address = "0.0.0.0:1338"
      grpc_histogram = true
    
  2. 常见 Metrics 列表

    • containerd_runtime_operations_seconds_count{operation="pull"}:拉取镜像耗时统计。
    • containerd_tasks_create_duration_seconds:创建容器任务耗时。
    • containerd_snapshotter_usage_bytes{snapshotter="overlayfs"}:Snapshot 驱动存储使用量。
    • containerd_content_store_blob_total:Content Store 中 Blob 总数。
  3. Prometheus 配置示例

    scrape_configs:
      - job_name: 'containerd'
        metrics_path: /metrics
        static_configs:
          - targets: ['node1:1338', 'node2:1338']
    
  4. Grafana 可视化

    • 官方提供了 Containerd 监控 Dashboard,可直接导入到 Grafana 中。
    • 通过监控 Snapshot 使用量、镜像拉取速率、容器启动延迟等指标,快速定位存储、网络或调度瓶颈。

常见日志路径与排障方法

  • Containerd 日志

    • 默认使用 systemd 进行管理,查看命令:

      sudo journalctl -u containerd -f
      
    • 日志等级可通过 config.toml 中的 [debug] level = "debug" 调整。

  • Shim 与 runc 日志

    • containerd-shim-runc-v2 启动时,会将 Stdout/Stderr 重定向到 systemd 日志中,可通过 journalctl -u containerd 一并查看。

    • 若调试运行时失败,可设置 --log-level debug 参数启动 shim:

      sudo containerd --log-level debug
      
  • Kubelet 日志

    sudo journalctl -u kubelet -f
    
    • 报错如 Failed to create containerd clientImagePullBackOff 等,可查看 Kubelet 与 Containerd 交互的细节。
  • CNI 插件日志

    • 一般打印在 /var/log/calico//var/log/flannel/ 目录下,需根据具体插件查询相应日志。
    • 如 Pod 启动后无网络,可查看 CNI 是否正常加载配置与二进制。

性能分析与优化

Benchmarks 对比(Bucketbench、Microbench)

  1. Bucketbench 简介

    • Bucketbench 是 CNCF 推出的容器运行时基准测试工具,对启动、停止、删除容器等操作进行对比。
    • 常见对比对象:Docker(内置 containerd)、containerd(runc)、CRI-O、CRI-Dockerd、gVisor 等。
  2. 示例 Benchmark 结果对比

    操作项Docker (containerd v1.4)Containerd (v1.4 + runc)CRI-O (v1.20)
    创建容器80ms65ms70ms
    启动容器120ms95ms110ms
    停止容器35ms25ms30ms
    删除容器40ms30ms32ms
    拉取镜像800ms750ms770ms
    镜像解压时长1.2s1.0s1.1s

    结论

    • Containerd 直接使用 runc,省去了 Docker Engine 的额外封装层,在容器启动与删除等场景下表现更优。
    • CRI-O 与 Containerd 性能相近,但在镜像管理与插件生态上,Containerd 更为丰富。
  3. Microbench:微基准测试

    • 通过对单个操作(如 ctr runctr pullctr delete)进行细粒度测量,分析不同 Snapshot 驱动、网络插件、存储后端对性能的影响。

    • 示例命令:

      # 基于 overlayfs 测试启动 100 次 nginx 容器
      for i in $(seq 1 100); do
        sudo time ctr run --rm --snapshotter overlayfs docker.io/library/nginx:alpine bench.$$ sh -c "echo hello"
      done
      

调优思路与最佳实践

快照驱动与存储后端调优

  1. OverlayFS 优化

    • 设置 mount 参数:

      • metacopy=on:减少 copy-up 时元数据操作,提高写性能。
      • upperdir_label:与 SELinux 配合时可开启标签。
    • 如果底层文件系统为 XFS,可使用 ftype=1 格式化,有助于 Overlay 性能。

  2. Btrfs / ZFS 场景

    • Btrfs/ZFS 原生支持写时复制(COW),可在多容器场景下减少层复制开销,提升快照创建速度。
    • 代价是需要额外学习与调优,如设置 ZFS recordsize、Btrfs compress 等参数。
  3. 分布式存储与共享

    • 对于多节点场景,可结合 Ceph/Rook、GlusterFS、NFS 等分布式存储,统一 Content Store 与 Snapshot Store,减少跨节点拉取延迟。
    • 需注意挂载性能、并发访问瓶颈与网络带宽限制。

网络层与 CNI 调优

  1. CNI Plugin 选择

    • Flannel:易于部署,支持 VXLAN 与 Host-GW 模式,适用于中小规模集群。
    • Calico:支持 BGP 路由,网段灵活,可与 Istio 等服务网格无缝集成。
    • Weave:支持 mesh 网络,拓扑自愈能力强。
  2. IPAM 与 MTU 调整

    • 根据集群物理网络 MTU 来调整 CNI 网络 MTU,避免分片带来的性能下降。
    • 如物理网络 MTU = 1500,则设置 CNI MTU = 1450(VXLAN 头部开销)或更合适的数值。
  3. Host Networking vs Bridge 模式

    • Host Networking:容器直接使用宿主机网络,性能最好,但缺少隔离;适用于高性能网络应用(如 DPDK)。
    • Bridge 模式:通过 Linux bridge 或虚拟交换机隔离网络,安全与隔离性好,性能略低于 Host。

收敛时间与资源隔离

  1. 容器启动收敛时间

    • 优化镜像体积:尽可能使用精简镜像(如 multistage 编译、distroless),减少拉取与解压时间。
    • 本地镜像缓存:部署 Registry Cache 节点或使用 CDN 缓存,以降低跨网络拉取耗时。
  2. 资源隔离与 QoS

    • 使用 cgroup v2 或 systemd cgroup driver,实现对容器 CPU、内存、IO 的精细限制。
    • 结合 Kubernetes QoS(Guaranteed/Burstable/BestEffort)策略,保证关键业务容器资源优先级。

常见问题与故障排查

服务无法启动

  1. 检查 systemd 状态

    sudo systemctl status containerd
    
    • 若出现 Failed to start containerd,查看具体错误日志:

      sudo journalctl -u containerd -xe
      
  2. 配置文件语法错误

    • 修改 /etc/containerd/config.toml 后,可能出现 TOML 语法错误:

      • 使用 containerd --config /etc/containerd/config.toml config check 验证(部分版本支持)。
      • 也可将配置文件输出到临时文件,尝试用 toml lint 工具进行校验。
  3. 端口或 Socket 冲突

    • 默认 gRPC 监听 unix:///run/containerd/containerd.sock,若 Socket 已被占用,可尝试删除旧文件:

      sudo rm /run/containerd/containerd.sock
      sudo systemctl restart containerd
      
    • metrics.address 绑定端口与其他服务冲突,需修改或关闭 Metrics。


容器无法拉取镜像

  1. 网络连通性问题

    • 验证节点能否访问外部 Registry:

      curl -v https://registry-1.docker.io/v2/
      
    • 若走私有仓库,检查 /etc/hosts 或 DNS 是否正确解析。

  2. TLS 证书验证失败

    • 私有仓库若使用自签名证书,需将根证书添加到节点的系统信任列表:

      sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
      sudo update-ca-trust
      sudo systemctl restart containerd
      
    • 或在 config.toml 中启用 insecure_skip_verify(不推荐,仅测试时使用)。

  3. 镜像名称或标签错误

    • Containerd 的 ctr 命令需要指定完整路径,如 docker.io/library/nginx:latest,否则可能拉取失败:

      sudo ctr images pull docker.io/library/nginx:latest
      
    • 若镜像是私有镜像,需要先登录:

      sudo ctr images pull --user=username:password harbor.mycompany.com/project/nginx:latest
      

容器启动失败

  1. Runtime 错误

    • 查看 shim 与 runc 报错:

      sudo journalctl -u containerd -n 50
      
    • 常见原因:Seccomp 或 AppArmor 限制,缺少特权权限或 Capabilities。

      • 可在运行时加上 --security-opt seccomp=unconfined--privileged 测试。
  2. 挂载或 Volume 问题

    • 检查挂载点是否存在、权限是否正确:

      ls -ld /data/myvolume
      sudo chown 1000:1000 /data/myvolume
      
    • CNI 网络未准备好,导致容器创建时网络 namespace 创建失败,可通过 ctr network ls(部分版本)或查看 cni 日志定位。

  3. 资源不足

    • 查看系统资源:

      free -h
      df -h
      
    • 检查 cgroup 限制:

      sudo cgget -r cpu.cfs_quota_us /kubepods.slice
      sudo cgget -r memory.limit_in_bytes /kubepods.slice
      

性能瓶颈与高内存占用

  1. Content Store 与 Metadata Store 数据量过大

    • 定期清理无用镜像与快照:

      sudo ctr images rm $(sudo ctr images ls -q | grep "<none>")
      sudo ctr snapshots cleanup
      
    • 确保 GC 插件正常运行,可在 config.toml 中调整 deletion_thresholdmutation_threshold 触发频率。

  2. 过多的 Shim 进程

    • 每个容器对应一个 containerd-shim-runc-v2 进程,容器数量过多可能占用大量内存。
    • 可通过 ctr tasks ls 压测容器并发承载能力,并合理规划节点规模。
  3. CNI 插件内存泄漏

    • 某些版本的 CNI 插件可能存在内存泄漏,升级到最新稳定版本,或切换到更轻量插件如 Flannel VXLAN。
    • 定期重启 containerdkubelet,清理可能残留的网络 namespace 与进程。

总结与展望

Containerd 作为 CNCF 顶级项目,已成为当前云原生生态中不可或缺的容器运行时。通过其轻量、插件化、性能优越的特点,成功取代了早期的 Docker Shim,成为 Kubernetes、云平台与企业内部容器平台的首选底层引擎。

核心优势

  • 高性能:直接调用 runc,无需额外封装层,启动、停止、删除容器效率高。
  • 插件化设计:灵活接入不同存储、网络、运行时插件,满足多种场景需求。
  • 社区活跃:CNCF 资助、厂商支持,版本迭代迅速,生态兼容性强。
  • 企业实战:已在阿里、腾讯、华为、百度等大规模生产环境中广泛应用,支撑千万级容器规模。

未来展望

  • 多样化运行时:与 WebAssembly(WASM)、gVisor、Kata Containers 等运行时深度集成,以满足更高安全隔离需求。
  • CNI 与 Service Mesh 深度融合:网络与服务网格(如 Istio、Linkerd)更加紧密结合,为复杂微服务场景提供更高效的流量管理与监控。
  • 更丰富的可观察性:通过 eBPF、OpenTelemetry 等技术,提升对容器网络、系统调用、性能指标的可视化与告警能力。
  • 边缘与 IoT 场景:针对边缘计算节点资源受限特性,推出更轻量化、自动化的 Containerd 发行版,适配 IoT 与边缘场景。

🎯 技术沉淀:本文从 Containerd 的演进背景切入,深入剖析其架构与关键模块,并结合丰富的企业级实战经验与性能对比,帮助读者全面理解 Containerd 在生产环境中的落地实践。 🚀 社区贡献:欢迎各位技术同仁在 GitHub 上为 Containerd 社区贡献代码、提交 Issue、参与讨论,共同推动云原生生态的繁荣。


参考链接

  1. Containerd 官方文档
  2. GitHub: containerd/containerd
  3. Kubernetes CRI 文档
  4. CNCF Containerd 监控 Dashboard (Grafana)
  5. Docker vs Containerd vs CRI-O 对比分析
  6. OverlayFS 调优最佳实践
  7. Flannel CNI 插件使用手册
  8. Calico CNI 插件官方文档
  9. Kata Containers 官方文档
  10. gVisor 用户指南