Skip to content

容器生态概念全景:从 nerdctl 到 Dragonfly,一篇理清所有关系

1. 为什么需要理清这些概念

容器生态在过去十年中经历了爆炸式增长。从最初的 Docker 一家独大,到如今围绕 OCI(Open Container Initiative)标准形成的多元化工具链——仅仅"运行一个容器"这件事,背后就涉及低级运行时高级运行时沙箱运行时构建工具分发系统等多个层次的不同项目。

如果你之前读过本博客的另一篇文章《Docker 引擎架构:理解 Docker、Moby、containerd 和 runc 之间的关系》,应该已经对 dockerdcontainerdrunc 这条核心调用链有了清晰认识。本文作为姊妹篇,将视野从 Docker 内部扩展到整个云原生容器生态,逐一厘清 nerdctlcri-ocrunkatagVisorKubeVirtbuildkit/buildxDragonflyunregistry 以及 distribution/distribution 这些概念的定位与关系。

阅读建议:如果你对 containerdruncdockerd 的基础概念还不熟悉,建议先阅读上文中链接的 Docker 引擎架构文章,再回到本文继续。

2. 容器运行时分层模型

在讨论具体项目之前,必须先理解容器运行时的分层架构。OCI 标准将"运行容器"这件事拆成了两个层级:

高级运行时负责镜像管理(拉取、解压、存储)、容器生命周期管理(创建、启动、停止)、网络与存储接口。它暴露 gRPC API,典型代表是 containerdcri-o

低级运行时负责实际操作 Linux 内核特性(命名空间、cgroups、安全策略)来创建和运行容器进程。它遵循 OCI 运行时规范,典型代表是 runccrun

二者的关系用一句话概括:高级运行时管理"要做什么",低级运行时执行"怎么做"

2.1 低级运行时:runc 与 crun

runc 是 OCI 运行时规范的参考实现,用 Go 语言编写。它诞生于 2015 年,由 Docker 公司将内部的 libcontainer 库改造为独立工具并捐赠给 OCI。runc 的职责非常纯粹——接收 OCI bundle(包含 rootfsconfig.json),创建 Linux 命名空间、设置 cgroups、挂载文件系统,然后启动容器进程。正如前文 Docker 引擎架构文章所述,你在执行 docker run 时,调用链末端的实际执行者就是 runc。

crun 是 Red Hat 主导开发的 OCI 运行时,用 C 语言编写。它的设计目标与 runc 完全一致(遵循同一 OCI 规范),但提供了更好的内存效率和更快的启动速度。crun 支持 cgroup v2,并且对 user_namespaces 有更完善的支持。在 Red Hat 的生态中(如 Podman、CRI-O),crun 常作为默认的 OCI 运行时。

特性runccrun
语言GoC
维护方OCI / CNCF 社区Red Hat
OCI 规范兼容参考实现完全兼容
内存占用较高较低
默认使用场景containerd / DockerCRI-O / Podman

对绝大多数用户来说,runc 和 crun 在使用上几乎无感知差异——它们做的事情完全相同,都是 OCI 运行时的"可替换零件"。

2.2 高级运行时:containerd 与 CRI-O

containerd 在前文 Docker 引擎架构文章中有详尽介绍。它的核心能力包括镜像传输与存储、容器执行与监控、快照管理。containerd 暴露 gRPC API,被 Docker(通过 moby 命名空间)和 Kubernetes(通过 k8s.io 命名空间)同时使用,也提供了 CRI 插件以对接 kubelet。containerd 是 CNCF 的毕业项目。

CRI-O(Container Runtime Interface - OCI)是专为 Kubernetes 设计的轻量级高级运行时。与 containerd 不同,CRI-O 的创建目标非常明确:只做 Kubernetes 需要的事情,不支持 Docker API,不提供镜像构建功能,不附带多余的抽象层。

CRI-O 的核心理念是"用系统工具做系统管理"——它将镜像存储委托给 containers/storage,将网络委托给 CNI 插件,将低级运行委托给 runc 或 crun。这种"单一职责"的设计使得 CRI-O 代码量更小、攻击面更窄。

