Linux container runtime 的 cgroup-driver=systemd vs cgroupfs 统一迁移

3次阅读

这是典型的 cgroup 驱动不匹配问题:宿主机用 systemd 管理 cgroups,但 kubelet 配置为 cgroupfs,导致无法获取根 cgroup 统计,进而使资源限制、oom 判定等功能失效。

Linux container runtime 的 cgroup-driver=systemd vs cgroupfs 统一迁移

为什么 kubelet 启动失败并报 “failed to run Kubelet: failed to get root cgroup stats”?

这是典型的 cgroup 驱动不匹配问题:宿主机用 systemd 管理 cgroups(现代主流发行版默认),但 kubelet 却配置成 cgroupfs,导致它找不到正确的 cgroup 根路径,连带容器运行时(如 containerd)的统计、OOM 判定、CPU/内存限制全部失效。

  • 检查当前系统实际使用的 cgroup 版本:stat -fc %T /sys/fs/cgroup —— 若输出 cgroup2fs,说明是 cgroup v2 + systemd;若为 cgroupfs 且目录下有 systemd 子目录,大概率是 cgroup v1 + systemd 混合模式
  • kubelet--cgroup-driver 必须和 containerdconfig.toml[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 下的 SystemdCgroup = true/false 严格一致
  • 常见错误:只改 kubelet 参数,忘了同步改 containerd 配置,结果 kubelet 起来了,但 Pod 一直卡在 ContainerCreating

如何安全地把已运行集群从 cgroupfs 迁移到 systemd

不能直接改参数重启,否则所有 Pod 的 cgroup 路径会断开,资源限制立即失效,可能触发批量 OOM 或 CPU 抢占异常。

  • 先确认 containerd 已启用 systemd cgroup:编辑 /etc/containerd/config.toml,确保 SystemdCgroup = true,然后执行 sudo systemctl restart containerd
  • 再更新 kubelet:修改 /var/lib/kubelet/kubeadm-flags.env(或 systemd unit 文件中的 Environment="KUBELET_EXTRA_ARGS=..."),加入 --cgroup-driver=systemd
  • 必须滚动重启节点:逐台 drain → 停 kubelet → 清空 /var/lib/kubelet/cgroup(如有)→ 启 kubelet → 等 Ready → uncordon。跳过清空步骤可能导致旧 cgroup 目录残留,kubelet 统计错乱

systemd cgroup driver 在 cgroup v2 下有哪些实际差异?

cgroup v2 是单层树、统一接口,而 systemd 驱动在 v2 下不再依赖 /sys/fs/cgroup/<controller>/kubepods/...</controller> 这类传统路径,而是通过 systemd unit 名称(如 kubepods-burstable-podxxx.slice)组织层级,这对监控和调试带来变化。

  • crictl statskubectl top pod 仍可用,但底层数据来自 systemd 的 dbus 接口或 /sys/fs/cgroup/cgroup.procs,不是传统文件读取
  • 手动 inspect cgroup:用 systemctl status <unit-name></unit-name> 查资源使用,或 cat /sys/fs/cgroup/<unit-name>/cpu.max</unit-name>(v2)代替旧版 cpu.cfs_quota_us
  • 某些老版本监控 agent(如早期 cadvisor)对 v2 + systemd 支持不完整,可能显示 0 值或 panic,需升级到 v0.47+

哪些场景下硬切 systemd 会出意料之外的问题?

不是所有环境都适合一刀切。尤其当节点混用多种运行时(比如同时跑 dockerpodman、containerd),或使用自定义 cgroup 路径时,systemd 驱动可能绕过你预设的隔离逻辑。

  • Docker 用户注意:Docker CE 24.0+ 默认用 systemd cgroup,但若你还在用 dockerd --exec-opt native.cgroupdriver=cgroupfs,和 kubelet 的 systemd 驱动共存会导致 cgroup 层级冲突
  • 内核参数未调优:cgroup v2 + systemd 要求 systemd.unified_cgroup_hierarchy=1,若 BIOS/UEFI 启用了 legacy cgroup,该参数会被忽略,kubelet 会静默回退到 cgroup v1 行为,但日志不报错
  • SElinux 强制模式下,systemd 创建的 cgroup slice 可能被策略拒绝写入,表现为 Pod 启动超时,需检查 ausearch -m avc -ts recent

迁移的核心不是改一个参数,而是确认整个 cgroup 生命周期——从内核挂载、systemd 初始化、containerd 创建 runtime、到 kubelet 分配 pod scope——全部落在同一套语义里。少一环,就可能在半夜收到 CPU 使用率突增告警,却查不到是哪个 Pod 在啃资源。

text=ZqhQzanResources