尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

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采用客户端-服务器架构主要由以下几个核心层和组件构成客户端接口层这是与containerd交互的入口。它提供了多种客户端协议最常用的是gRPC API。Docker Engine、Kubernetes的kubelet通过containerd-shim和CRI插件都是通过这套gRPC API与containerd守护进程通信的。此外它也提供了一个名为ctr的命令行工具虽然功能不如dockerCLI丰富但它是直接与daemon对话的“手术刀”非常适合调试和深入操作。核心服务层这是containerd的大脑运行在一个常驻守护进程containerd中。它内部又包含了多个关键服务内容服务管理所有不可变的内容主要是镜像的层Blobs。它负责从镜像仓库拉取内容并存储在本地的内容可寻址存储CAS中。镜像服务管理镜像的元数据。它将镜像视为一个清单文件该文件指向内容服务中的多个层并包含配置信息。镜像服务不存储实际数据只存储索引关系。容器服务管理容器的元数据和生命周期。当创建一个容器时容器服务会记录其配置如要使用的镜像、启动命令、环境变量等但此时并不运行任何进程。任务服务这是真正让容器“动起来”的服务。它负责根据容器配置创建实际的进程任务。一个容器可以关联多个任务例如docker exec就会创建新任务但主进程任务只有一个。运行时层任务服务在需要执行容器进程时会调用底层的运行时。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来接入。存储与快照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-runc。dockerd通过gRPC调用containerd而containerd再去调用runc。因此当你安装Docker时其实也安装了Containerd。在Kubernetes场景下为了追求更简洁和稳定的运行时社区推荐直接使用Containerd绕过Docker Engine这一层。3. 从零开始Containerd的安装与基础配置理论清晰后我们动手实践。这里以最常见的Linux发行版如Ubuntu 20.04/22.04或CentOS 7/8为例介绍两种主流的安装方式。3.1 安装方式选型包管理器与二进制部署对于生产环境我强烈建议使用操作系统厂商或容器项目官方提供的包管理器如apt或yum进行安装。这能确保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 SocketKubelet需要通过这个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 version4. 掌握核心操作镜像与容器生命周期管理虽然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:alpine4.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与KubernetesContainerd在Kubernetes生态中扮演着容器运行时接口CRI的实现者角色。kubelet通过一个名为containerd-shim的插件与containerd通信。5.1 配置Kubelet使用Containerd当你使用kubeadm初始化集群时可以通过配置文件指定CRI运行时。首先创建一个kubeadm配置文件例如kubeadm-config.yamlapiVersion: 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 --configkubeadm-config.yaml对于已有的节点你需要修改kubelet的配置。编辑/var/lib/kubelet/kubeadm-flags.env文件确保--container-runtime-endpoint参数指向containerd的socketKUBELET_KUBEADM_ARGS--container-runtimeremote --container-runtime-endpointunix:///run/containerd/containerd.sock ...修改后重启kubeletsudo systemctl restart kubelet5.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-driversystemd或者由kubeadm自动检测并设置。5.3 排查Kubernetes与Containerd集成问题集成后最常见的问题集中在镜像拉取和容器创建上。查看容器日志当Pod状态异常时除了kubectl logs和kubectl describe pod你可以直接通过containerd查看更底层的日志。首先用crictlKubernetes的CRI调试工具找到容器ID# 安装crictl # 列出所有Pod sudo crictl pods # 列出所有容器 sudo crictl ps -a # 查看特定容器的日志 sudo crictl logs container-idcrictl是比ctr更贴近Kubernetes视角的调试工具。直接通过containerd查看每个容器的日志默认存储在/var/log/pods/和/var/log/containers/下但也可以通过containerd的插件配置重定向。更直接的方式是查看shim的日志但shim日志默认不持久化。你可以调整containerd的日志级别为debug临时用于排障然后通过journalctl查看sudo journalctl -u containerd -f6. 高级特性与生产调优掌握了基础操作和集成后我们来看看containerd的一些高级特性和生产环境调优点。6.1 镜像优化快照与存储驱动Containerd支持多种快照器最常用的是overlayfs。你可以通过配置选择不同的存储驱动。对于某些旧内核或特定发行版可能需要使用devicemapper或aufs但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.limits和resources.requestscontainerd会忠实地通过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状态一直为ContainerCreating或Pending。排查思路检查节点资源kubectl describe node node-name看是否资源不足。检查镜像拉取kubectl describe pod pod-name查看Events部分。如果提示镜像拉取失败到对应节点上手动用ctr image pull测试。检查容器运行时在节点上执行sudo crictl ps -a看容器是否被创建。执行sudo systemctl status containerd和sudo journalctl -u containerd -n 50 --no-pager查看containerd服务状态和近期日志。检查CNI网络如果Events中有网络相关错误检查CNI插件状态如sudo journalctl -u kubelet | grep -i cni。问题二容器运行后立即退出CrashLoopBackOff。排查思路查看应用日志kubectl logs pod-name --previous查看前一个容器的日志。检查容器启动命令和参数kubectl describe pod pod-name确认command和args是否正确。检查容器内进程如果可能在Pod Spec中为容器添加一个sleep infinity的sidecar或使用kubectl debug进入容器排查。检查运行时配置确认containerd的runc运行时配置无误特别是当使用非默认运行时如Kata时。问题三磁盘空间不足。排查思路清理未使用的镜像sudo ctr images ls查看使用sudo ctr images rm ref删除。清理containerd内部数据containerd提供了ctr content和ctr snapshot命令来管理内容和快照但清理需谨慎。更安全的方式是使用crictlsudo crictl rmi --prune。调整日志轮转策略如前所述确保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列出所有命名空间默认是defaultk8s用的是k8s.ioK8s视角调试sudo crictl ps/pods/info/stats/logs通过CRI接口查看容器、Pod、信息、状态、日志内容与快照sudo ctr content lssudo 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后观察日志的好习惯将问题扼杀在启动阶段。
返回列表