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计算型 8C16GSpot 为主可中断,需容忍回收
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。污点过多会导致调度器匹配失败难以排查。

节点池治理:标签吸引 + 污点排斥 + 自动扩缩容 kube-scheduler nodeSelector / affinity / tolerations 系统池 system 标签: node-pool=system 污点: dedicated=system:NoSchedule 实例: 2C4G 包年包月 ×3 承载: CoreDNS / Ingress / 监控 扩缩容: 固定 3 节点,不自动缩容 通用业务池 general 标签: node-pool=general 污点: 无(允许业务调度) 实例: 4C8G 按量 + 弹性 承载: 无状态在线服务 扩缩容: 2~20 节点,HPA 联动 计算池 compute 标签: node-pool=compute 污点: spot=true:NoSchedule 实例: 8C16G Spot 为主 承载: 批处理 / CI Runner 扩缩容: 0~50 节点,容忍回收 内存池 标签: memory=high 污点: memory=high 实例: 8C64G 承载: 缓存 / 大数据 扩缩容: 固定 自动扩缩容决策链(以 general 池为例) 指标触发 CPU>70% 或 Pending Pod 扩容判断 当前节点数 < max 节点加入 打标签 + 就绪 缩容 低水位 10min

自动扩缩容策略:分层响应,避免震荡

生产集群的扩缩容通常分三层: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 三个指标,它们分别对应“扩不出来”“扩了没用”“扩失败”三类问题。

节点池的标签和污点一旦上线,变更成本很高。建议在集群初始化阶段就用 IaC(Terraform / Crossplane)固化节点池定义,把实例类型、镜像、kubelet 参数、标签污点全部纳入版本控制,避免手工修改导致环境漂移。

节点池治理的最终检验标准不是“配置多漂亮”,而是:当业务流量突增时,新节点能否在 2 分钟内就绪并正确调度;当 Spot 实例被回收时,任务能否自动迁移而不影响在线服务;当需要升级 OS 镜像时,能否按池灰度、逐批滚动而不中断业务。做到这三点,节点池才算真正进入生产可用状态。