当前位置: 首页 > news >正文

Containerd容器运行时:从核心架构到Kubernetes生产实践

1. 容器运行时演进与Containerd的定位

如果你在运维Kubernetes集群,或者在生产环境中与Docker打过交道,那么“containerd”这个名字你一定不陌生。但很多时候,它就像一个默默无闻的幕后英雄,被Docker或Kubelet的光环所掩盖。简单来说,containerd是一个行业标准的容器运行时,它负责管理容器的完整生命周期——从镜像的拉取、解压,到容器的创建、启动、停止和删除,再到底层存储和网络命名空间的隔离。它不是一个直接面向最终用户的工具,而是一个被设计为嵌入到更大系统中的核心引擎。

为什么我们需要专门了解它?因为在现代云原生架构中,容器运行时的角色正在被清晰地解耦和标准化。早期,Docker Engine是一个“大而全”的解决方案,集成了运行时、构建、镜像管理、API和CLI。这种捆绑带来了便利,但也引入了复杂性、臃肿和潜在的维护负担。随着Kubernetes成为容器编排的事实标准,社区需要一个更轻量、更专注、更稳定的底层运行时。于是,containerd从Docker项目中孵化并捐赠给了云原生计算基金会(CNCF),成为了一个独立的顶级项目。今天,无论是Docker Desktop还是Kubernetes(通过CRI插件),其底层默认的运行时引擎都是containerd。理解containerd,就是理解现代容器技术的基石,它能帮助你在排查问题、优化性能、甚至构建自己的容器平台时,拥有更清晰的视野和更直接的控制力。

2. Containerd核心架构深度解析

要驾驭containerd,不能只停留在命令层面,必须对其内部架构有一个清晰的认知。它的设计遵循了“单一职责”和“模块化”原则,各个组件通过清晰的接口进行通信。

2.1 分层架构与核心组件

Containerd采用客户端-服务器架构,主要由以下几个核心层和组件构成:

  1. 客户端接口层:这是与containerd交互的入口。它提供了多种客户端协议,最常用的是gRPC API。Docker Engine、Kubernetes的kubelet(通过containerd-shim和CRI插件)都是通过这套gRPC API与containerd守护进程通信的。此外,它也提供了一个名为ctr的命令行工具,虽然功能不如dockerCLI丰富,但它是直接与daemon对话的“手术刀”,非常适合调试和深入操作。

  2. 核心服务层:这是containerd的大脑,运行在一个常驻守护进程(containerd)中。它内部又包含了多个关键服务:

    • 内容服务:管理所有不可变的内容,主要是镜像的层(Blobs)。它负责从镜像仓库拉取内容,并存储在本地的内容可寻址存储(CAS)中。
    • 镜像服务:管理镜像的元数据。它将镜像视为一个清单文件,该文件指向内容服务中的多个层,并包含配置信息。镜像服务不存储实际数据,只存储索引关系。
    • 容器服务:管理容器的元数据和生命周期。当创建一个容器时,容器服务会记录其配置(如要使用的镜像、启动命令、环境变量等),但此时并不运行任何进程。
    • 任务服务:这是真正让容器“动起来”的服务。它负责根据容器配置创建实际的进程(任务)。一个容器可以关联多个任务(例如,docker exec就会创建新任务),但主进程任务只有一个。
  3. 运行时层:任务服务在需要执行容器进程时,会调用底层的运行时。Containerd支持通过shim架构来适配不同的低级运行时。

    • containerd-shim:这是一个关键的设计。每个容器进程都由一个独立的shim进程管理。Shim作为容器进程的父进程,主要有几个作用:第一,它允许containerd daemon在启动容器后退出或重启,而不影响正在运行的容器(实现了daemon与容器的生命周期解耦);第二,它将容器的标准输入输出(stdio)转发到日志驱动(如json-file);第三,它负责收集容器退出后的状态并汇报给containerd。我们常用的runc就是通过containerd-shim-runc-v2来调用的。
    • 运行时:最常用的是runc,它是一个符合OCI(开放容器倡议)运行时标准的轻量级工具,直接利用Linux内核的cgroups和namespaces来创建隔离的容器环境。Containerd也支持其他运行时,如gVisor(安全沙箱)、Kata Containers(轻量级虚拟机)等,通过不同的shim来接入。
  4. 存储与快照:Containerd使用快照器来管理容器的根文件系统。当你拉取一个镜像时,它的每一层都会被作为快照存储起来。创建容器时,containerd会基于镜像的顶层快照,创建一个新的、可写的“容器层”快照(通常使用overlayfs驱动)。这个设计非常高效,因为镜像层是只读共享的,而每个容器的写入操作都发生在自己独立的可写层中。