containerd 和 CRI-O 的关键区别:

维度containerdCRI-O
设计目标通用容器运行时专为 Kubernetes 设计
Docker API 兼容间接(通过命名空间隔离)不支持
镜像仓库管理内置委托给 containers/image
使用场景Docker / Kubernetes几乎仅用于 Kubernetes
额外 CLI 生态ctr / nerdctlcrictl(CRI 标准工具)
代码复杂度较高较低

选择建议:如果你运行纯 Kubernetes 集群且希望最小化攻击面,CRI-O 是合理选择;如果你需要一个既能对接 Kubernetes 也能用于本地开发的通用运行时,containerd 更合适。

2.3 containerd 的 CLI 生态:ctr、crictl 与 nerdctl

containerd 作为守护进程运行后,用户需要通过 CLI 工具与之交互。这里存在三个不同定位的工具,初学者很容易混淆。

ctr 是 containerd 项目自带的调试用 CLI。它功能简陋、不支持命名字段,主要用于 containerd 开发者的日常调试:

bash
# 拉取镜像
ctr images pull docker.io/library/nginx:latest

# 运行容器(参数必须按顺序提供,非常不友好)
ctr run --rm -t docker.io/library/nginx:latest nginx-test

crictl 是 Kubernetes 社区维护的 CRI 兼容调试工具。它通过 CRI API 与任何兼容的高级运行时(containerd 或 CRI-O)交互,是 Kubernetes 节点上排查容器问题的标准工具:

bash
# 列出 Pod(crictl 用的是 Kubernetes 的 Pod 概念)
crictl pods

# 列出容器
crictl ps -a

# 查看容器日志
crictl logs <container-id>

nerdctl 是 containerd 的"类 Docker CLI"工具,由 containerd 社区维护。它的设计目标是:让习惯 docker 命令的用户无缝切换到 containerd。nerdctl 兼容 Docker Compose(nerdctl compose up),支持 rootless 模式,支持镜像签名与验证,支持多种网络驱动。2025 年,nerdctl 已成为 containerd 生态中推荐的首选 CLI:

bash
# 运行容器——几乎和 docker 命令一模一样
nerdctl run -d --name web -p 8080:80 nginx:latest

# 构建镜像(背后调用 BuildKit)
nerdctl build -t myapp:latest .

# Compose 多容器编排
nerdctl compose up -d

# Rootless 模式
nerdctl run --rm -it alpine

记忆口诀ctr 是 containerd 的"调试螺丝刀",crictl 是 Kubernetes 的"诊断工具",nerdctl 是 containerd 的"通用用户界面"。

3. 沙箱化与虚拟化运行时

前面介绍的 runc 和 crun 都是"进程级隔离"的低级运行时——容器进程与宿主机共享同一个 Linux 内核。这种方式性能极佳,但隔离性较弱:一旦内核出现漏洞,容器就可能逃逸到宿主机。

沙箱化运行时(Sandboxed Runtime)通过在容器外层增加额外的隔离层来解决这个问题。它们同样遵循 OCI 运行时规范,可以被 containerd 或 CRI-O 作为 drop-in 替代品使用。

3.1 Kata Containers

Kata Containers 是 Intel 和 Hyper 公司于 2017 年联合发起的项目,现已捐给 OpenInfra 基金会。它的核心思想是:每个容器运行在独立的轻量级虚拟机中,拥有自己的精简内核。这提供了硬件级别的隔离强度,同时保持了容器的操作体验。

Kata 的架构原理如下:

Kata 支持多种虚拟机监控器(VMM):QEMU(默认)、Cloud Hypervisor、Firecracker。Kata 3.0 之后,社区已将运行时核心重写为 Rust,称为 kata-runtime-rs,大幅降低了内存开销。Kata 是"安全容器"领域最高级的实现,广泛应用于多租户公有云和金融场景。

3.2 gVisor(runsc)

