在 Kubernetes 1.30+ 中如何强制启用 cgroups v2

解读

国内云原生面试中,“强制启用 cgroups v2” 并不是简单改一行配置,而是考察候选人是否理解:

  1. 节点操作系统内核与 systemd 版本是否已原生支持 v2(国内主流镜像如 Alibaba Cloud Linux 3、TencentOS 3、CentOS Stream 9 默认已开,但 CentOS 7、Ubuntu 18.04 仍需手动迁移);
  2. kubelet 与容器运行时的联动逻辑(kubelet 1.30+ 在检测到系统已统一挂载到 v2 时才会自动切换,否则回退到 v1);
  3. 强制“失败即拒绝调度”而非“静默回退”——这正是题目里“强制”二字的得分点。

如果仅回答“把 kubelet 的 --cgroup-driver 改成 systemd”只能拿到 30% 分数,必须给出“让节点无法加入集群直到 v2 就绪”的闭环方案才能打动面试官。

知识点

  • cgroups v2 统一层级(unified hierarchy):内核 4.15+ 且 systemd 231+ 才具备生产可用性;国内私有云常因存量 CentOS 7 导致混合层级。
  • kubelet 1.30+ 的 KubeletConfiguration 结构体:字段 cgroupDriver: systemd 仅声明期望,不会强制校验实际挂载类型;真正的强制点在 NodeAllocatable 依赖的 cgroupfs 路径。
  • 容器运行时
    • containerd[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true 必须与 v2 匹配;
    • Docker Engine 25.x/etc/docker/daemon.json"exec-opts": ["native.cgroupdriver=systemd"] 同样依赖 v2。
  • 准入控制:可使用 NodeRestriction + 自定义 Admission Webhook 在节点注册时检查 /proc/self/cgroup 是否全部为 0::/ 前缀,否则拒绝 kubelet register
  • 国内镜像源加速:修改 containerd 的 config_path 指向 registry.aliyuncs.com 不影响 cgroup 逻辑,但面试时提及可体现落地细节。

答案

步骤化“强制”方案,任何一步不满足即导致节点 NotReady,从而满足“强制”要求

  1. 操作系统级前置检查
    在节点启动脚本(cloud-init 或 Ansible)中加入:

    #!/bin/bash
    set -e
    # 1) 内核版本
    kernel=$(uname -r | cut -d- -f1)
    [ "$(printf '%s\n' 4.15 "$kernel" | sort -V | head -n1)" != 4.15 ] && exit 101
    # 2) systemd 版本
    systemctl --version | awk '/systemd/{if($2<231) exit 102}'
    # 3) 已挂载 v2 且未混合
    mount | grep -q 'cgroup2 on /sys/fs/cgroup' || exit 103
    [ -d /sys/fs/cgroup/unified ] && exit 104   # 混合层级拒绝
    
  2. 内核启动参数固化
    编辑 /etc/default/grub

    GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all"
    

    执行 grub2-mkconfig -o /boot/grub2/grub.cfg && reboot确保无 v1 控制器残留

  3. 容器运行时强制对接 v2

    • containerd 1.7+
      version = 2
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
        SystemdCgroup = true
      
    • Docker 25.x
      {
        "exec-opts": ["native.cgroupdriver=systemd"],
        "cgroup-parent": "system.slice"
      }
      

    修改后执行 systemctl restart containerddockerd若系统仍为 v1 则 runc 启动失败,直接体现强制效果

  4. kubelet 配置显式声明并关闭回退
    创建 /etc/kubernetes/kubelet-config.yaml

    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    cgroupDriver: systemd
    failSwapOn: true
    featureGates:
      DisableCgroupV1Fallback: true   # 1.30+ 新增,必须显式打开
    

    启动参数追加 --config=/etc/kubernetes/kubelet-config.yaml --cgroup-driver=systemd
    若检测到 v1 挂载,kubelet 直接退出并返回错误码,节点状态为 NotReady

  5. 集群级二次校验(可选加分)
    部署 ValidatingAdmissionWebhook,监听 Node 对象的 metadata.annotations["node.kubernetes.io/cgroup-version"],若不等于 v2 则拒绝更新;配合 ClusterAPIACK/TKE 节点池的实例启动模板,可实现“不符合就替换”。

完成以上五步后,任意节点在 cgroups v2 未就绪时均无法注册到 Kubernetes 1.30+ 集群,满足“强制启用”的面试要求。

拓展思考

  • 混用云与物理机场景:国内金融客户常有存量 CentOS 7 物理机,直接升级内核风险高,可引入 cgroup-v2-devicemapper 兼容层先重装为 Alibaba Cloud Linux 3 再接入集群;面试时可展示如何编写 Packer 模板 批量构建黄金镜像。
  • 观测与可观测性:v2 的 memory.max 统计单位与 v1 的 memory.limit_in_bytes 不同,HPA 自定义指标需适配 cadvisor 0.48+;可提及在 阿里云 Prometheus 监控 中如何切换指标别名。
  • 安全加固:v2 支持 cgroup.freezePSI(Pressure Stall Information),结合 SeccompProfile 可实现内存压力熔断;回答时强调对 多租户平台 的 QoS 提升,可体现深度。
  • 回滚策略:若业务镜像因旧版 JDK 读取 /sys/fs/cgroup/memory 路径失败,需设置节点标签 cgroup=v2 并通过 nodeAffinity 隔离,面试中展示灰度能力可加分。