☸️ 卷③ Kubernetes 与容器
20 条。适用场景:集群升级、容器资源异常、网络入口改造。
「要点」「我的补充」为本人撰写,非原文摘录;版本相关结论请以链接内的官方表述为准。
一、cgroup 与资源管理:升级时最容易出事的地方
41. New Conversion from cgroup v1 CPU Shares to v2 CPU Weight
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:迁到 cgroup v2 之后 CPU 竞争行为变了,需要知道换算关系改在哪。
要点
- cgroup v1 用
cpu.shares(默认 1024 的相对权重),v2 用cpu.weight(范围 1–10000),两者需要换算 - 换算公式的选择会直接影响高竞争场景下各容器实际拿到的 CPU 比例
- 这类变更在低负载时完全看不出来,只在 CPU 打满时暴露
我的补充:升级前后要专门压一遍混合优先级负载:同节点上跑一个 requests 很低的和一个很高的,把 CPU 打满,看实际分配比是否符合预期。检查实际值:cgroup v2 下读 /sys/fs/cgroup/<pod路径>/cpu.weight。判断节点用的哪个版本:stat -fc %T /sys/fs/cgroup/,返回 cgroup2fs 就是 v2。
42. Kubernetes v1.36: PSI Metrics for Kubernetes Graduates to GA
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:想在 K8s 里判断"节点是不是真的资源紧张",而不是只看利用率。
要点
- PSI(Pressure Stall Information)度量的是任务因等待资源而停滞的时间占比,直接反映饱和度
- 分 CPU、memory、IO 三类,各有
some(部分任务受影响)与full(全部停滞)两档 - 相比利用率,PSI 更适合做扩容与驱逐决策:利用率 100% 不一定难受,PSI 高一定难受
我的补充:这是卷①提到的"饱和度"在 K8s 里的落地形态。GA 之后可以直接拿它做 HPA 的辅助信号或告警。节点上手工看:cat /proc/pressure/memory,some avg10 持续高于 10 就该关注了。要区分 some 和 full:full 不为零意味着所有任务都在等,那时候用户已经明确感知到卡了。
43. Kubernetes v1.36: Tiered Memory Protection with Memory QoS
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:想让内存回收压力优先落在低优先级容器上,而不是随机砍。
要点
- 借助 cgroup v2 的
memory.min/memory.low给不同 QoS 等级的 Pod 提供分层保护 - 效果是内存紧张时,Guaranteed 类的页更不容易被回收
- 前提是节点用 cgroup v2
我的补充:这个能力让 requests.memory 从"仅参与调度"变成"运行时也起保护作用",所以认真填 requests 的收益变大了。注意它减轻的是回收压力,不改变 OOMKill 的判定——超 limit 照样被杀。真正的内存问题还是得看卷①第 18 条那套 pgscan/pgsteal 判断法。
44. Kubernetes v1.36: Pod-Level Resource Managers (Alpha)
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:一个 Pod 内多容器需要协同分配 CPU/内存/设备时,容器级管理不够用。
要点
- 传统的 CPU manager、memory manager 以容器为单位决策,同 Pod 内容器可能被分到不同 NUMA 节点
- Pod 级管理让同 Pod 的容器能作为整体做拓扑对齐
- Alpha 阶段,需显式开 feature gate
我的补充:受益最明显的是 sidecar 密集型和 AI 推理这类主容器与辅助容器之间有大量本地通信的场景——跨 NUMA 访问内存的延迟差距是实打实的。Alpha 特性别上生产。想先看现状可以在节点上用 numactl --hardware 看 NUMA 拓扑,lscpu 看核心分布。
45. Kubernetes v1.36: In-Place Vertical Scaling for Pod-Level Resources Graduates to Beta
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:调整 Pod 的 CPU/内存不想重启容器。
要点
- 原地垂直扩缩允许改变已运行 Pod 的资源而不重建,这对有状态或启动慢的服务价值很大
- 内存缩容比扩容困难得多(已分配的页不能凭空收回),所以两个方向的支持程度不同
- Beta 阶段通常默认开启,但仍要看具体版本
我的补充:这个特性能显著改善"启动要 3 分钟的 Java 服务需要加内存"这类场景——以前只能滚动重启。但别把它当 VPA 的替代:自动化改资源仍需谨慎,尤其内存缩容可能触发 OOM。落地建议先手工用 kubectl patch 在测试环境验证目标工作负载是否真支持(容器需声明 resizePolicy)。
46. Kubernetes v1.36: Mutable Pod Resources for Suspended Jobs (beta)
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:批处理 Job 在真正开跑前需要根据排队情况调整资源。
要点
- Job 处于 suspended 状态时修改资源,避免"创建时就要猜准资源量"
- 对批处理调度器(如 Kueue)很有用:可以先入队再按集群空闲情况定资源
- 只在 suspended 状态可改,运行中不行
我的补充:这条与上一条配合起来,才让"资源量后置决策"成为可能。做 AI 训练任务排队的团队值得关注——常见痛点正是提交时不知道该申请几张卡。用 Kueue 的话它已经在往这个方向对接,但注意 Job 一旦 unsuspend 就不能再改。
47. Kubernetes v1.36: Advancing Workload-Aware Scheduling
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:默认调度器逐 Pod 决策,对"要么全部调度成功要么都不跑"的负载不适用。
要点
- 工作负载感知调度关注一组 Pod 的整体可调度性(gang scheduling 类需求)
- 典型场景是分布式训练:只启动一半的 worker 等于白占资源
- 涉及调度器扩展点与新的 API 对象
我的补充:在这些原生能力成熟前,社区方案是 Volcano 或 Kueue。判断你是否需要它很简单:如果你的任务"缺一个 Pod 就完全无法工作",你就需要 gang scheduling,否则默认调度器够用。硬凑 initContainer 等待其他 Pod 的做法能跑但会造成资源死锁,别这么干。
48. Kubernetes v1.36: More Drivers, New Features, and the Next Era of DRA
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:GPU 等设备用 device plugin 表达能力不足时,了解 DRA 这套新模型。
要点
- DRA(Dynamic Resource Allocation)把设备当作可申请、可共享、带属性的资源,而不是简单的整数计数
- 相比 device plugin 的"几张卡",DRA 能表达显存大小、拓扑、共享模式等约束
- 生态依赖厂商提供 DRA driver
我的补充:这是 GPU 调度的方向,但迁移不便宜。现在要跑 GPU 的话,先确认你的 GPU operator 版本是否已支持 DRA,多数环境仍在 device plugin 上。GPU 共享(MPS/MIG)的配置比调度器本身更容易出问题——先把 nvidia-smi 在容器里能正确看到设备这件事跑通,再谈精细调度。
二、安全与隔离
49. Kubernetes v1.36: User Namespaces in Kubernetes are finally GA
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:容器里的 root 就是宿主机 root,这个长期风险终于有了正式解法。
要点
- 用户命名空间把容器内的 UID 映射到宿主机上另一段非特权 UID,容器内的 root 在宿主机上不是 root
- 效果是显著缩小容器逃逸的影响面:即使突破了容器边界,拿到的也不是宿主机特权
- GA 意味着可以在生产考虑,但需要运行时(containerd/CRI-O)与内核配合
我的补充:这是近年容器安全里性价比最高的一个开关。开启前要注意两点:一是挂载的 hostPath 与 PV 的文件属主会经过映射,权限问题是主要迁移成本,有状态服务要先测;二是需要较新内核(推荐 6.x)。开之前先确认 /proc/sys/user/max_user_namespaces 不是 0。别忘了这不替代 runAsNonRoot——两者叠加才是纵深防御。
50. Securing Production Debugging in Kubernetes
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:需要在生产 Pod 里排查问题,又不想为此开放过大权限。
要点
kubectl debug的临时容器(ephemeral container)比exec进业务容器更可控:不改动原容器、可用专门的调试镜像- 风险点在于调试容器能共享目标的进程与网络命名空间,权限设计要跟上
- 应当把调试能力做成受审计的独立权限,而不是给开发
exec全权
我的补充:这条正好回答卷①第 1 条的容器排查难题——业务镜像应该保持精简,调试工具通过临时容器带进去:kubectl debug -it <pod> --image=nicolaka/netshoot --target=<容器名>。--target 是关键,加了才共享目标进程命名空间,否则看不到对方的进程。RBAC 上把 pods/ephemeralcontainers 单独授权并接入审计日志。
51. Kubernetes v1.36: Fine-Grained Kubelet API Authorization Graduates to GA
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:nodes/proxy 这一个权限过去等于放开 kubelet 全部接口,粒度太粗。
要点
- 细化后可以按 kubelet 的具体端点授权,而不是一把梭
- 意义在于最小权限:监控只需要 metrics,不该顺带获得 exec 能力
- GA 后应重新审视既有的 ClusterRole
我的补充:值得专门做一次审计:kubectl get clusterroles -o json 里搜 nodes/proxy,看哪些角色拿了它。常见问题是监控组件的 ServiceAccount 被授了过大权限,而它实际只需要 nodes/metrics。拿到 nodes/proxy 相当于能对任意节点上的容器执行命令,这是很多集群里事实存在的提权路径。
52. Kubernetes v1.36: Admission Policies That Can't Be Deleted
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:安全策略被误删或被有权限的人绕过,需要更强的保证。
要点
- 通过基于清单(manifest)的准入控制,让部分策略不可通过 API 删除
- 解决的是"拿到 cluster-admin 就能关掉所有策略"这个根本问题
- 属于把控制平面配置从"集群内对象"移到"启动配置"的思路
我的补充:这类机制配合 GitOps 才有意义——策略的真实来源应该在 Git 里,集群只是执行者。同时提醒一点:不可删除也意味着改起来麻烦,上线前务必在测试集群验证策略不会误伤(尤其别把自己的 CI 部署账号挡在外面,我见过这种把整条发布流水线锁死的事故)。
53. SELinux Volume Label Changes goes GA
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:挂载大卷时因为递归打 SELinux 标签导致 Pod 启动极慢。
要点
- 过去为卷内每个文件递归设置 SELinux 上下文,文件多时启动时间可以变得非常长
- 改为通过挂载选项一次性指定上下文,避免逐文件遍历
- 变更涉及行为差异,官方特别提示了后续版本的影响
我的补充:如果你的 Pod 挂了包含大量小文件的 PV(比如代码仓库缓存、模型文件)且启动慢得离谱,这就是元凶之一。同类问题还有 fsGroup 导致的递归 chown——用 fsGroupChangePolicy: OnRootMismatch 可以避免每次都全量遍历。这两个开关能把启动时间从几分钟降到几秒。
54. Reconciling the Past: Correcting Records for Unfixed Kubernetes CVEs
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:搞清楚 CVE 记录本身也可能不准确,以及该如何看待漏洞清单。
要点
- 历史 CVE 的状态记录存在与实际修复情况不一致的问题,社区在做校正
- 对使用者的含义是:扫描器报出的结论需要复核,不能直接等同于"我有风险"
- 判断是否受影响要看具体版本与配置,而不只看 CVE 编号
我的补充:这解释了为什么安全扫描报告里常有一堆"不适用"的告警。实际做法是:拿到报告先按"是否启用了相关特性"过滤一遍再排优先级。官方权威来源是 kubernetes.io/docs/reference/issues-security/official-cve-feed/,比第三方扫描器的数据库准。别把扫描器输出直接当工单派下去,会把团队淹掉。
三、网络入口:Ingress 到 Gateway API
55. Gateway API v1.6: TCPRoute and UDPRoute Graduate to Standard
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:需要暴露非 HTTP 的四层服务,Ingress 表达不了。
要点
- Gateway API 用分层的资源模型(GatewayClass / Gateway / Route)替代 Ingress 的单一对象加注解
- TCPRoute、UDPRoute 进入标准通道,意味着四层转发有了正式的表达方式
- 分层的另一个目的是职责分离:集群管理员管 Gateway,应用团队管 Route
我的补充:Ingress 的最大痛点是能力全靠 controller 私有注解,换 controller 就要重写。Gateway API 把常用能力标准化了。迁移不必一步到底:两者可以并存,新服务先用 Gateway API。UDP 转发前记得确认你的 LoadBalancer(尤其云厂商的)是否支持 UDP,很多不支持或额外收费。
56. Gateway API v1.5: Moving features to Stable
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:判断 Gateway API 是否已经足够稳定到可以承载生产流量。
要点
- 特性从实验通道进入标准通道是可用性的关键信号,Gateway API 明确区分这两个通道
- 关注哪些能力变 stable,比关注版本号更有意义
- 生产选型应只依赖标准通道的能力
我的补充:实践建议:只用标准通道特性,实验通道的东西不要进生产——它们的 API 可能不兼容变更。检查方式是看 CRD 的 annotation gateway.networking.k8s.io/bundle-version 与安装的是 standard 还是 experimental 包。另外 Gateway API 的 CRD 是独立安装的,升级 K8s 不会自动升它,这点和 Ingress 不同。
57. Before You Migrate: Five Surprising Ingress-NGINX Behaviors
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:从 ingress-nginx 迁走之前,先知道你依赖了哪些没写进标准的行为。
要点
- 长期使用 ingress-nginx 的集群往往隐式依赖了它的特有行为,换实现后表现不一致
- 差异常出现在路径匹配语义、重写规则、超时与缓冲默认值
- 迁移风险主要来自"没意识到自己依赖了它"
我的补充:迁移前务必做一次行为对比测试而不是只看配置能否转换:准备一批真实请求(含尾斜杠、编码字符、大 body、慢客户端),在新旧入口上跑一遍对比响应码与头。历史坑最多的是 rewrite-target 配合正则捕获组,以及 use-regex 开启后路径优先级的变化。
58. Ingress NGINX: Statement from the Steering and Security Response Committees
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:了解 ingress-nginx 的维护状态,这会直接影响你的技术选型。
要点
- 关键项目的维护者带宽与项目生命周期是选型的一部分,不只是功能对比
- 官方声明类信息应作为迁移计划的输入
- 对使用者的意义是:要有替代方案的评估,而不是等到不得不换
我的补充:ingress-nginx 在国内集群里几乎是默认选择,所以这条值得认真看。现在就该做的事:确认自己用了哪些注解(kubectl get ing -A -o json 里 grep nginx.ingress.kubernetes.io),评估目标方案(Gateway API + Envoy Gateway / Cilium / Istio)的对应能力。不要等到出安全公告才开始查。
59. Experimenting with Gateway API using kind
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:想在本地零成本试 Gateway API,不动真集群。
要点
- kind(Kubernetes in Docker)能在本机起完整集群,适合验证 API 与 controller 行为
- Gateway API 需要单独安装 CRD 与一个实现(controller),这一步在本地练一遍能避免生产上手忙脚乱
- 本地实验的价值是可以随便删了重建
我的补充:kind 起集群要注意端口映射——extraPortMappings 得在创建时写进配置,事后加不了,这是新手最容易卡住的地方。另外 kind 里的 LoadBalancer 默认没有 external IP,要配 cloud-provider-kind 或直接用 NodePort。本地验证完再写生产 manifest,能省掉大量试错。
60. How the controller-runtime Cache Actually Works
来源:Kubernetes 官方博客 · kubernetes.io 读它解决什么:写 Operator 时想知道为什么自己的 controller 没把 API server 压垮。
要点
- controller-runtime 的 client 默认走本地 cache(informer + watch),读请求不打到 API server
- 因此"看到的是稍旧的状态"是设计使然,写 controller 必须容忍这一点
- 直连 API server 的读(
APIReader)在特定场景才需要,滥用会造成压力
我的补充:两个高频 bug 都源自没理解这点。一是读到旧对象就直接更新导致冲突——正确做法是遇到 conflict 就重新入队重试,而不是加锁或重读。二是自己写循环 List 大量对象却不用 cache,把 API server 打爆。另外 cache 是按 GVK 建 informer 的,watch 一个高频变更的资源(比如 Events)代价很高,用 cache.Options 里的 field selector 限制范围。
本卷小结
三条:
- cgroup v2 升级要压混合优先级负载。CPU 权重换算变了,低负载看不出来,打满才暴露。
- 用户命名空间 GA 了,去开它。容器内 root 不再是宿主机 root,性价比最高的容器安全开关,代价主要是卷的属主映射。
- 别
exec进业务容器排查。用kubectl debug --target带调试镜像进去,业务镜像保持精简。