gVisor 是 Google 开发的用户态内核方案,用于 Google Cloud Run、GKE Sandbox 等产品。与 Kata 不同,gVisor 并不启动真正的虚拟机,而是在用户态实现了一个由 Go 语言编写的"应用内核"——sentry。

runsc 是 gVisor 提供的 OCI 兼容运行时。当你用 runsc 运行容器时:

  1. 容器的系统调用被拦截,不直接发给宿主内核;
  2. 系统调用由 Sentry(用户态内核)处理;
  3. Sentry 仅将少量可控的系统调用转发给宿主内核。

gVisor 的优势在于启动速度比虚拟机快(无需启动 Guest 内核),兼容性比传统容器好(自带网络栈实现)。缺点是某些系统调用(如部分 io_uring 操作)不被支持,兼容性不如 Kata 的完整内核。

3.3 KubeVirt

KubeVirt 的定位与前两者有本质不同。Kata 和 gVisor 的核心目标都是"让容器更安全",而 KubeVirt 的核心目标是"在 Kubernetes 上运行传统虚拟机"

KubeVirt 不是 OCI 运行时——你不能用 --runtime=kubevirt 来运行容器。它是一个 Kubernetes 扩展(通过 CRD 和自定义控制器),为集群添加了管理虚拟机的能力:

yaml
# KubeVirt 定义的 VirtualMachine 资源
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: my-vm
spec:
  running: true
  template:
    spec:
      domain:
        devices:
          disks:
            - name: containerdisk
              disk: {}
        resources:
          requests:
            memory: 2Gi
      volumes:
        - name: containerdisk
          containerDisk:
            image: quay.io/containerdisks/centos:9

KubeVirt 使用 KVM 和 QEMU 虚拟化技术,每个 VM 的磁盘、网络、配置全部通过 Kubernetes 原生 API 管理。它最适合以下场景:

  • 需要在同一集群中同时管理容器和传统 VM 工作负载
  • 需要迁移遗留应用而暂时无法容器化
  • 需要比容器更强的隔离性,但不想脱离 Kubernetes 运维体系

区分要点:Kata 让容器像 VM 一样安全,KubeVirt 让 VM 像容器一样管理。二者解决的是不同问题。

3.4 沙箱运行时对比总结

方案隔离方式启动速度兼容性适用场景
runc / crun进程级(共享内核)极快最好普通容器
gVisor (runsc)用户态内核较高无服务器 / 轻量安全
Kata Containers轻量 VM较慢完整多租户 / 金融级安全
KubeVirt完整 KVM VM完整(VM)传统 VM 迁移到 K8s

这四种方案可以共存于同一个 Kubernetes 集群中,通过 RuntimeClass 资源按需选择:

yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata
---
apiVersion: v1
kind: Pod
spec:
  runtimeClassName: kata  # 指定使用 Kata 运行时
  containers:
    - name: secure-app
      image: myapp:latest

4. 镜像构建现代化:BuildKit 与 buildx

4.1 BuildKit

BuildKit 是 Moby 项目下的下一代镜像构建引擎,由 Docker 社区和 Tõnis Tiigi 于 2017 年发起。相较于旧版 docker build,BuildKit 带来了质的飞跃:

  • 并行构建:无依赖关系的构建阶段可以同时执行;
  • 增量构建与高效缓存:支持多种缓存后端(本地、registry、S3、GitHub Actions cache);
  • 构建秘钥管理--secret 参数允许在构建时安全传递敏感信息而不写入镜像层;
  • 多架构构建:原生支持 manifest list 和跨平台构建;
  • 非 Dockerfile 前端:BuildKit 支持自定义构建语法,如 Mockerfile、Earthly 等。

BuildKit 作为一个独立守护进程运行(buildkitd),可以通过 gRPC 接收构建请求。containerd 也可以直接对接 BuildKit 实现 nerdctl build

4.2 buildx

buildx 是 Docker CLI 的一个插件,本质上是 BuildKit 的"用户界面"。它把 BuildKit 的强大能力以 docker build 兼容的语法暴露给用户。

