青蛙小白
博客 / 2025/12

Kubernetes cgroup v2 迁移:为什么新建集群比原地升级更靠谱

2025/12/16 · — 字 · 阅读约 — 分钟 ·
目录

Kubernetes 1.35 带来了一个不小的变化:kubelet 默认拒绝在 cgroup v1 节点上启动。这可不是简单的功能弃用,而是整个 Linux 生态在资源管理架构上的一次集体转向。

生态系统的连锁反应

这次变化特殊在哪?它不是某个项目的孤立决定,而是整个生态在同步行动:

  • systemd v256 开始默认拒绝 cgroup v1,v258 会完全移除支持。
  • RHEL 9.4 废弃 cgroup v1,RHEL 10 只支持 v2。
  • Fedora 的时间表更激进:41/42 版本默认不启动 v1,43 会完全移除。
  • Ubuntu 22.04 LTS 及更高版本默认使用 cgroup v2。
  • Debian Trixie 使用 systemd 257,默认不支持 v1。

这种同步性背后有技术原因。cgroup v1 的混合层级架构在设计上就存在复杂性问题:每个资源控制器(CPU、内存、IO)都有独立的层级树,同一个进程在不同控制器中可能处于不同位置。这种设计在容器密度增加、资源管理需求变复杂后,维护成本越来越高。

cgroup v2 的统一层级架构从根本上解决了这个问题。所有资源控制器在同一个层级树中管理,进程在所有控制器中的位置一致。这不仅简化了管理,还让内核能做更多优化。systemd 作为现代 Linux 发行版的核心组件,与 cgroup 紧密集成,它的转向直接影响了整个生态。

Kubernetes 的时间表

Kubernetes 1.35 引入的 FailCgroupV1 参数是个明确信号。到 1.38 版本,cgroup v1 的代码将被完全移除。这个时间表看起来激进,但考虑到 Kubernetes 的发布节奏(每年 3 个版本),从 1.35 到 1.38 大约是 12 个月。

这个时间窗口对运维团队来说其实挺紧的。如果你的集群还在 1.30 或更早版本,得先升级到 1.35+,然后处理 cgroup v2 迁移,再跟上后续版本。每个环节都要测试验证,生产环境更是马虎不得。

更麻烦的是应用兼容性。不是所有应用都能无缝切换到 cgroup v2。

应用兼容性的问题

Java 应用就是个典型例子。OpenJDK 早期版本对 cgroup v2 支持不完整,得用 8u372、11.0.16 或 15+ 版本才能正确识别资源限制。要是集群里跑着老版本 Java 应用,直接切到 cgroup v2 可能导致容器内存限制失效。

Node.js 的情况类似,需要 20.3.0+ 版本。

这些版本要求看起来不高,但实际环境中,很多应用因为各种原因(依赖兼容性、测试成本、业务优先级)一直在用老版本。

监控和安全 Agent 也是个问题。很多 Agent 直接读取 cgroup 文件系统来获取容器资源使用情况,cgroup v1 和 v2 的文件路径和格式完全不同。如果 Agent 没有更新,切换后监控数据会丢失或错误。

原地升级的风险

理论上,原地升级是最直接的方案:更新内核参数,重启节点,让系统切换到 cgroup v2。但实际操作中,这个方案的风险很高。

首先是 kubelet 直接起不来的问题。如果节点上的容器运行时(如containerd)没有正确配置 cgroup v2 支持,或者 kubelet 版本太老,重启后节点就无法加入集群。这时候就需要登录节点手动修复,但如果是批量操作,可能同时有多个节点出问题。

应用兼容性问题更隐蔽。节点重启后,kubelet 能正常工作,容器也能启动,但应用行为可能不正常。比如 Java 应用没有正确识别内存限制,实际使用超过了容器配额,触发 OOM。这种问题在低负载时不明显,高峰期才暴露。

老集群的历史包袱是另一个麻烦。不同时期加入的K8S节点,配置可能不一致。有的节点装了自定义内核模块,有的节点有特殊的 systemd 配置。原地升级时,这些差异会放大问题的复杂度。

回滚也很困难。cgroup 切换需要重启节点,如果升级后发现问题,回滚又要重启一次。对于有状态服务,频繁重启的影响很大。而且 cgroup 切换不是简单的配置回退,可能涉及内核参数、systemd 配置、容器运行时配置的多处修改。

新建集群的优势

相比原地升级,新建集群迁移的方案虽然工作量大,但风险可控。

环境干净是最大优势。新集群从一开始就配置 cgroup v2,不需要处理历史遗留问题。节点配置统一,容器运行时版本一致,systemd 配置标准化。这种一致性让问题排查和维护都简单很多。

充分测试是另一个关键点。新集群可以先部署非核心应用,观察一段时间,确认没有问题再逐步迁移核心业务。这个过程中,老集群继续提供服务,不影响业务连续性。如果发现应用兼容性问题,可以在新集群上慢慢调试,不用担心影响生产。

保留回退能力也很重要。新老集群并存期间,如果新集群出现无法快速解决的问题,可以把流量切回老集群。这种灵活性在原地升级方案中很难实现。

更有价值的是,新建集群给了你一个重新审视架构的机会。可以顺便升级 Kubernetes 版本、容器运行时、网络插件、存储方案。可以重新规划节点规格、网络拓扑、资源配额。可以梳理现有应用,淘汰不再使用的服务,优化资源分配。

这些工作在老集群上做风险很高,但在新集群上可以从容规划。

迁移策略建议

基于新建集群的方案,迁移过程可以分几个阶段。

第一阶段是准备和验证。搭建新集群,配置 cgroup v2,部署基础组件(监控、日志、网络插件)。然后部署几个简单的无状态应用,验证基本功能。这个阶段的重点是确认基础设施没有问题。

第二阶段是应用兼容性测试。选择几个典型应用(Java、Node.js、Go、Python),在新集群上部署测试环境。重点关注资源限制是否生效、监控数据是否正常、日志采集是否完整。如果发现兼容性问题,这时候有充足时间升级应用或调整配置。

第三阶段是逐步迁移。从非核心应用开始,逐个迁移到新集群。每迁移一批应用,观察一段时间,确认稳定后再继续。这个过程可能需要几周甚至几个月,但风险可控。

第四阶段是核心业务迁移。这是最关键的环节,需要详细的切换方案和回退预案。可以采用蓝绿部署或金丝雀发布的方式,先切一小部分流量到新集群,逐步增加比例。

最后是老集群下线。确认所有业务都稳定运行在新集群上后,保留老集群一段时间作为备份,然后再完全下线。

时间规划

从现在开始规划,到完成迁移,大约需要 8-12 个月。这个时间表看起来很长,但考虑到测试、验证、逐步迁移的过程,其实不算宽裕。

如果你的集群还在 Kubernetes 1.30 或更早版本,建议尽快开始规划。等到 1.38 发布,cgroup v1 代码被移除,再被动应对就很被动了。

参考

评论