2.2 与Docker Engine的关系辨析

很多人会混淆Docker和Containerd。你可以这样理解:Docker Engine = Containerd + 一系列增值服务(如构建工具docker build、镜像打包格式docker image、用户友好的CLIdocker、网络和卷管理的高级抽象等)。从Docker 1.11版本开始,Docker Engine的架构就演变为:dockerd(Docker Daemon) ->containerd->runcdockerd通过gRPC调用containerd,而containerd再去调用runc。因此,当你安装Docker时,其实也安装了Containerd。在Kubernetes场景下,为了追求更简洁和稳定的运行时,社区推荐直接使用Containerd,绕过Docker Engine这一层。

3. 从零开始:Containerd的安装与基础配置

理论清晰后,我们动手实践。这里以最常见的Linux发行版(如Ubuntu 20.04/22.04或CentOS 7/8)为例,介绍两种主流的安装方式。

3.1 安装方式选型:包管理器与二进制部署

对于生产环境,我强烈建议使用操作系统厂商或容器项目官方提供的包管理器(如aptyum)进行安装。这能确保containerd与系统更好地集成,方便接收安全更新和系统维护。

通过APT安装(Debian/Ubuntu):

# 1. 安装必要的依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release # 2. 添加Docker官方GPG密钥(Containerd的包也在Docker仓库中) sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 3. 设置稳定版仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 4. 更新包索引并安装containerd.io sudo apt-get update sudo apt-get install -y containerd.io

通过YUM安装(RHEL/CentOS/Rocky Linux):

# 1. 安装yum-utils工具集 sudo yum install -y yum-utils # 2. 添加Docker官方仓库 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 3. 安装containerd.io sudo yum install -y containerd.io

安装完成后,containerd的systemd服务单元会自动创建,但默认的配置文件可能不存在,我们需要生成一个。

3.2 生成与解读默认配置文件

Containerd的主配置文件默认位于/etc/containerd/config.toml。如果该文件不存在,我们可以使用containerd命令生成一个默认配置。

# 停止containerd服务(如果正在运行) sudo systemctl stop containerd # 备份可能存在的旧配置(如果有) sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.bak 2>/dev/null || true # 生成默认配置 sudo containerd config default | sudo tee /etc/containerd/config.toml

现在,让我们打开这个配置文件,看看几个最关键的配置节:

# /etc/containerd/config.toml 关键部分解读 version = 2 # 根目录,存放containerd的持久化数据 root = "/var/lib/containerd" # 状态目录,存放运行时状态信息 state = "/run/containerd" # grpc配置,定义API服务的监听地址 [grpc] address = "/run/containerd/containerd.sock" # 注意:默认是Unix Socket,Kubelet需要通过这个Socket与containerd通信 # 配置使用Systemd作为cgroup驱动,这对与Kubernetes集成至关重要 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] ... [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true # 镜像仓库镜像配置,用于加速或替换默认仓库 [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://registry-1.docker.io"]

注意:对于要接入Kubernetes的节点,将SystemdCgroup设置为true是必须的,这需要与kubelet的cgroup驱动配置保持一致,否则Kubernetes将无法正确管理容器的资源。

3.3 应用配置并启动服务

生成并修改配置后,需要重启服务使其生效。

# 重新加载systemd配置 sudo systemctl daemon-reload # 启动containerd服务并设置开机自启 sudo systemctl enable --now containerd # 检查服务状态 sudo systemctl status containerd

如果状态显示为active (running),恭喜你,containerd已经成功运行。你可以使用自带的ctr工具进行验证:

# 查看containerd版本信息 sudo ctr version

4. 掌握核心操作:镜像与容器生命周期管理

虽然ctr命令不如docker命令直观,但它是理解containerd工作模型的绝佳工具。我们通过它来演练核心工作流。

4.1 使用ctr管理镜像

ctr命令默认需要root权限,因为它直接操作/run/containerd/containerd.sock

拉取镜像:

# 拉取一个nginx镜像 sudo ctr image pull docker.io/library/nginx:alpine

这里需要完整的镜像地址。docker.io/library/是官方镜像的命名空间。

列出镜像:

sudo ctr image list

你会看到镜像的名称、标签、大小和摘要等信息。

打标签和推送镜像:

