跳至主要内容

Containerd 与 Docker:理解容器运行时

了解 Docker 与容器运行时 containerd 之间的关键差异。学习它们的架构,以及在开发中各自适用的场景。
Benito Martin
Benito Martin

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续继续即表示您接受我们的 使用条款,我们的 隐私政策 ,并且您的数据存储在美国。

在容器生态中,一个常见的困惑源于将 Docker(一个完整的平台)与 containerd(实际上是一个聚焦的运行时组件)进行比较。这种比较就像把一辆完整的汽车和它的发动机相比较:两者都不可或缺,但它们在不同的抽象层级上服务于不同的目的。 

在本指南中,我将厘清这两项技术之间的关系,探讨它们的架构、与 Kubernetes 的集成,以及各自擅长的具体使用场景。无论您是在本地构建容器,还是在管理生产级 Kubernetes 集群,弄清何时使用 Docker、何时使用 containerd,都将显著影响您的基础设施决策。

读完本文,您将清楚了解 Docker 与 containerd 如何相互补充,并能根据自身需求选择合适的工具——无论是快速的本地开发,还是大规模的生产部署。

如果您刚接触容器化,我强烈建议您学习我们的 容器化与虚拟化概念课程。

什么是 Docker?

Docker 通过让容器化变得易用且切实可行,彻底改变了软件开发格局。作为一个全面的平台,Docker 提供构建、分发和运行容器化应用所需的一切。

对于初学者,这份容器实践指南是理解 Docker 与容器的绝佳入门材料。

一体化的容器平台

Docker作为一体化的容器平台,提供覆盖大部分容器全生命周期的集成工具链。其核心是将应用及其所有依赖打包为可移植、独立的镜像,从开发者的笔记本到生产服务器,都能在任意环境中一致运行。

Docker logo

这种全面的方法使 Docker 成为本地开发与开发者体验的行业标准。其主要优势包括:

  • 庞大生态:Docker Hub 托管了数百万个预构建镜像,从数据库到 Web 服务器,免去繁琐的安装流程
  • 通用可移植性:只要安装了 Docker,在 macOS 上构建的容器在 Linux 或 Windows 上都能一致运行
  • 面向开发者:抽象底层基础设施差异,让开发者专注于应用而非部署复杂性
  • 快速上手:新成员可以在几分钟而非数小时内启动开发环境

关键组件与工作流

Docker 的架构由数个无缝协作的组件组成。 

Docker CLI 提供面向用户的界面,开发者在此发出命令。当您运行如 docker run 这样的命令时,CLI 会与 Docker 守护进程(dockerd)通信,后者是管理容器、镜像、网络与数据卷的核心服务。

对于 Windows 和 macOS 用户,Docker Desktop 通过图形界面带来额外的便捷,即便不熟悉命令行工具的用户也能轻松管理容器。这个集成环境包含本地开发所需的一切,从对 Kubernetes 的支持到数据卷管理。

典型的工作流遵循清晰的步骤: 

  1. 开发者使用 Dockerfile 构建镜像
  2. 将镜像推送到 Docker Hub 等镜像仓库
  3. 最后在任何启用 Docker 的主机上将镜像作为容器运行 

许多开发者并未意识到,Docker 并不直接执行容器。相反,dockerd 会将实际的容器执行委托给 containerd,后者作为独立进程在后台运行。

Docker Components

这种模块化设计让 Docker 使用 containerd 处理底层操作,使各自专注于所长:Docker 提供对开发者友好的界面与生态,而 containerd 负责容器执行的具体细节。

无论您是刚开始接触 Docker,还是想更进一步,不妨看看我们的10 个 Docker 项目灵感,覆盖各类水平。

什么是 Containerd?

在了解了 Docker 的全面平台化方法之后,我们来看看 containerd——这个在 Docker 内部及更广泛生态中驱动容器执行的专业运行时。

行业标准的容器运行时

Containerd 是轻量级、行业标准的核心容器运行时,已从 云原生计算基金会(CNCF)毕业,体现了其成熟度、可靠性与广泛采用。Containerd 最初是 Docker 的一部分,2017 年被拆分并捐赠给 CNCF,以促进更广泛的生态采用。

Containerd Logo

不同于 Docker 的全面功能集,containerd 的范围有意保持精简:它管理容器生命周期的核心操作,如启动、停止、暂停与删除,并处理镜像传输与存储。专注使 containerd 在生产环境中具备出色的稳定性与高效性。

可以把 containerd 视为“管道设施”:这是一种面向嵌入到更大系统中的基础设施,而非供人直接使用。诸如 Kubernetes、AWS Fargate 和 Google Kubernetes Engine 等主流平台都依赖 containerd 来执行容器,尽管用户通过它们各自的界面进行交互。