buildx 最具价值的功能是跨平台镜像构建。传统的 docker build 只能构建与宿主机相同架构的镜像,而 docker buildx build --platform linux/amd64,linux/arm64 可以使用 QEMU 模拟或通过远程原生节点,一次性构建多个架构的镜像并合并成一个 manifest list:

bash
# 创建多架构构建器
docker buildx create --name mybuilder --use

# 构建并推送 linux/amd64 和 linux/arm64 镜像
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myregistry/myapp:latest \
  --push .

一句话关系buildx 是 Docker CLI 插件,负责把用户的 docker buildx build 命令翻译成 BuildKit 的 gRPC 请求;buildkitd 是实际干活的那个。

5. 容器镜像分发生态

镜像构建完成后,需要通过分发系统在网络中传输。这是容器生态中容易忽视但至关重要的环节。

5.1 CNCF Distribution(distribution/distribution)

https://github.com/distribution/distribution 是 CNCF 的孵化项目,前身为 Docker Registry v2。它是大多数容器镜像仓库的核心实现——Docker Hub、Harbor、阿里云 ACR、腾讯云 TCR 等公开和私有镜像仓库的底层都是它或其 fork。

Distribution 提供标准的 OCI 分发 API(符合 OCI Distribution Spec),支持:

  • 镜像推送(push)和拉取(pull);
  • 层去重和垃圾回收;
  • 多种存储后端(本地文件系统、S3、GCS、Azure Blob 等);
  • 访问控制和认证(通过委托给代理或中间件)。

在生产环境中,Distribution 通常不单独部署,而是嵌入到更完整的镜像仓库产品中(如 Harbor 在 Distribution 外层添加了漏洞扫描、签名验证、RBAC 等)。但如果你只需要一个轻量级的私有镜像仓库,运行一个 Distribution 实例就是最简单的选择:

bash
docker run -d -p 5000:5000 --name registry registry:2

5.2 Dragonfly:P2P 镜像分发加速

Dragonfly(原名 Dragonfly)是阿里云开源的 P2P 文件与镜像分发系统,CNCF 孵化项目。在拥有数万台节点的大型 Kubernetes 集群中,成百上千个节点同时从中心仓库拉取镜像会造成严重的网络拥塞(即"网络打桩"问题)。Dragonfly 通过 P2P 协议解决这个问题:

Dragonfly 的核心组件包括:

  • Manager(v2):管理 P2P 集群,调度下载任务;
  • Scheduler:决策哪些 peer 之间互相传输数据;
  • Seed Peer:作为集群内的"种子节点",直接从源仓库拉取镜像;
  • Peer:部署在每个需要拉取镜像的节点上的客户端(dfdaemon),拦截 containerd 或 Docker 的镜像拉取请求并转为 P2P 下载。

Dragonfly 的优势在大规模集群中尤为显著:当 1000 个节点同时需要拉取 10GB 的 AI 推理镜像时,传统方式下 registry 的出带宽会被打满,而 P2P 方式下每个节点从周围的 peer 获取数据,registry 只需提供一份完整拷贝给种子节点。

值得一提的是,Dragonfly 2.0 从 Go 语言重写,架构大幅简化,已去除对 Redis 和 MySQL 的外部依赖,部署门槛显著降低。

5.3 unregistry:内存级 OCI 分发

https://github.com/psviderski/unregistry 是一个极简的 OCI 分发规范实现,它的核心特性是完全运行在内存中——没有持久化存储,不依赖外部数据库,重启即清空。

unregistry 的设计定位是"分发系统的最后一块拼图"——它实现了 CNCF Distribution 的核心 API(push、pull、discover),但将所有代码压缩到单文件,使其成为 OCI 分发生态中的参考实现和研究工具。

它的使用场景非常独特:

  • 集成测试:在 CI 中快速启动一个 OCI registry 作为测试桩(无需持久化,免清理);
  • 教学演示:用最少的代码展示 OCI 分发规范的实现原理;
  • 本地临时中转:在开发和调试过程中临时存放镜像,用完即弃。