# 为镜像打上一个新标签(例如,推送到私有仓库前) sudo ctr image tag docker.io/library/nginx:alpine myregistry.local:5000/nginx:my-tag # 推送镜像到私有仓库(需要提前登录,使用`ctr i pull`的认证方式配置) # sudo ctr image push myregistry.local:5000/nginx:my-tag --user <username>:<password>

删除镜像:

sudo ctr image remove docker.io/library/nginx:alpine

4.2 使用ctr管理容器与任务

在containerd中,“容器”和“任务”是两个阶段的概念,这比Docker的抽象更底层。

创建容器:

# 创建一个名为`mynginx`的容器,但此时它只是一个静态配置,没有运行进程。 sudo ctr container create docker.io/library/nginx:alpine mynginx

这个命令会在/var/lib/containerd/io.containerd.runtime.v2.task/default/下创建容器的运行时目录结构。

启动任务(运行容器):

# 为容器`mynginx`创建一个主任务(即启动容器进程) sudo ctr task start mynginx

现在,一个nginx进程就在容器中运行起来了。你可以用ctr task ls查看运行中的任务。

与任务交互:

# 在运行的任务中执行一个命令(类似于 docker exec) sudo ctr task exec --exec-id myexec1 mynginx sh # 暂停任务(发送SIGSTOP) sudo ctr task pause mynginx # 恢复任务 sudo ctr task resume mynginx

停止和删除:

# 停止任务(发送SIGTERM,默认等待10秒后发送SIGKILL) sudo ctr task kill mynginx # 或者先暂停再删除任务 sudo ctr task pause mynginx && sudo ctr task rm mynginx # 最后删除容器定义 sudo ctr container rm mynginx

实操心得ctr命令的参数顺序比较固定,通常是ctr [命名空间] [对象类型] [命令] [标识]。默认的命名空间是default。如果你操作失败,先检查对象(镜像、容器、任务)是否存在正确的命名空间下。对于生产环境,我们很少直接使用ctr,但它是排障的利器,比如当kubelet无法拉取镜像时,你可以用ctr image pull手动测试仓库连通性。

5. 生产环境集成:Containerd与Kubernetes

Containerd在Kubernetes生态中扮演着容器运行时接口(CRI)的实现者角色。kubelet通过一个名为containerd-shim的插件与containerd通信。

5.1 配置Kubelet使用Containerd

当你使用kubeadm初始化集群时,可以通过配置文件指定CRI运行时。

首先,创建一个kubeadm配置文件,例如kubeadm-config.yaml

apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration nodeRegistration: criSocket: unix:///run/containerd/containerd.sock # 关键配置,指向containerd的socket --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.0

然后使用此配置文件初始化集群:

sudo kubeadm init --config=kubeadm-config.yaml

对于已有的节点,你需要修改kubelet的配置。编辑/var/lib/kubelet/kubeadm-flags.env文件,确保--container-runtime-endpoint参数指向containerd的socket:

KUBELET_KUBEADM_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock ..."

修改后重启kubelet:

sudo systemctl restart kubelet

5.2 关键配置:CRI插件与cgroup驱动

确保containerd的CRI插件已启用且配置正确。前面生成的默认配置已经包含了CRI插件。你需要重点关注的是cgroup驱动。在config.toml中确认:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true

同时,kubelet也需要配置相同的cgroup驱动。通常可以通过在kubelet的启动参数中添加--cgroup-driver=systemd,或者由kubeadm自动检测并设置。

5.3 排查Kubernetes与Containerd集成问题

集成后最常见的问题集中在镜像拉取和容器创建上。

查看容器日志:当Pod状态异常时,除了kubectl logskubectl describe pod,你可以直接通过containerd查看更底层的日志。首先用crictl(Kubernetes的CRI调试工具)找到容器ID:

# 安装crictl # 列出所有Pod sudo crictl pods # 列出所有容器 sudo crictl ps -a # 查看特定容器的日志 sudo crictl logs <container-id>

crictl是比ctr更贴近Kubernetes视角的调试工具。

直接通过containerd查看:每个容器的日志默认存储在/var/log/pods//var/log/containers/下,但也可以通过containerd的插件配置重定向。更直接的方式是查看shim的日志,但shim日志默认不持久化。你可以调整containerd的日志级别为debug(临时用于排障),然后通过journalctl查看:

sudo journalctl -u containerd -f

6. 高级特性与生产调优

掌握了基础操作和集成后,我们来看看containerd的一些高级特性和生产环境调优点。

6.1 镜像优化:快照与存储驱动

