3. 生产级集群初始化与节点池治理
生产级 Kubernetes 集群的可靠性,从节点池设计那一刻就开始分化。很多团队在测试环境用“一个节点池打天下”,上线后才发现 GPU 任务和在线业务抢资源、Spot 实例被回收时把核心服务一起带走、节点镜像升级导致整池滚动重启。节点池治理的核心,是把“节点”从同质资源变成有明确职责、可独立扩缩、可独立升级的治理单元。
节点池设计:按职责切分,而不是按机器型号切分
节点池(Node Pool / Node Group)是云厂商对一组同规格、同配置节点的抽象。设计时先回答三个问题:这组节点承载什么工作负载?它的故障域和成本模型是什么?它需要独立升级吗?
一个典型的生产集群可以划分为四类节点池:
- 系统池(system):运行 CoreDNS、Ingress Controller、监控采集、日志代理等集群基础组件。建议 3 节点起,跨可用区分布,使用按量或包年包月实例,禁止业务 Pod 调度。
- 通用业务池(general):运行无状态在线服务,使用 4C8G 或 8C16G 通用型实例,开启弹性伸缩,最小 2 节点、最大按峰值 QPS 估算。
- 计算密集型池(compute):运行批处理、转码、CI Runner,使用计算优化型实例,可混合 Spot 实例,通过污点隔离。
- 内存型池(memory):运行缓存、大数据组件,使用内存优化型实例,设置较高的 kubelet 预留。
切分粒度不是越细越好。节点池过多会导致碎片化、调度复杂度上升、扩缩容决策变慢。经验上,单集群 3~6 个节点池是较平衡的区间。每个节点池应有明确的标签体系,例如 node-pool=general、workload-type=online、topology.kubernetes.io/zone=cn-hangzhou-h,供调度器、HPA、监控和成本分摊共同使用。
实例类型选择:把成本与可用性放在同一张表里
实例选择不是“选最便宜的”,而是把可用性要求、性能特征和成本模型对齐。以下是一组示例参数,用于说明决策逻辑(非实测数据):
| 节点池 | 实例族 | 计费方式 | 关键考量 |
|---|---|---|---|
| system | 通用型 2C4G | 包年包月 | 稳定、跨 AZ、不被回收 |
| general | 通用型 4C8G | 按量 + 弹性 | 突发扩容快,成本可控 |
| compute | 计算型 8C16G | Spot 为主 | 可中断,需容忍回收 |
| memory | 内存型 8C64G | 包年包月 | 大页、NUMA 敏感 |
Spot 实例的使用有一条硬规则:只放在有污点、有容忍、有中断处理器的节点池里。Kubernetes 本身不会感知 Spot 回收,需要配合云厂商的回收通知(如阿里云 ECS 实例元数据、AWS EC2 Instance Rebalance Recommendation)触发节点排水。核心在线业务不应与 Spot 节点池共享污点容忍。
操作系统与容器运行时配置
生产节点建议使用云厂商提供的容器优化 OS(如 Alibaba Cloud Linux 3、Container-Optimized OS、Bottlerocket),而不是通用 CentOS。容器优化 OS 已预置 containerd、kubelet 依赖、内核参数和 cgroup v2 支持,减少手工调优带来的漂移。
容器运行时统一使用 containerd,不再使用 Docker Shim。关键配置包括:
# /etc/containerd/config.toml 片段
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.cn-hangzhou.aliyuncs.com/acs/pause:3.9"
max_concurrent_downloads = 10
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
kubelet 侧需要设置合理的预留:--system-reserved=cpu=500m,memory=1Gi、--kube-reserved=cpu=500m,memory=1Gi、--eviction-hard=memory.available<500Mi,nodefs.available<10%。这些值不是拍脑袋,而是根据节点规格和实际组件内存占用反推。例如 8C16G 节点,系统组件加 kubelet 常驻约 1.5~2Gi,预留 2Gi 后 Allocatable 约 14Gi,再按 70% 水位规划 Pod 资源请求。
节点标签与污点管理
标签用于“吸引”调度,污点用于“排斥”调度,两者配合才能实现节点池隔离。系统池应打上 node-role.kubernetes.io/system=true:NoSchedule,只允许带对应容忍的基础组件进入。计算池打上 workload=compute:NoSchedule,批处理任务显式声明容忍。
标签命名要避免随意。建议固定三类:拓扑类(zone、region、rack)、能力类(gpu、ssd、high-memory)、职责类(system、general、compute)。污点则只保留少量稳定语义,例如 dedicated=system:NoSchedule、spot=true:NoSchedule、gpu=true:NoSchedule。污点过多会导致调度器匹配失败难以排查。
自动扩缩容策略:分层响应,避免震荡
生产集群的扩缩容通常分三层:HPA 负责 Pod 副本数,Cluster Autoscaler 或云厂商节点自动伸缩负责节点数,而节点池的 min/max 是硬边界。三层必须协同,否则会出现“Pod 扩了但节点没扩,全部 Pending”或“节点扩了但 Pod 没扩,资源空转”。
以 general 池为例,可设置如下策略:
- HPA:目标 CPU 利用率 70%,最小 4 副本,最大 40 副本,扩容冷却 60s,缩容冷却 300s。
- 节点伸缩:最小 2 节点,最大 20 节点,扩容触发条件为“存在 Pending Pod 超过 30s”,缩容触发条件为“节点利用率低于 30% 持续 10 分钟”。
- 缩容保护:系统池不自动缩容;compute 池允许缩到 0,但需确保批处理任务有重试机制。
扩缩容震荡是常见故障。缓解手段包括:设置合理的缩容冷却时间、使用 cluster-autoscaler.kubernetes.io/safe-to-evict: "false" 保护关键 Pod、避免 HPA 和节点伸缩同时以激进阈值触发。监控上要重点关注 pending_pods、node_utilization、scale_up_failed 三个指标,它们分别对应“扩不出来”“扩了没用”“扩失败”三类问题。
节点池治理的最终检验标准不是“配置多漂亮”,而是:当业务流量突增时,新节点能否在 2 分钟内就绪并正确调度;当 Spot 实例被回收时,任务能否自动迁移而不影响在线服务;当需要升级 OS 镜像时,能否按池灰度、逐批滚动而不中断业务。做到这三点,节点池才算真正进入生产可用状态。