bash
# 用 Python 快速启动 unregistry(在内存中运行)
uvx unregistry --port 5000

# 推送镜像(和标准 registry 操作完全一致)
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest

unregistry 约 400 行 Python 代码,可作为学习 OCI 分发规范 API 实现的最佳起点。

三者关系distribution/distribution 是生产级实现,Dragonfly 是 P2P 加速层,unregistry 是教学与测试用的最小实现。它们分别代表了容器镜像分发生态的核心网络优化理解学习三个维度。

6. 全景关系图与总结

6.1 全景架构

将上述所有概念放在一张图中,可以看到完整的容器生态分层关系:

6.2 速记参考表

概念一句话定位关键关联
dockerdDocker 守护进程,容器平台入口内部调用 containerd
containerdCNCF 毕业高级运行时Kubernetes 和 Docker 的共同基础
runcOCI 低级运行时参考实现containerd 的默认执行器
crunC 语言 OCI 低级运行时Red Hat 生态默认,内存更省
cri-o专为 K8s 设计的高级运行时只做 K8s 需要的,更轻更小
nerdctlcontainerd 的"类 Docker"CLI支持 Compose、rootless、镜像签名
buildkit下一代镜像构建引擎并行构建、多架构、缓存
buildxDocker CLI 的构建插件是 BuildKit 的用户界面
kataVM 级安全容器运行时每个容器独享精简内核
gVisor / runsc用户态内核安全运行时Google Cloud Run 的底层
KubeVirtK8s 上运行传统 VM不是 OCI 运行时,是 K8s 扩展
distribution标准容器镜像仓库实现原 Docker Registry v2,CNCF 项目
DragonflyP2P 镜像分发加速大规模集群避免网络拥塞
unregistry内存级 OCI Registry 最小实现教学 / 测试 / 临时中转

6.3 总结

容器生态的多样性源于 OCI 标准的开放设计。当你理解了镜像规范定义了镜像格式,运行时规范定义了容器执行接口,分发规范定义了镜像传输协议——你就能明白为什么有这么多不同的项目,因为它们各司其职、可互换组合。

选择容器技术栈时,可以从以下维度做决策:

  1. 隔离性 vs 性能:普通业务用 runc/crun,多租户敏感业务用 Kata,Serverless 场景考虑 gVisor;
  2. 通用性 vs 专注性:通用使用 Docker/containerd,纯 K8s 环境用 CRI-O 更轻便;
  3. 规模考量:小集群直接用 registry,大集群(500+ 节点)引入 Dragonfly P2P 加速;
  4. 构建效率:多架构构建用 buildx,复杂构建流水线考虑直接对接 BuildKit API;
  5. 学习路径:理解规范层面可看 unregistry 的源码,生产环境用 distribution/distribution。

希望这篇文章能帮助你在面对纷繁的容器生态论文、选型和部署文档时,快速定位每个组件的坐标,做出合理的技术决策。

7. 参考文献

  1. OCI Distribution Specification — https://specs.opencontainers.org/distribution-spec/
  2. OCI Runtime Specification — https://specs.opencontainers.org/runtime-spec/
  3. OCI Image Specification — https://specs.opencontainers.org/image-spec/
  4. containerd — https://github.com/containerd/containerd
  5. nerdctl — https://github.com/containerd/nerdctl
  6. CRI-O — https://github.com/cri-o/cri-o
  7. crun — https://github.com/containers/crun
  8. Kata Containers — https://github.com/kata-containers/kata-containers
  9. gVisor — https://github.com/google/gvisor
  10. KubeVirt — https://github.com/kubevirt/kubevirt
  11. BuildKit — https://github.com/moby/buildkit
  12. buildx — https://github.com/docker/buildx
  13. Dragonfly — https://github.com/dragonflyoss/Dragonfly2
  14. CNCF Distribution — https://github.com/distribution/distribution
  15. unregistry — https://github.com/psviderski/unregistry
  16. Docker 引擎架构:理解 Docker、Moby、containerd 和 runc 之间的关系 — 本博客 2025-10 月文章