Containerd支持多种快照器,最常用的是overlayfs。你可以通过配置选择不同的存储驱动。对于某些旧内核或特定发行版,可能需要使用devicemapperaufs,但overlayfs是性能最好、最推荐的选择。

config.toml中配置:

[plugins."io.containerd.grpc.v1.cri".containerd] snapshotter = "overlayfs" disable_snapshot_annotations = false

注意事项:切换快照器是一个危险操作,因为它涉及底层存储结构的变更。通常应在初次安装时确定,后期切换可能需要迁移现有镜像和容器,操作复杂且易丢失数据。

6.2 资源限制与隔离配置

虽然资源限制主要由Kubernetes通过CRI指定,但containerd底层通过runc实现。你可以在config.toml中为runc运行时配置默认的cgroup路径、root路径等。更常见的做法是在Kubernetes的Pod Spec中定义resources.limitsresources.requests,containerd会忠实地通过runc应用这些cgroup限制。

6.3 网络集成模式

Containerd本身不管理网络,容器的网络命名空间由它创建,但网络配置(如IP分配、网卡创建)由外部的CNI(容器网络接口)插件负责。当containerd通过CRI创建容器时,它会调用配置好的CNI插件来配置网络。因此,网络问题的排查通常需要结合CNI插件(如Calico、Flannel)的日志和状态。

6.4 日志管理策略

默认情况下,容器日志通过containerd-shim被捕获并写入到stdout/stderr,然后由kubelet收集。你可以配置containerd使用不同的日志驱动,例如json-file(默认)或journald。在生产环境中,为了集中管理日志,通常会部署如Fluentd、Filebeat等边车容器或DaemonSet,从/var/log/containers/目录收集日志并发送到Elasticsearch等后端。

config.toml中可以调整CRI插件的日志配置:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] ... [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] ... # 限制单个容器日志文件大小和数量,防止磁盘被撑爆 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] ... # 这些是runc的选项,但日志限制通常由kubelet或上层配置

更关键的日志限制是在Kubernetes层面,通过kubelet参数--container-log-max-size--container-log-max-files来控制。

7. 故障诊断与日常运维命令手册

即使配置无误,在生产环境中也会遇到各种问题。这里整理一份实用的诊断清单和命令。

7.1 常见问题与排查路径

问题一:Pod状态一直为ContainerCreatingPending

  • 排查思路
    1. 检查节点资源kubectl describe node <node-name>,看是否资源不足。
    2. 检查镜像拉取kubectl describe pod <pod-name>,查看Events部分。如果提示镜像拉取失败,到对应节点上手动用ctr image pull测试。
    3. 检查容器运行时:在节点上执行sudo crictl ps -a,看容器是否被创建。执行sudo systemctl status containerdsudo journalctl -u containerd -n 50 --no-pager查看containerd服务状态和近期日志。
    4. 检查CNI网络:如果Events中有网络相关错误,检查CNI插件状态,如sudo journalctl -u kubelet | grep -i cni

问题二:容器运行后立即退出(CrashLoopBackOff)。

  • 排查思路
    1. 查看应用日志kubectl logs <pod-name> --previous(查看前一个容器的日志)。
    2. 检查容器启动命令和参数kubectl describe pod <pod-name>,确认commandargs是否正确。
    3. 检查容器内进程:如果可能,在Pod Spec中为容器添加一个sleep infinity的sidecar或使用kubectl debug进入容器排查。
    4. 检查运行时配置:确认containerd的runc运行时配置无误,特别是当使用非默认运行时(如Kata)时。

问题三:磁盘空间不足。

  • 排查思路
    1. 清理未使用的镜像sudo ctr images ls查看,使用sudo ctr images rm <ref>删除。
    2. 清理containerd内部数据:containerd提供了ctr contentctr snapshot命令来管理内容和快照,但清理需谨慎。更安全的方式是使用crictlsudo crictl rmi --prune
    3. 调整日志轮转策略:如前所述,确保kubelet的容器日志大小限制已配置。

7.2 实用运维命令速查表

