ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

cri-containerd 1.7.23部署实战:Kubernetes节点运行时安装与避坑

cri-containerd 1.7.23部署实战:Kubernetes节点运行时安装与避坑 简介针对Kubernetes与容器化运维场景cri-containerd-1.7.23-linux-amd64.tar是一份完整的Containerd 1.7.23部署压缩包。Containerd是云原生计算基金会旗下的容器运行时标准项目与Kubernetes的CRI接口深度结合能够统一承担镜像拉取、存储管理、容器生命周期控制等关键职责。该版本面向Linux AMD64架构可部署在常见x86服务器上适合集群节点初始化、离线镜像导入及运行时环境重建等任务。压缩包内共整理19个文件主体为containerd、ctr、crictl、runc、containerd-shim等可执行程序同时辅以yaml配置、systemd服务模板、环境变量文件等整体体积101.22MB。解压后可参照标准目录结构将二进制与配置分别放置于etc、usr/local、opt等路径并通过systemd注册守护进程便于快速启动并保持节点运行时的稳定性。包内还附有旧版组件的弃用说明提前标注迁移与升级注意事项对版本迭代规划具有参考价值。此压缩包当前已有261人学习适合运维工程师与Kubernetes开发者用于容器运行时部署、CRI接口验证、离线环境搭建或故障排查前的环境准备。1. cri-containerd 解压即用先弄明白这个 tar 包在 Kubernetes 节点上扮演什么角色kubelet 只认 CRIContainer Runtime Interface协议。Kubernetes 自 1.24 把 dockershim 从 kubelet 里摘掉之后节点上要继续跑容器最主流的选择就是 containerd而 cri-containerd 正是 containerd 官方把 CRI 服务编译进产物后打出来的发布包。它不是一个安装脚本而是一整套运行时全家桶containerd 本体、runc、crictl 命令行、CNI 网络配置都放在一个 tar 里解压到对应目录并注册 systemd 服务就能被 kubelet 发现。1.7.23 是 containerd 1.7 长期维护分支的补丁版本linux-amd64 则限定了它只能跑在 x86_64 Linux 上。要替换 docker、离线搭集群、排查节点 NotReady都得先把这个包吃透。2. 装它之前内核模块、sysctl 和 cgroup 驱动先对一遍2.1 为什么要做前置检查containerd 不是装完就能跑很多文章的开头就是解压、启动但实际生产环境里翻车往往发生在更早的地方。containerd 本体是个守护进程真正创建容器的是 runc而 runc 启动容器时依赖内核的 overlayfs 和网络命名空间转发。如果你在内核里没加载 overlay 模块containerd 创建容器时会直接报类似failed to mount overlay: no such device的错误如果你忘了加载 br_netfilter后面 kubelet 和 CNI 插件做 iptables 规则时会发现 bridge 流量根本不经过 netfilter直接导致集群内 Pod 互通异常。这类问题不是改配置能救的必须在装运行时之前先把内核参数设好。另一件必须提前定下的事是 cgroup 驱动。containerd 和 kubelet 都通过 cgroup 来限制容器资源两者必须使用同一个驱动。实际环境里最常见的是 kubelet 被 kubeadm 写成了 systemd而 containerd 默认生成配置里SystemdCgroup false这两个值一冲突kubelet 启动时会直接报 misconfiguration节点根本进不了 Ready 状态。与其等那个时刻再去排查不如装包前就把方向定死只要节点用 systemd 作为 init就让 containerd 跟随 systemd。cgroup 驱动选型上没有绝对的对错关键是一致性。kubeadm 初始化的集群默认给 kubelet 写cgroupDriver: systemd如果你手工二进制部署 kubelet默认值则是 cgroupfs。我一般会把节点上所有跟容器相关的组件都统一到 systemd 这一侧因为在云厂商镜像和 CentOS、Ubuntu 这类发行版上systemd 是唯一稳定可预期的 cgroup 管理方。下面这张表能帮你快速判断自己的环境应该站在哪一边驱动管理方式谁在用常见场景systemd通过 systemd 的 cgroup 切片管理kubeadm 集成的 kubelet、新版 docker、containerd 1.7 推荐值系统以 systemd 为 init 的生产节点cgroupfs运行时直接写 sysfs 下的 cgroup 文件手工部署的 kubelet、旧版 docker容器化基础设施非 systemd 发行版不一致后果kubelet 无法正确识别容器的 cgroup 归属节点 NotReady、已有容器被反复杀掉混合驱动但未对齐的节点还有一个容易被忽略的前提Kubernetes 节点要求关闭 swap。kubelet 默认--fail-swap-ontrue只要 swap 分区的状态是 onkubelet 就直接拒绝启动。如果你只是单跑 containerd、不接 kubeletswap 可以不管但凡是准备加入集群的节点swapoff和改/etc/fstab这两步不能省。2.2 用一段 linux 脚本把 overlay 与 br_netfilter 放进内核下面这段配置是标准做法把需要加载的内核模块写进/etc/modules-load.d/让节点重启后依然自动加载同时用 modprobe 让当前会话立刻生效cat EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter lsmod | grep -E ^overlay|^br_netfilter这里用了 heredoc 把两行模块名写入配置文件再逐个 modprobe。sudo tee是以 root 身份创建文件的标准写法比echo 更可控不会出现因为权限不足导致写入失败。lsmod 的作用是确认两个模块真的加载进来了缺哪个就回到 modprobe 那一步单独检查不要在模块没加载的情况下继续往下走。接下来是 sysctl 参数。Kubernetes 官方文档里的99-kubernetes-cri.conf是一份现成清单直接抄过来用即可cat EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sudo sysctl --system三个参数各司其职前两个让桥接的 IPv4/IPv6 流量经过 iptables 处理这是 flannel、calico 这类 CNI 插件能把流量规则真正落到 bridge 设备上的前提ip_forward是容器访问外部网络的开关。注意sysctl --system会按/etc/sysctl.d/下所有文件依次加载比老式的sysctl -p /etc/sysctl.conf更全面也是现在主流发行版推荐的方式。执行完可以顺手跑sysctl net.ipv4.ip_forward确认输出是 1。最后关 swap并确保重启后不会回来sudo swapoff -a sudo sed -i / swap / s/^/#/ /etc/fstabswapoff -a只对当前会话生效/etc/fstab里如果没有注释掉 swap 挂载行重启后 swap 会重新启用。用sed -i / swap / s/^/#/把包含 swap 空格分隔的行直接注释掉比手工编辑更不容易漏。改完 fstab 后我建议检查一下文件内容确认没有误伤别的挂载项毕竟有些云盘的挂载行里也可能出现类似字样。到这一步内核和 cgroup 方向已经定好了可以开始解压并部署 cri-containerd。3. 把 cri-containerd-1.7.23-linux-amd64.tar 落到系统里3.1 tar 包里的目录布局一层 etc、一层 usr直接铺到根目录拿到cri-containerd-1.7.23-linux-amd64.tar这个文件第一步不是急着解压而是先看包里有什么。用 tar 的列表模式查看内容tar -tf cri-containerd-1.7.23-linux-amd64.tar | less输出里你会看到两棵顶层目录etc/和usr/。etc/下面带着 systemd 的 unit 文件路径形如etc/systemd/system/containerd.serviceusr/下面则是usr/local/bin/里面是 containerd、runc、crictl、ctr 这几个可执行文件。有些带 CNI 的打包版本还会有opt/cni/bin/这一层具体看你下载的包是不是带 CNI 插件的版本用tar -tf一眼就能确认。这种布局说明它不是一个需要交互安装的安装包而是一个镜像了根目录结构的文件集。解压时的基准目录定在/包内的etc/...就落到/etc/...usr/...落到/usr/...等于把发布物里的文件直接铺进了系统。这也是为什么我不推荐在 Windows 上下载这个 tar 后马上用解压工具展开看——tar 包内的符号链接和权限位在 NTFS 文件系统上会丢失等你传到 Linux 上再用反而多一层不确定性。常见的做法是在目标 Linux 机器本地或 WSL 里操作直接ls -l /usr/local/bin/containerd这类常用命令验证结果。3.2 用 tar -C / 解压并接管 systemd 服务解压命令非常简单但有一个关键参数不能漏sudo tar -C / -xvf cri-containerd-1.7.23-linux-amd64.tar-C /表示把当前目录切换到根目录再执行解压-x是解压-v显示过程-f指定文件。注意如果你拿到的是.tar.gz后缀的压缩包需要把参数改成-xzvf多出来的z表示先解 gzip。为什么用-C /而不是解压到临时目录后再手工拷贝因为 systemd unit 文件的预期路径、二进制文件的权限和 owner 都是按根目录结构打好的包直接落根目录可以完整保留这些元数据。手工cp容易漏掉 unit 文件或者把可执行文件拷成普通权限后面启动时会遇到一堆奇怪问题。解压完成之后加载 systemd 并启动sudo systemctl daemon-reload sudo systemctl enable --now containerd systemctl status containerd --no-pager --fulldaemon-reload让 systemd 重新扫描 /etc/systemd/system 下的 unit 文件这是新装服务必须做的一步不做的话后面 enable 会提示找不到服务。enable --now是 enable 和 start 的组合先建立开机自启的软链再立刻启动服务。containerd 启动后默认监听unix:///run/containerd/containerd.sock这个 socket 就是后续 kubelet 和 crictl 连接运行时的地方。3.3 解压后完整性和版本核对逐个验证二进制解压和启动只是第一步我还习惯把包里三个核心二进制分别核对一遍版本确认系统的 PATH 里优先找到的是新装的这一套containerd --version runc --version | head -2 crictl --versioncontainerd --version的输出应该包含版本号和 commitrunc 是低层运行时版本不能和 containerd 的依赖范围差太多crictl 是 CRI 客户端用于模拟 kubelet 对运行时的调用它和你用的 Kubernetes 版本之间没有严格绑定但有个能用的新版总是好的。如果这几个命令提示找不到文件常见原因是/usr/local/bin没有进入 PATH或者解压时没有用-C /导致二进制散落在当前目录。再确认一下服务单元文件实际执行的启动参数这一步对后面的配置排查很省事systemctl cat containerd输出里能看到ExecStart/usr/local/bin/containerd之类的完整命令行。注意观察里面有没有显式写--config参数如果没写containerd 会默认读/etc/containerd/config.toml如果写了说明 unit 文件指定了别的路径后面改配置的时候就得到那个路径去改。很多第 5 章的排查问题根源都在这一行参数上。4. 配置文件 config.toml 里三个必调参数否则 kubelet 不认这个运行时服务跑起来只是第一步真正让 kubelet 愿意使用这个运行时必须在 config.toml 里做对齐。containerd 安装时不会自动生成配置文件常见的做法是手动生成一份默认配置sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml /dev/nullconfig default会输出一份适用于当前 containerd 版本的完整默认配置包含 CRI 插件、registry、CNI 等所有区块。把它写到/etc/containerd/config.toml后再基于这份默认值做定制比自己从零写配置安全得多。接下来有三个参数是 kubelet 接入前必须检查的。4.1 sandbox_imagepause 镜像决定了第一个 Pod 能不能起来containerd 在创建每个 Pod 之前会先创建一个 sandbox 容器这个容器的镜像就是 pause。它持有 Pod 的网络命名空间其他业务容器通过加入这个命名空间来实现同 Pod 共享网络。config.toml 里对应的配置项是sandbox_image默认生成的值指向registry.k8s.io/pause:3.x。在你的节点能访问这个地址的环境下默认值没问题但在内网集群、离线环境或者网络受限的机房kubelet 创建第一个 Pod 时就会卡在拉取 pause 镜像这一步。我一般直接把 sandbox_image 改成当前网络可达的镜像仓库地址sudo sed -i s#sandbox_image registry.k8s.io/pause:3.*#sandbox_image registry.example.com/google_containers/pause:3.9#g /etc/containerd/config.toml grep sandbox_image /etc/containerd/config.tomlsed 命令里用了#作为分隔符避免和路径里的/混淆3.*匹配默认配置里的任意 3.x 小版本。registry.example.com/google_containers/pause:3.9这一步要替换成你实际可用的仓库地址可以是内网 Harbor也可以是云厂商的公共镜像仓库。改完之后的 grep 只是确认替换生效真正的验证要等后续用 crictl 或直接跑一个 Pod 才能看到。4.2 SystemdCgroup和 kubelet 的 cgroup 驱动必须一致如果你在节点上跑的是 systemd 发行版kubelet 是由 kubeadm 或者发行版初始化脚本管理的那么 kubelet 的 cgroup 驱动几乎都是 systemd。而 containerd 的默认配置里SystemdCgroup false也就是让 CRI 插件用 cgroupfs 去管理容器的 cgroup。这是一个典型的隐藏坑运行时能启动但 kubelet 虽然连上了 containerd在判断容器归属时会发现两边的路径不一致轻则节点 NotReady重则节点上的容器被反复重启。我的处理方式是一行 sed 改到位sudo sed -i s/SystemdCgroup false/SystemdCgroup true/g /etc/containerd/config.toml grep SystemdCgroup /etc/containerd/config.toml这里有两点提醒。第一不要只改 containerd 这一侧改完必须确认 kubelet 那侧确实是 systemd否则只是把不一致换了个方向。查看方式是在/var/lib/kubelet/config.yaml里找cgroupDriver字段或者看 kubelet 进程参数。第二如果你用的是三方的 containerd 集群管理工具有些工具自己管理 cgroup 驱动这时候要按工具的约定来不能机械地把这一项设为 true。4.3 镜像加速与内网仓库让生产节点的拉取路径可控sandbox_image 解决了 pause 镜像的问题但节点上后续所有业务镜像也都要走同一个注册表配置。config.toml 的默认配置里registry 区块已经为 docker.io 这类常用仓库定义了 endpoint。我建议在生产环境里把默认 endpoint 替换成内网能访问的镜像缓存或加速地址让节点上的镜像安装行为可控而不是让每个节点都直接访问公网仓库。默认生成的配置里对应段落结构如下[plugins.io.containerd.grpc.v1.cri.registry.mirrors] [plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io] endpoint [https://registry-1.docker.io]这个块是嵌套结构用 sed 去全局替换 endpoint 非常容易误伤其他仓库配置我一般是直接打开配置文件定位到这一段修改sudo sed -i /docker.io/a\ [plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io] /etc/containerd/config.toml上面的命令只是示意实际并不建议这样操作。最稳的方式还是用文本编辑器打开 config.toml找到 docker.io 对应的 endpoint 行把数组里的地址换成你内网 registry 的域名。注意镜像仓库的镜像同步策略内网仓库里必须预先同步好了需要的镜像节点拉取时才会命中缓存。把 endpoint 改成公网地址并不会自动加速只会在你的网络路径上加一跳。4.4 重启并确认生效配置而不是猜测config.toml 的所有修改只有重启 containerd 才会加载。改完配置后按顺序做这三步sudo systemctl restart containerd systemctl status containerd --no-pager containerd config dump | grep -E sandbox_image|SystemdCgroupconfig dump打印的是 containerd 进程当前内存里生效的配置和文件里的内容可能不完全一致。如果 grep 出来还是旧值说明文件没改对位置或者 containerd 启动时读的根本不是这个文件。回到 3.3 里的systemctl cat containerd检查--config参数再确认一次。这一步是后面所有 kubelet 接入操作是否顺畅的分水岭。5. 避坑专项cri-containerd 上线后最常见的 5 个故障现象与修复这一章写的都是我在带 Kubernetes 节点和排查 linux 运维故障案例时真实遇到过的场景。每个故障按现象、原因、解决三段式来写方便你对照排查。5.1 containerd 启动失败socket 被占用服务反复重启现象systemctl start containerd后服务状态很快变成 failed 或 active 后又退出journalctl -u containerd -n 50里出现bind: address already in use或者提示/run/containerd/containerd.sock创建失败。原因节点上可能残留了旧版本的 containerd 进程或者之前手工启动过 containerdsocket 文件还在但进程已经死了。解决先停掉所有旧的容器运行时相关进程再清理 socket 文件然后重启服务sudo systemctl stop containerd sudo rm -f /run/containerd/containerd.sock sudo systemctl start containerd如果确认没有其他进程占用但 socket 依然创建失败检查/run/containerd目录是否存在且属主是 root。这个目录如果被普通用户占用了containerd 启动时同样会失败。5.2 节点 NotReadyCNI 网络没就绪现象kubelet 能连上 containerd但节点一直 NotReadykubectl describe node显示NetworkReadyfalsecontainerd 日志里重复出现failed to load cni config或cni config load: no networks found。原因这个 tar 包里如果不带 CNI 插件解压后/opt/cni/bin是空的或者/etc/cni/net.d下没有网络配置文件。解决确认包内是否有 opt/cni/bin 目录没有的话需要单独安装你用的 CNI 插件并创建对应的网络配置。有配置但加载失败时先检查配置文件的扩展名必须是.conflist或.conf并且 JSON 格式没有语法错误。5.3 Pod 卡在 ContainerCreatingpause 镜像拉不下来现象新建 Pod 后事件里出现Failed to pull image registry.k8s.io/pause:3.x: failed to resolve reference或者超时。原因第 4.1 节里的 sandbox_image 没有替换节点访问不了默认的镜像仓库。这是离线环境最容易踩的一步因为 containerd 本身启动完全正常只有创建 Pod 时才暴露。解决把 sandbox_image 改成内网镜像仓库地址重启 containerd再手动用 crictl 拉一次 pause 镜像确认sudo crictl pull registry.example.com/google_containers/pause:3.9这一步能直接验证镜像仓库的网络连通性不用反复创建 Pod 试错。5.4 kubelet 报 cgroup driver 不一致现象kubelet 启动失败日志里出现kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs注意即使节点上已经没有 docker这个报错也可能出现因为 containerd 的 CRI 插件被 kubelet 当成了 docker 兼容层。原因kubelet 和 containerd 的 cgroup 驱动不在同一边。解决把 containerd 的SystemdCgroup改成和 kubelet 一致。如果 kubelet 那侧是 cgroupfs就把 4.2 里的修改反着做但我个人不建议动 kubelet尤其是 kubeadm 管理的节点因为 kubeadm 初始化时会把 systemd 写进 kubelet 配置改了 kubelet 下次 join 时还会被重置。5.5 改了 config.toml 不生效配置路径与版本差异现象明明改完了配置重启 containerd 后crictl info显示的还是旧值或者镜像拉取还是走老地址。原因有三个一是 systemd unit 文件的 ExecStart 里指定了别的--config路径二是改错了配置区块CRI 插件只认[plugins.io.containerd.grpc.v1.cri]下的配置写错段落就静默忽略三是 containerd 有缓存没有真正重启进程。解决先用systemctl cat containerd确认配置路径再用containerd config dump确认生效值。排查顺序固定为cat 看路径、dump 看生效值、restart 让配置落地三步做完基本能定位 90% 的配置不生效问题。6. 用 crictl 和 kubeadm 验证一套可用的容器运行时配置全部就位后不要急着去 kubeadm init先用 crictl 以 CRI 协议的视角验证一下当前运行时。crictl 是专门为 CRI 设计的客户端它看到的信息和 kubelet 看到的完全一致比直接使用 ctr 更有参考价值。sudo crictl config runtime-endpoint unix:///run/containerd/containerd.sock sudo crictl info第一条命令把 runtime-endpoint 指向 containerd 的 socket以后执行 crictl 就不用在每条命令后面带-r。第二条命令会输出一大段 JSON重点关注cgroupDriver字段是不是systemd以及sandboxImage是不是你改过的那个镜像地址。这两项验证通过说明 CRI 插件的配置已经在运行中的进程里生效了。接下来可以配合 kubeadm 对接集群。如果你的集群初始化命令没有显式传过容器运行时地址加上这一行参数避免 kubeadm 在探测时走错逻辑sudo kubeadm init \ --pod-network-cidr10.244.0.0/16 \ --container-runtime-endpointunix:///run/containerd/containerd.sock有两点需要说明。第一--container-runtime-endpoint在较新版本的 kubeadm 里其实已经是默认值但显式写出来能避免某些发行版或旧版本 kubeadm 默认找 docker socket 的行为属于无害的保险操作。第二--pod-network-cidr要和你后续选择的 CNI 插件保持一致这里用了 flannel 常见的 10.244.0.0/16如果你装 calico需要按 calico 的要求改。初始化完成后节点成为 Ready 需要先安装 CNI 插件。我习惯在做这一步之前先把 pause 镜像在节点上预热一遍防止第一个业务 Pod 卡在拉取镜像上sudo ctr -n k8s.io images pull registry.example.com/google_containers/pause:3.9这条命令通过 ctr 直接向 containerd 的 k8s.io 命名空间写入镜像kubelet 在创建 sandbox 时可以直接命中本地镜像不需要再走远程拉取。这个技巧对离线部署和镜像仓库带宽紧张的机房尤其有效。等 CNI 插件部署完成最后用kubectl get nodes -o wide查看节点状态。只要节点进入 Ready并且 crictl 能看到正在运行的 sandbox 容器这一套 cri-containerd 就算真正跑通了。回看我做过的环境凡是卡在 NotReady 的十有八九都出在前面第 2 章的内核参数和这一章的 sandbox 镜像配置上。后来我养成了一个习惯每到一个新环境先把第 2 章那段 linux 脚本跑一遍再改配置最后才去动 kubeadm。这套顺序帮我把上线时间从一整天压缩到一个小时以内希望也能帮到你。本文还有配套的精品资源点击获取
返回列表