架构与设计

要理解 containerd 为何在生产系统中被广泛采用,我们需要审视其架构原则。

Containerd 的架构充分体现了模块化设计原则。其核心采用 OCI(开放容器倡议)标准,确保在整个容器生态中的兼容性。这意味着任何符合 OCI 的镜像都能与 containerd 协同工作,无论使用何种工具创建。

其架构包含几个关键层:

  • gRPC API 层:提供客户端-服务端模型,多个客户端(Docker、Kubernetes、自定义工具)可与同一个 containerd 实例通信
  • Containerd 守护进程:维护运行中容器的状态,通过快照器管理镜像存储,并通过插件协调网络
  • runc 运行时:轻量、符合 OCI 的运行时,直接对接 Linux 内核功能,创建命名空间、设置 cgroups,并启动容器进程
  • 模块化插件:可无须修改 containerd 即集成自定义快照器(用于特定存储)、替代运行时(gVisor、Kata Containers)和网络插件

当 containerd 需要启动容器时,它会拉起 runc,由后者实际完成创建隔离命名空间、设置资源限制的 cgroups 并启动容器进程等工作。 

这种职责分离使 containerd 具有很高的可扩展性。组织几乎可以在不触及 containerd 核心代码的情况下自定义每个方面。

Containerd 与 Docker 的关键差异

在清楚了解各工具的职责后,我们从架构到集成模式来看看它们在实践中的差异。

容器运行时 vs 平台

根本差异在于范围与目的。 

Docker 采用集中式守护进程架构,其中 dockerd 协调诸多方面,例如:

  • 镜像构建
  • 容器执行
  • 网络
  • 数据卷管理

这种一体化设计简化了开发者体验,但也因多层抽象带来了开销。

相较之下,containerd 专注于容器执行本身。它不包含镜像构建能力、编排功能或图形界面。在网络方面,Docker 在其守护进程中内置了 Libnetwork,而 containerd 则依赖可按需替换的外部 CNI(容器网络接口)插件。

此类架构差异会影响性能。在容器频繁启停的高波动环境(如自动扩缩的 Kubernetes 集群)中,containerd 的精简设计可带来更快的启动时间与更低的资源消耗。更小的开销意味着 containerd 更省内存与 CPU,这在规模化时尤为重要。

Kubernetes 集成

谈到 Kubernetes,容器运行时与编排平台之间的关系近年发生了显著演变,尤其体现在 Kubernetes 如何连接到 containerd。

Kubernetes 与容器运行时的关系在 2022 年发布的 Kubernetes 1.24 中发生了重大转变。该版本移除了允许 Kubernetes 使用 Docker 作为容器运行时的兼容层 “Dockershim”。

Dockershim 自始至终只是临时桥梁。Kubernetes 引入容器运行时接口(CRI)以标准化与运行时的通信时,因 Docker 早于 CRI 的设计而无法直接实现 CRI。Dockershim 负责在 Kubernetes 与 Docker 之间做翻译,但最终这一翻译层变得不再必要。

现代的 Kubernetes 版本通过 CRI 直接与 containerd 通信,彻底消除了 Docker 的翻译层。这种简化带来了实实在在的好处:

  • 降低延迟:直接通信消除了容器操作中的翻译开销
  • 提升稳定性:组件更少,潜在故障点也更少
  • 更佳性能:精简的运行时栈对大规模部署尤为有利
  • 简化排障:直接的 CRI 集成让故障排查更直接

对于 Kubernetes 用户而言,这一变化在很大程度上是透明的。因为遵循 OCI 标准,Docker 镜像依然完全兼容。其实际影响是:生产级 Kubernetes 集群如今通过直接使用 containerd 运行得更高效,而开发者仍可在本地用 Docker 进行构建与测试。

如果您还在犹豫是否该使用 Kubernetes,不妨看看这篇关于 Docker Compose 与 Kubernetes 的对比文章。

镜像构建与管理

虽然运行时集成对编排至关重要,但开发者工作流很大程度上取决于各工具如何处理镜像创建与存储。镜像构建正是 Docker 与 containerd 之间的重要能力差距所在。 

Docker 通过 Dockerfile 与 BuildKit 提供集成的构建系统,允许开发者构建具有缓存、并行化,以及构建密钥与 SSH 代理转发等高级特性的复杂多阶段构建。

Containerd 在设计上不包含原生的镜像构建工作流。若要用 containerd 构建镜像,开发者必须使用外部工具。可选方案包括以独立守护进程运行 BuildKit 并使用 buildctl 命令行构建,或采用与 Docker 兼容的 CLI nerdctl 来集成 BuildKit。