场景命令说明
服务管理sudo systemctl status/restart/stop containerd管理containerd守护进程
查看版本sudo ctr version查看containerd和runc版本
镜像操作sudo ctr image pull/push/list/tag/rm拉取、推送、列表、打标签、删除镜像
容器操作sudo ctr container create/ls/info/rm创建、列表、查看信息、删除容器
任务操作sudo ctr task start/ls/exec/pause/resume/kill/rm启动、列表、执行命令、暂停、恢复、停止、删除任务
命名空间sudo ctr ns ls列出所有命名空间(默认是default,k8s用的是k8s.io
K8s视角调试sudo crictl ps/pods/info/stats/logs通过CRI接口查看容器、Pod、信息、状态、日志
内容与快照sudo ctr content ls
sudo ctr snapshot ls
查看拉取的镜像层内容、查看快照(谨慎操作)
日志查看sudo journalctl -u containerd -f -n 100实时查看containerd服务日志

7.3 性能监控与健康检查

Containerd暴露了metrics接口,可以与Prometheus集成进行监控。默认情况下,metrics端点未开启。你可以在config.toml中启用:

[metrics] address = "0.0.0.0:1338" # 设置一个监听地址和端口 grpc_histogram = false

启用后,你可以通过http://<node-ip>:1338/metrics获取监控指标,如容器创建耗时、镜像拉取计数、运行时操作错误等,这对于构建生产监控大盘至关重要。

最后,关于containerd的升级,我个人的经验是:在测试环境充分验证,关注版本变更日志中关于CRI插件和配置结构的改动。升级时,先滚动升级工作节点,确保业务Pod能平滑迁移,最后再处理控制平面节点。每次变更配置后,养成用sudo containerd config validate(如果版本支持)或至少用sudo systemctl restart containerd后观察日志的好习惯,将问题扼杀在启动阶段。

http://www.jsqmd.com/news/1391297/

相关文章:

  • 如何快速识别Go二进制文件?AlphaGolang的YARA规则实战教程
  • iPhone USB网络共享驱动装不上?一行PowerShell命令3分钟搞定
  • Django入门实战:从零搭建Python Web项目骨架与环境配置
  • 如何提升网站流量和转化率以及提高网站收录与权重? - 甄选测评馆
  • BT下载总连不上源?用这份公共Tracker清单,三分钟让种子活过来
  • 终极指南:Maps SDK for Unity 3D地图可视化完全上手教程
  • STM32+VScode+CubeMX(官方VS code插件/EIDE插件)开发
  • K8s NodePort 端口冲突排查实战:从报错端口被占到 iptables 残留规则清理
  • PDFPatcher完整上手指南:从零解决PDF书签、页面与批量处理难题
  • KMS_VL_ALL_AIO 使用教程:3分钟完成 Windows 与 Office 一键激活
  • 5分钟搞定DeepL翻译Chrome插件:免费API密钥配置与划词翻译完整教程
  • 从零到一搭建属于自己的数字名片:一份关于网站建设与管理课程报告的深度复盘与实战心得
  • 消息刚发出就被撤回?RevokeMsgPatcher防撤回工具实测:3步让微信/QQ/TIM的撤回键失效
  • 零基础3小时让旧款Mac重获新生:OpenCore Legacy Patcher免费升级最新macOS实战指南
  • 行业内口碑好的发酵辣椒粕供应商口碑 - 米諾
  • Topit:macOS窗口置顶工具全攻略,轻松告别窗口遮挡烦恼
  • 从零搭建Web服务器:Python、Nginx、XAMPP实战与域名解析详解
  • 2026年六安抖音代运营正规服务商中网创信选型指南:服务模式、内容体系与长期价值 - 中国品牌价值观察网
  • 2026年8月最新消息宁波坳沟注浆加固技术路基隐患别硬扛,邦如注浆稳四方-邦如工程 - 行业甄选汇
  • 抖音批量下载实战:douyin-downloader 的四场景用法与素材库搭建
  • 2026空气净化器选购终极横评|6大品牌实测对比,无滤网技术打破传统滤网二次污染困局 - 互联网科技品牌测评
  • 抖音无水印下载实用指南:一个开源工具,从单条视频到整页批量全搞定
  • 打造沉浸式体验:专业汽车城网站建设方案助力数字化转型与品牌升级
  • 差分法详解:从数学原理到数据处理的实战应用
  • Trolol系统兼容性解析:Linux与Mac平台功能对比
  • 廊坊LV包回收就来毓典奢品汇 15369396611,闲置贵重物品回收指南 - mazhaoyun11
  • ROS Launch文件实战:集成YOLO与Python节点实现机械臂视觉控制
  • 2026香港协助公司注册本土持牌服务商盘点:正规性筛选、适配场景解析与签约避坑指南 - 商业大观
  • 2026年污水管非开挖修复供应商全景解析:沈阳盖德橡胶制品有限公司工艺实力透视 - 卓企推荐
  • Linux系统密码遗忘应急指南:GRUB2单用户模式重置root密码全解析