拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从Docker到containerd:容器运行时核心引擎实战指南

1. 从Docker到containerd为什么我们需要关注这个“引擎”如果你在过去几年里接触过容器技术那么“Docker”这个名字几乎就是容器的代名词。我们习惯了用docker run启动一个容器用docker ps查看运行状态用docker build构建镜像。Docker 为我们封装了一个完整的、用户友好的容器生命周期管理体验。然而在 Docker 这个“全家桶”之下真正负责容器核心运行时runtime工作的是一个名为containerd的组件。随着 Kubernetes 在 1.20 版本宣布弃用 Docker并在后续版本中转向直接集成 containerd 和 CRI-O 作为其容器运行时这个曾经默默无闻的后台引擎正式走到了舞台中央。简单来说containerd 是一个行业标准的容器运行时。它专注于容器的核心生命周期管理镜像的拉取与管理、容器的创建、启动、停止、删除以及底层存储和网络命名空间的分配。它不像 Docker 那样提供构建镜像、高级网络编排或用户友好的 CLI 等上层功能而是将这些职责剥离出去自己则成为一个更专注、更稳定、性能开销更小的底层引擎。这种“专注”带来的好处是巨大的更清晰的架构边界、更易于被其他系统如 Kubernetes集成、更快的启动速度以及更少的安全攻击面。对于运维工程师、SRE 或者正在构建基于容器的平台开发者而言深入理解 containerd 不再是“可选项”而是“必选项”。无论是排查 Kubernetes 节点上诡异的容器启动失败还是优化大规模集群的镜像分发效率亦或是实现自定义的容器管理逻辑你最终都需要和 containerd 打交道。这份攻略的目的就是带你穿透 Docker 这层“舒适”的封装直接掌握容器生态中最核心的引擎让你在面对生产环境中的复杂容器问题时能够游刃有余。2. containerd 核心架构与组件拆解要驾驭 containerd首先得理解它的内部构造。containerd 采用客户端-服务器架构通过 gRPC API 对外提供服务这使得它天生就适合被集成到分布式系统中。2.1 守护进程containerd 与 runc当我们安装并启动 containerd 后系统中会运行一个名为containerd的守护进程。这个守护进程是大脑它管理着一切。但是它并不直接创建容器进程。当需要启动一个容器时containerd 会调用一个符合 OCIOpen Container Initiative标准的低级运行时最常用的就是runc。你可以把 containerd 看作项目经理而 runc 就是那个干具体活的工程师。containerd 负责制定计划准备 rootfs、配置网络、生成 OCI 运行时规范文件config.json然后 fork/exec 一个 runc 进程由 runc 根据规范文件去调用 Linux 内核的命名空间、cgroups 等能力最终创建出隔离的容器进程。这种分层设计非常清晰containerd 管理状态和生命周期runc 执行创建动作。2.2 核心服务Namespace, Content, Snapshottercontainerd 内部通过插件化的服务来组织功能其中几个最关键的服务是命名空间服务containerd 自身支持多租户隔离其顶层逻辑单元就是“命名空间”。不同的命名空间下的镜像、容器、网络等都是隔离的。Kubernetes 会为每个 Pod 创建一个独立的 containerd 命名空间通常以k8s.io作为默认命名空间这确保了不同 Pod 的资源不会相互干扰。你可以通过ctr namespace ls命令查看。内容服务负责所有不可变内容Blobs的存储与管理最主要的就是镜像层。镜像从仓库拉取下来后会被拆解成一个个的压缩数据块Blob存储在内容存储区。这个服务不关心这些数据是什么只负责存储、校验和寻址。快照服务这是理解容器存储效率的关键。当我们基于一个镜像创建容器时并不是把镜像的所有层都完整拷贝一份。快照服务会为镜像的每一层创建一个“只读快照”并为容器创建一个基于这些只读快照的“可写层”即容器层。所有容器层共享底层的只读镜像数据只有写入的数据才会保存在自己的可写层中。这种写时复制机制极大地节省了磁盘空间并加速了容器的创建。containerd 支持多种快照器如overlayfs最常用、devmapper、zfs等通过配置文件中的snapshotter字段指定。2.3 插件系统与 CRI 插件containerd 的强大之处在于其高度可扩展的插件系统。几乎所有的核心功能如运行时、快照器、内容存储后端甚至是一些监控指标暴露都是以插件形式实现的。对于 Kubernetes 用户而言最重要的插件莫过于CRIContainer Runtime Interface插件。这个插件实现了 Kubernetes CRI 定义的所有 gRPC 接口如RuntimeService和ImageService。当 kubelet 需要管理 Pod 和容器时它不再通过 Docker而是直接通过 CRI 插件与 containerd 通信。这个插件负责将 Kubernetes Pod 的概念包含多个容器共享网络等翻译成 containerd 能理解的单个容器操作并协调它们之间的关系。因此在 Kubernetes 节点上你的 containerd 配置中必须启用并正确配置 CRI 插件。3. 实战部署与基础配置指南理论了解之后我们进入实战环节。我们将从零开始在一个干净的 Linux 节点上部署 containerd并进行基础配置。3.1 安装与版本选择目前获取 containerd 最推荐的方式是通过其官方 GitHub Release 页面下载静态编译的二进制包或者使用各大 Linux 发行版的包管理器。以 Ubuntu 22.04 为例使用官方二进制包安装# 下载最新稳定版 containerd请替换为实际版本号 export VERSION1.7.0 wget https://github.com/containerd/containerd/releases/download/v${VERSION}/containerd-${VERSION}-linux-amd64.tar.gz # 解压到系统目录 sudo tar Cxzvf /usr/local containerd-${VERSION}-linux-amd64.tar.gz # 下载并安装 runc wget https://github.com/opencontainers/runc/releases/download/v1.1.0/runc.amd64 sudo install -m 755 runc.amd64 /usr/local/sbin/runc # 下载并安装 CNI 插件用于容器网络 export CNI_VERSION1.3.0 sudo mkdir -p /opt/cni/bin curl -L https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz | sudo tar -C /opt/cni/bin -xz版本选择建议生产环境务必使用稳定版。可以关注 GitHub Release 页面的标签通常1.6.x1.7.x这样的主版本号加小版本号的系列是长期支持版本。避免使用带有-rc候选版本或-beta后缀的版本。3.2 生成与解读默认配置文件containerd 的配置文件默认路径是/etc/containerd/config.toml。如果该文件不存在我们可以让 containerd 自己生成一个默认配置sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml让我们解读几个关键配置段# 根目录存放 containerd 的持久化数据如容器状态、镜像内容、命名空间元数据等。 root /var/lib/containerd # 状态目录存放运行时产生的临时状态信息如容器标准输入输出管道。 state /run/containerd # 指定默认的 OCI 运行时这里使用的是 runc。 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] runtime_type io.containerd.runc.v2 # 指定默认的快照器overlayfs 是性能与兼容性最平衡的选择。 [plugins.io.containerd.grpc.v1.cri.containerd] snapshotter overlayfs # 禁用拉取镜像时的 TLS 验证仅用于测试私有仓库生产环境应配置证书 [plugins.io.containerd.grpc.v1.cri.registry.configs.myregistry.local:5000.tls] insecure_skip_verify true注意insecure_skip_verify true是一个安全隐患仅在内部测试且网络环境绝对安全时使用。对于生产环境的私有仓库正确的做法是配置 CA 证书。将仓库的 CA 证书放置于/etc/containerd/certs.d/仓库地址/目录下containerd 会自动识别。3.3 配置 systemd 并启动服务为了让 containerd 作为守护进程运行我们需要一个 systemd 服务文件。containerd 的 release 包中通常包含一个containerd.service文件我们需要将其放到正确的位置。# 将服务文件复制到 systemd 目录 sudo cp /usr/local/lib/systemd/system/containerd.service /etc/systemd/system/ # 重新加载 systemd 配置 sudo systemctl daemon-reload # 设置开机自启 sudo systemctl enable --now containerd # 检查服务状态 sudo systemctl status containerd如果状态显示为active (running)恭喜你containerd 已经成功运行。你可以使用自带的命令行工具ctr进行初步验证# 查看 containerd 版本 sudo ctr version # 列出命名空间初始可能只有‘default’ sudo ctr namespace ls4. 深入镜像管理拉取、导出与垃圾回收镜像是一切容器的基础。containerd 的镜像管理逻辑清晰但不同于 Docker需要适应。4.1 使用ctr管理镜像ctr是 containerd 自带的命令行工具功能相对基础适合调试和简单操作。拉取镜像# 拉取一个镜像到 ‘docker.io’ 仓库的 ‘library’ 命名空间下 sudo ctr image pull docker.io/library/nginx:alpine这里需要完整的镜像地址。docker.io是仓库地址library是组织名对于官方镜像nginx是镜像名alpine是标签。列出镜像sudo ctr image ls你会看到镜像以仓库地址/命名空间/镜像名:标签的格式显示。导出与导入镜像这是ctr非常实用的功能常用于离线环境部署。# 导出镜像为 tar 包 sudo ctr image export nginx.tar docker.io/library/nginx:alpine # 在另一台机器导入 sudo ctr image import nginx.tar删除镜像sudo ctr image rm docker.io/library/nginx:alpine4.2 镜像存储与垃圾回收原理containerd 的镜像存储分为两部分元数据在/var/lib/containerd/io.containerd.content.v1.content中和内容数据块Blob在/var/lib/containerd/io.containerd.snapshotter.v1.snapshotter等目录中。当你删除一个镜像时只是删除了它的元数据标签Tag其底层的数据块可能还被其他镜像引用着因此不会立即被物理删除。这就引出了垃圾回收的概念。containerd 需要定期清理那些没有被任何镜像引用的“悬空”数据块。垃圾回收有两种触发方式手动触发使用ctr命令。# 清理未被任何内容引用的数据块 sudo ctr content gc # 更激进的清理包括未被任何容器使用的快照 sudo ctr snapshots prune自动触发在config.toml中配置。[plugins.io.containerd.gc.v1.scheduler] deletion_threshold 0 mutation_threshold 100 pause_threshold 0.02 schedule_delay 0s startup_delay 100s这个调度器插件会在后台定期执行垃圾回收。deletion_threshold为 0 表示一旦有内容可删除就立即执行。在生产环境中你可能需要调整这些阈值避免在高峰时段进行密集的磁盘删除操作影响性能。实操心得在磁盘空间紧张的节点上镜像删除后空间未释放是常见问题。首先用ctr image ls确认镜像已删然后用df -h和du -sh /var/lib/containerd/*对比查看如果发现content或snapshotter目录仍然很大大概率是垃圾回收未执行。手动运行ctr content gc和ctr snapshots prune通常能立即回收空间。建议在业务低峰期配置自动 GC。4.3 配置私有镜像仓库对接私有仓库是生产环境必备技能。假设我们有一个自签证书的私有仓库myregistry.local:5000。放置 CA 证书如果仓库使用自签名证书需要将其 CA 证书放到 containerd 的信任目录。sudo mkdir -p /etc/containerd/certs.d/myregistry.local:5000 # 将你的 ca.crt 文件复制进去 sudo cp ca.crt /etc/containerd/certs.d/myregistry.local:5000/containerd 会读取这个目录下的.crt文件作为可信根证书。配置仓库认证如果需要用户名密码认证需要配置config.toml。[plugins.io.containerd.grpc.v1.cri.registry.configs.myregistry.local:5000.auth] username myuser password mypassword更安全的做法是使用config.toml引用外部文件或者使用 Docker 的config.json。containerd 的 CRI 插件可以兼容 Docker 的认证配置它会尝试从$HOME/.docker/config.json读取认证信息。对于系统服务可以将该文件放在 kubelet 或 containerd 运行用户的 home 目录下。5. 容器生命周期管理实战掌握了镜像我们来操作容器。这里我们分别用ctr和crictlKubernetes CRI 工具来演示。5.1 使用ctr运行容器ctr运行容器的命令比docker run更底层需要明确指定更多参数。# 1. 创建一个容器 # 这里我们显式指定了快照器snapshotter和运行时runtime sudo ctr container create \ --snapshotter overlayfs \ --runtime io.containerd.runc.v2 \ docker.io/library/nginx:alpine \ my-nginx # 2. 启动容器 sudo ctr task start my-nginx # 3. 查看任务即运行中的容器 sudo ctr task ls # 4. 连接到容器的控制台标准输入输出 sudo ctr task attach my-nginx # 按 CtrlP, CtrlQ 可以退出 attach 而不停止容器 # 5. 在容器内执行命令 sudo ctr task exec --exec-id my-exec-1 -t my-nginx sh # 这会打开一个交互式 shell # 6. 停止和删除容器 sudo ctr task kill my-nginx # 发送信号默认 SIGTERM sudo ctr task rm my-nginx # 删除任务 sudo ctr container rm my-nginx # 删除容器定义需要注意ctr创建的容器是“单容器”视角没有 Docker 那种端口映射、自定义网络等高级功能。它更适合于测试 containerd 本身的功能是否正常。5.2 使用crictl模拟 Kubernetes 行为crictl是 CRI 兼容的命令行工具它的操作模式更贴近 Kubernetes。在配置好 containerd 的 CRI 插件后我们可以用它来操作。首先需要配置crictl连接到 containerd 的 CRI 服务端点。创建或编辑/etc/crictl.yamlruntime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 pull-image-on-create: false然后就可以进行类似docker的操作了# 拉取镜像 sudo crictl pull nginx:alpine # 创建 Pod沙箱。Pod 是 Kubernetes 的概念包含一组共享网络的容器。 # 这里需要提供一个 Pod 配置 JSON 文件内容较多通常由 kubelet 生成。 # 我们略过此步直接创建容器。 # 创建容器需要指定一个不存在的 Pod ID 用于测试实际操作中由 kubelet 管理 POD_ID$(sudo crictl runp dummy-pod-config.json) # 假设有一个配置文件 CONTAINER_ID$(sudo crictl create $POD_ID container-config.json pod-config.json) # 启动容器 sudo crictl start $CONTAINER_ID # 查看容器 sudo crictl ps # 查看容器日志 sudo crictl logs $CONTAINER_ID # 执行命令 sudo crictl exec $CONTAINER_ID ls / # 停止和删除 sudo crictl stop $CONTAINER_ID sudo crictl rm $CONTAINER_ID sudo crictl stopp $POD_ID sudo crictl rmp $POD_ID通过crictl你可以模拟 kubelet 的行为这对于调试 Kubernetes 节点上 Pod 无法启动的问题极其有用。例如当kubectl describe pod显示CreateContainerError时你可以用crictl在对应节点上直接尝试拉取镜像或创建容器从而快速定位是镜像问题、配置问题还是运行时问题。5.3 容器日志管理containerd 默认将容器的标准输出和标准错误stdout/stderr以二进制格式存储在/var/log/pods/和/var/log/containers/当通过 CRI 运行时目录下。这些日志文件会被 Kubernetes 的日志代理如 Fluentd、Filebeat收集。对于通过ctr直接运行的容器其日志可以通过ctr task logs命令查看sudo ctr task logs my-nginx但更常见的需求是配置日志驱动和轮转。这需要在config.toml中配置 CRI 插件的日志选项[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] # 使用 systemd 作为 cgroup 驱动与 kubelet 配置保持一致 SystemdCgroup true [plugins.io.containerd.grpc.v1.cri.containerd] # 禁用特权容器安全加固 disable_privileged_ports true # 日志配置 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] # 日志目录通常保持默认 # Root /var/lib/containerd/runc对于日志轮转通常不在 containerd 层面配置而是依赖系统的logrotate服务来管理/var/log/containers/*.log文件。6. 生产环境调优与故障排查将 containerd 投入生产意味着要面对性能、稳定性和各种疑难杂症。本章节分享一些关键的调优点和排查思路。6.1 关键配置调优系统参数调优在/etc/sysctl.d/99-containerd.conf中设置以下参数然后执行sysctl --system。# 提高网络连接跟踪表大小应对大量容器网络连接 net.netfilter.nf_conntrack_max 1048576 # 允许转发容器网络必需 net.ipv4.ip_forward 1 # 优化网络性能 net.core.somaxconn 32768 net.ipv4.tcp_tw_reuse 1containerd 配置调优# 在 config.toml 的 [debug] 部分调整日志级别生产环境建议 info 或 warn [debug] level warn # 调整 gRPC 服务的监听地址和参数 [grpc] address /run/containerd/containerd.sock # 增大 gRPC 消息大小限制应对大镜像或复杂配置 max_recv_message_size 16777216 max_send_message_size 16777216 [plugins.io.containerd.grpc.v1.cri] # 禁用不需要的插件减少内存占用 disable_apparmor true # 如果不用 AppArmor restrict_oom_score_adj true # 限制容器调整 OOM 分数增强稳定性 # 流式服务器配置影响 exec 和 logs 性能 stream_server_address 127.0.0.1 stream_server_port 0 enable_selinux false # 如果不用 SELinux镜像拉取并行度与重试在config.toml的 CRI 插件部分可以配置拉取镜像的并发数和重试策略这对从网络不稳定的仓库拉取大镜像很有帮助。6.2 常见故障排查命令与思路当容器或 Pod 出现问题时可以遵循以下排查路径检查 containerd 服务状态sudo systemctl status containerd -l。查看是否有错误日志。检查 containerd 运行时日志sudo journalctl -u containerd -f。这是最直接的错误信息来源。使用crictl检查运行时状态# 检查运行时服务是否就绪 sudo crictl info # 查看所有 Pod sudo crictl pods # 查看所有容器 sudo crictl ps -a # 查看镜像 sudo crictl images如果crictl info失败说明 CRI 服务未就绪检查 containerd 配置中 CRI 插件是否启用。容器启动失败如果crictl ps -a显示容器状态为Created或Exited使用sudo crictl inspect container_id查看详细状态和错误信息。常见错误包括镜像拉取失败网络问题、认证问题、镜像不存在。用crictl pull手动测试。启动命令错误检查容器配置中的Command和Args。权限不足容器要求特权模式或者宿主机路径挂载权限不对。运行时创建失败检查 runc 版本兼容性或查看dmesg中是否有内核相关报错如 seccomp 规则冲突。磁盘空间不足容器运行、镜像拉取、日志写入都需要磁盘空间。使用df -h和du -sh /var/lib/containerd/* /var/log/containers/检查关键目录。定期执行垃圾回收。网络问题Pod 内容器无法互访或无法访问外网。首先检查 CNI 插件是否安装正确 (ls /opt/cni/bin/)。然后检查 Pod 对应的网络命名空间和网卡sudo crictl inspectp pod_id | grep -A 10 -B 5 ipAddress获取 Pod IP再用nsenter命令进入网络命名空间排查。6.3 监控与度量指标containerd 暴露了丰富的 Prometheus 格式的度量指标这对于监控集群中容器运行时的健康状态至关重要。在config.toml中启用 metrics 收集[metrics] address 0.0.0.0:1338 # 设置一个监听地址和端口 grpc_histogram false重启 containerd 后就可以通过http://node-ip:1338/metrics访问指标。关键指标包括container_runtime_cpu_usage_seconds_total容器 CPU 使用时间。container_runtime_memory_usage_bytes容器内存使用量。container_runtime_operations_total各种运行时操作如创建、启动、停止的总次数。container_runtime_operations_errors_total运行时操作出错的次数。container_runtime_operations_duration_seconds运行时操作的耗时分布。将这些指标接入 Prometheus 和 Grafana可以建立容器运行时的监控大盘及时发现性能瓶颈或异常错误率的增长。从 Docker 的“黑盒”到 containerd 的“白盒”我们深入了解了容器运行时的核心引擎。这份攻略涵盖了从架构原理、部署配置、镜像与容器管理到生产调优和故障排查的全链路。掌握 containerd不仅能让你更好地运维 Kubernetes 集群更能让你在容器技术出现深水区问题时拥有从底层定位和解决问题的能力。记住工具本身在不断迭代但理解其核心设计思想和关键路径才是应对万变的不二法门。在实际操作中多使用ctr和crictl进行探索和验证将配置文件的变化与产生的现象关联起来你的 containerd 实战经验就会在一次次的问题解决中快速积累。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门