两者在存储机制上的理念也有所不同。Docker 的数据卷管理提供对开发者更自然的抽象,通过命名卷在容器生命周期之外持久化数据。Containerd 则使用更底层的快照器系统,可插入不同的快照驱动,以根据底层存储需求差异化处理分层文件系统。

这种差异反映了两者的目标受众: 

  • Docker 通过内建便捷功能优化开发者效率
  • Containerd 提供灵活的原语,供平台构建者按需组合搭建

CLI、开发者体验与 nerdctl

抛开架构与特性,日常开发者体验最直接地体现在各工具提供的命令行界面。命令行体验最能揭示不同的设计哲学。 

Docker 的 CLI 以友好易用著称。诸如 docker run、docker build 与 docker logs 等命令直观、文档完善且面向人类使用。CLI 提供贴心的默认值、清晰的错误信息,以及覆盖大多数用例的丰富选项。

Containerd 附带的 ctr 是一个仅用于调试与测试 containerd 底层功能的最小化 CLI。ctr 有意不对开发者友好,缺少端口映射简写、自动重启策略以及与凭据助手的集成等常见功能。它面向的是 containerd 的开发者,而非应用开发者。

用 nerdctl 弥合差距

这一可用性差距催生了 nerdctl——一个面向 containerd 的、与 Docker 兼容的 CLI。使用 nerdctl 的体验与使用 Docker 基本相同:命令语法一致、参数一致、工作流一致,但底层运行时换成了 containerd。这使 nerdctl 成为团队在开发环境中从 Docker 迁移到 containerd 的理想过渡工具。

在实践中,开发者很少直接与 containerd 交互。需要时,nerdctl 提供熟悉的界面;而平台运营者与管理员则通过 Kubernetes 等编排系统以编程方式使用 containerd 的 API。

为直观对比,下面展示三种 CLI 工具在常见容器操作上的差异:

任务

Docker

nerdctl

ctr

运行容器

docker run -d -p 8080:80 nginx

nerdctl run -d -p 8080:80 nginx

ctr run --net-host -d docker.io/library/nginx:latest nginx_id

列出容器

docker ps

nerdctl ps

ctr tasks list

构建镜像

docker build -t myapp .

nerdctl build -t myapp .

不支持

查看日志

docker logs <container>

nerdctl logs <container>

不支持

检查容器

docker inspect <container>

nerdctl inspect <container>

ctr containers info <container>

拉取镜像

docker pull nginx

nerdctl pull nginx

ctr images pull docker.io/library/nginx:latest

Compose 支持

docker compose up

nerdctl compose up

不支持

注:ctr 不支持端口映射(-p),需要使用主机网络(--net-host)来暴露服务。它也不会自动拉取镜像。

关键差异概览

在给出具体用例建议之前,我们先回顾一下已讨论的差异。下表总结了 Docker 与 containerd 在关键功能上的差异,突出它们各自的能力与目标受众:

特性

Docker

Containerd

镜像构建

集成(Dockerfile、BuildKit)

需要外部工具(buildctl、nerdctl)

编排

Docker Swarm / Kubernetes

无(由 Kubernetes 使用)

存储管理

数据卷管理

快照器系统

图形界面

Docker Desktop

无

容器生命周期

完整管理(经由 containerd)

核心聚焦(兼容 CRI)

主要用户

应用开发者

集群运营者、平台构建者

为何选择 Docker?

在讲完技术差异后,我们来看看各自擅长的实际场景。尽管 containerd 在生产环境中崭露头角,但在强调开发者体验与完善工具链的特定场景下,Docker 仍是更优之选。

用于本地开发与原型制作

当您需要一套“开箱即用”的编写与测试代码方案时,Docker 表现出色。借助集成工具链,开发者无需组装多个组件或配置复杂网络,就能在几分钟内从零到运行容器。

Docker 生态带来显著的效率提升:

  • Docker Hub:数以百万计可即用的数据库、消息队列与 Web 服务器镜像
  • Docker Compose:在单个 YAML 文件中定义多容器应用,一条命令即可启动完整开发环境
  • Docker Desktop GUI:可视化容器管理、数据卷浏览、资源限制调整,以及集成的 Kubernetes 支持
  • 更低上手门槛:图形化工具与直观命令让新接触容器的开发者也能轻松入门

Docker ecosystem

对于使用 Docker Desktop 的团队,GUI 提供的额外便捷可显著加速入职与日常工作流。

用于复杂的构建流水线

除了开发环节,Docker 的构建能力也使其成为复杂持续集成与交付工作流的自然之选。

Docker 集成的 BuildKit 提供现代 CI/CD 流水线所必需的高级功能。多阶段构建在保持 Dockerfile 可读性的同时最大限度缩小镜像体积。BuildKit 的缓存机制可在构建间智能复用层,在持续集成环境中显著缩短构建时间。

大多数自动化平台(如 GitHub Actions、GitLab CI、Jenkins 等)都具备成熟、久经考验的 Docker 集成。这些集成可无缝处理认证、缓存与镜像发布。

虽然其他工具也能达成类似效果,但 Docker 的普及度意味着解决方案与故障排查资源更易获得,这同样是巨大优势。

为何选择 Containerd?

在强调极简、性能与稳定性胜过集成工具便利性的生产场景中,containerd 大放异彩。

用于生产级 Kubernetes 集群

将 containerd 用作 Kubernetes 节点的运行时可带来可量化的收益:

  • 更低开销:移除 Docker 守护进程后,每个节点的资源占用更低,在规模化时可带来可观节省
  • 更高稳定性:组件更少,基础设施的潜在故障点更少
  • 更小攻击面:更少的代码需审计、潜在安全漏洞更少
  • 更易排障:直接 CRI 集成移除了 dockershim 翻译层,使故障排查更直接
  • 更佳性能:精简的运行时栈改善容器启动时间与响应性

Containerd with Kubernetes

Containerd 获得 CNCF 毕业认证,表明其成熟与可靠。包括 AWS、Google Cloud 与 Azure 在内的主要云服务商都在其托管的 Kubernetes 产品中采用了 containerd,体现了其在关键基础设施中的生产可用性。

用于专业化、极简环境

尽管 Kubernetes 是最常见的生产用例,但 containerd 的轻量特性也为 Docker 难以胜任的部署场景打开了大门。

边缘计算与物联网设备常常在严苛的资源约束下运行。Containerd 的轻量设计使其在这些环境中可行,而 Docker 的完整栈则可能过于笨重。在嵌入式硬件上运行时,每一兆内存与每一个 CPU 周期都弥足珍贵。

高级安全场景也受益于 containerd 的模块化运行时架构。组织可为需要更强安全边界的工作负载集成沙箱化运行时。例如:

  • gVisor:提供更强的内核隔离
  • Kata Containers:在轻量虚拟机中运行容器

这些集成可直接插入 containerd,而无需进行大幅修改。

从 Docker 迁移到 Containerd

理解各工具的适用时机很重要,但同样重要的是掌握如何在它们之间迁移。在现有环境中从 Docker 迁移到 containerd 需要周密规划,但过程有据可依且相对直接。

迁移 Kubernetes 节点

将 Kubernetes 节点从 Docker 迁移到 containerd 的操作步骤通常遵循标准模式:

  1. 隔离节点(cordon):阻止新 Pod 被调度(kubectl cordon <node-name>)

  2. 排空现有 Pod:将工作负载迁移到其他节点(kubectl drain <node-name>)

  3. 更新 Kubelet 配置:指向 containerd 的 CRI 套接字 /run/containerd/containerd.sock

  4. 验证 CNI 插件:确保 containerd 已安装必要的网络插件

  5. 重启 Kubelet:使用新运行时注册并重新加入集群

先在非生产节点上测试迁移,有助于在全集群推广前识别特定环境问题。为避免常见陷阱,请务必遵循以下最佳实践:

  • 日志路径变更:Docker 与 containerd 的默认日志位置不同,请相应更新日志基础设施
  • 安装缺失的 CNI 插件:Containerd 需要用于网络的 CNI 插件二进制文件;这些并不总是默认安装
  • 注意镜像拉取差异:认证与镜像仓库配置可能需要调整
  • 警惕存储驱动不匹配:确保持久卷与 containerd 的快照器兼容

适应 CLI 差异

当基础设施迁移完成后,开发者需要调整日常工作流以适配新运行时。

对于习惯了 Docker 命令的开发者,nerdctl 提供几乎一致的体验。nerdctl run、nerdctl build 与 nerdctl compose up 等命令与它们在 Docker 中的对应用法完全一致,使迁移变得顺畅。

在调试方面,理解如何将 Docker 的调试工作流映射到 containerd 至关重要。比如,查看容器时,您可能会用 docker inspect,而在 containerd 中 ctr containers info 提供类似信息,只是格式不同。同样,ctr tasks list 用于查看正在运行的容器。

大多数开发者会发现,nerdctl 让他们在日常任务中几乎无需学习 ctr 的语法。低层的 ctr 仍然在排查运行时特定问题或直接与 containerd API 交互时有用。

结论

Docker 与 containerd 之间的关系,是软件基础设施中成功模块化设计的完美示例。对于编写代码的人类而言,Docker 仍是最佳工具,因为它提供集成体验、完善生态与对开发者友好的界面,使构建容器化应用既高效又愉悦。

与此同时,containerd 作为机器执行代码的运行时表现出色,具备生产级编排平台所需的稳定性、性能与极简特性。Docker Engine 在底层使用 containerd,正说明两者是互补而非对立的。

对大多数组织,我建议采取务实做法:在开发者的笔记本上继续使用 Docker,以加速开发工作流;而在生产级 Kubernetes 集群中考虑直接迁移到 containerd,以获得更低开销与更简化的运行时栈带来的运维收益。

无论您选择 Docker 的全面平台,还是 containerd 的聚焦运行时,它们都是现代容器生态中不可或缺的组成部分,各自针对应用生命周期的不同阶段进行了优化。

若想继续学习,欢迎报名我们的 Docker 与 Kubernetes 容器化与虚拟化技能路径。

Containerd 与 Docker 常见问题

我可以在 containerd 中使用 Docker 镜像吗?

当然可以。Containerd 支持任何符合 OCI 的容器镜像,包括使用 Docker 构建的镜像。由于 Docker 构建的是符合 OCI 标准的镜像,它们能与 containerd 及所有其他符合 OCI 的运行时无缝协作。您可以在本地使用 docker build,并在生产中用 containerd 运行这些镜像,而无需担心兼容性问题。

Docker 底层是否使用了 containerd?

可以。Docker Engine 使用 containerd 作为其核心容器运行时。从 Docker 1.11 起,Docker 集成了 containerd 来处理容器的创建、执行与管理等生命周期操作。当您运行 docker run 时,Docker 守护进程(dockerd)会将实际的容器执行委托给 containerd,随后由 containerd 使用 runc 与 Linux 内核交互。

为什么 Kubernetes 移除了对 Docker 的支持?

早在 2022 年,Kubernetes 就移除了 dockershim(Docker 兼容层),以消除不必要的翻译层。Docker 早于容器运行时接口(CRI)问世,因此 Kubernetes 需要 dockershim 在其 API 与 Docker 之间进行翻译。通过经由 CRI 直接与 containerd 通信,Kubernetes 获得了更好的性能、稳定性以及更简洁的运行时栈。而 Docker 镜像在 Kubernetes 中依然能完美运行。

我是否应在本地开发中从 Docker 切换到 containerd?

不需要,Docker 仍是本地开发的更佳选择。Docker 提供集成工具链,其中包含Docker Compose、Docker Desktop 的 GUI,以及广泛的生态支持,能加速开发工作流。在生产级 Kubernetes 集群中使用 containerd,可借助其更低开销与直接 CRI 集成带来的优势;但在开发者笔记本上仍保留 Docker,以获得更佳的开发者体验。

什么是 nerdctl?我需要用它吗?

Nerdctl 是面向 containerd 的、与 Docker 兼容的 CLI,提供与 Docker 相同的使用体验(支持大多数常见命令与参数),但运行时使用的是 containerd。若您希望以熟悉的 Docker 命令直接与 containerd 交互,就需要它。它在使用 containerd 的开发环境中尤为有用,或在团队从 Docker 过渡到基于 containerd 的工作流时发挥作用。

Benito Martin

作者

Benito Martin

作为 Martin Data Solutions 的创始人兼自由职业数据科学家、ML 与 AI 工程师,我在回归、分类、NLP、LLM、RAG、神经网络、集成方法和计算机视觉等领域拥有多元项目经验。

  • 成功交付多项端到端机器学习项目,覆盖数据清洗、分析、建模与在 AWS 和 GCP 上的部署,提供具备影响力且可扩展的解决方案。
  • 使用 Streamlit 和 Gradio 构建交互式、可扩展的网页应用,服务于不同行业场景。
  • 教授并指导数据科学与数据分析学员,通过个性化学习路径促进其职业成长。
  • 为企业级需求设计检索增强生成(RAG)应用课程内容。
  • 撰写高影响力的 AI 与 ML 技术博客,涵盖 MLOps、向量数据库和 LLM 等主题,并获得显著关注。

在我参与的每个项目中,我都会应用最新的软件工程与 DevOps 实践,如 CI/CD、代码规范检查、格式化、模型监控、实验追踪以及健壮的错误处理。我致力于交付完整的解决方案,将数据洞察转化为切实可行的策略,帮助企业成长,并充分发挥数据科学、机器学习与 AI 的价值。

主题