使用 CPA + CA 预热扩容 Node
概述
在 Kubernetes 集群中,业务 Pod 扩容时如果资源不足,Cluster Autoscaler (CA) 会扩容节点,但通常需要 2-5 分钟,导致业务 Pod 长时间 Pending。
本文介绍通过 Cluster Proportional Autoscaler (CPA) 管理低优先级占位 Pod,提前触发 CA 扩容,业务 Pod 获得秒级响应(5-10 秒),响应速度提升 24-60 倍。这种思路和这种思路和阿里云基于ack-autoscaling-placeholder实现容器秒级伸缩 是类似的
工作原理
核心流程:
- CPA 根据节点数量自动管理占位 Pod(运行 pause 容器,最低优先级)
- 业务 Pod 扩容时立即抢占占位 Pod 资源
- 占位 Pod 进入 Pending 状态,触发 CA 扩容节点
- 新节点就绪后,占位 Pod 重新调度,恢复资源缓冲池
优势:
- 部分业务 Pod 立即获得资源(5-10 秒 vs 2-5 分钟)
- 保持集群始终有一定比例的资源缓冲
- pause 容器实际消耗极少(~1MB 内存,~0.001 核 CPU)
适用场景:
- 在线业务(Web、API)对响应时间敏感
- 突发流量场景(秒杀、营销活动)
- 资源规格统一的业务
- 可承受 10-30% 资源预留成本
容量规划
以 16C32G 节点、4C8G 业务 Pod 为例:
节点规格:16C/32G
系统组件占用:~4C/8G(kubelet、kube-proxy、监控等)
可用资源:12C/24G
可运行业务 Pod(4C/8G):3 个
缓冲策略选择:
| 策略 | nodesPerReplica | 每节点占位 Pod | 资源缓冲 | 适用场景 |
|---|---|---|---|---|
| 保守 | 3 | 0.33 个 | 10-13% | 成本优先 |
| 推荐 | 2 | 0.5 个 | 17-20% | 平衡成本与性能 |
| 水位线 | 0.667 | 1.5 个 | 30-37% | 保证资源水位线 |
| 激进 | 1 | 1 个 | 33% | 极致响应速度 |
计算 nodesPerReplica
方法 1:基于目标缓冲比例
每个节点可运行业务 Pod 数:3 个
目标缓冲比例:20%
每个节点占位 Pod 数 = 3 × 20% = 0.6 个
nodesPerReplica = 1 / 0.6 = 1.67 ≈ 2
方法 2:基于资源水位线(更精确)
目标保留 30% 资源水位线:
步骤 1:每节点需保留 = 16C × 30% = 4.8C
步骤 2:每节点占位 Pod 数 = 4.8C / 4C = 1.2 个
步骤 3:向上调整为 1.5 个(更保守)
步骤 4:nodesPerReplica = 1 / 1.5 = 0.667
不同水位线配置对照:
| 目 标水位线 | 每节点保留 | 占位 Pod 数 (4C) | nodesPerReplica | 实际缓冲 |
|---|---|---|---|---|
| 20% | 3.2C | 1 个 | 1.0 | ~25% |
| 30% | 4.8C | 1.5 个 | 0.667 | ~37% |
| 40% | 6.4C | 2 个 | 0.5 | ~50% |
注意:实际缓冲比例因 ceil() 向上取整会略高于目标值。
完整部署方案
步骤 1:创建 PriorityClass
# placeholder-priority.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: placeholder-priority
value: -10 # 负数表示最低优先级
globalDefault: false
description: "Priority class for placeholder pods"
步骤 2:创建占位 Pod Deployment
# placeholder-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: placeholder-pods
namespace: default
spec:
replicas: 1 # CPA 会自动调整
selector:
matchLabels:
app: placeholder
template:
metadata:
labels:
app: placeholder
spec:
priorityClassName: placeholder-priority
containers:
- name: pause
image: registry.k8s.io/pause:3.9
resources:
requests:
cpu: "4000m"
memory: "8Gi"
limits:
cpu: "4000m"
memory: "8Gi"
# 反亲和性:分散到不同节点
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: placeholder
topologyKey: kubernetes.io/hostname
步骤 3:部署 CPA
推荐策略配置(20% 缓冲):
# placeholder-autoscaler-values.yaml
config:
linear:
nodesPerReplica: 2
min: 2
max: 50
preventSinglePointFailure: false
includeUnschedulableNodes: false
options:
namespace: default
target: "deployment/placeholder-pods"
pollPeriodSeconds: 30
水位线策略配置(30% 缓冲):
config:
linear:
nodesPerReplica: 0.667
min: 2
max: 100
CPA 配置语法说明:
上面的 placeholder-autoscaler-values.yaml 是 Helm Chart 的 values 写法。安装后 Chart 会把 config.linear 转换成 CPA
读取的 ConfigMap,options 会转换成 CPA 容器启动参数。
config:
linear:
nodesPerReplica: 2
min: 2
max: 50
preventSinglePointFailure: false
includeUnschedulableNodes: false
options:
namespace: default
target: "deployment/placeholder-pods"
pollPeriodSeconds: 30
字段含义:
| 字段 | 说明 |
|---|---|
config.linear | 使用线性模式。CPA 还支持 ladder 阶梯模式,但占位 Pod 预热扩容通常使用 linear。 |
nodesPerReplica | 每多少个节点创建 1 个目标副本,支持小数。例如 2 表示每 2 个节点 1 个占位 Pod。 |
coresPerReplica | 每多少个 CPU Core 创建 1 个目标副本, 支持小数。可与 nodesPerReplica 二选一或同时配置。 |
min | 副本数下限。用于保证小集群也有最少占位 Pod。 |
max | 副本数上限。用于控制成本,避免大集群中占位 Pod 过多。 |
preventSinglePointFailure | 为 true 且集群节点数大于 1 时,CPA 至少保留 2 个副本。占位 Pod 场景通常由 min 控制即可。 |
includeUnschedulableNodes | 是否把不可调度节点也计入计算。为 false 时会排除 cordon/drain 中的节点,通常更符合资源缓冲语义。 |
options.namespace | 被扩缩容对象所在命名空间。 |
options.target | 被 CPA 调整副本数的目标对象,格式如 deployment/placeholder-pods。 |
options.pollPeriodSeconds | CPA 轮询集群规模并更新副本数的周期。 |
线性模式的核心计算逻辑:
replicas = max(
ceil(cores / coresPerReplica),
ceil(nodes / nodesPerReplica)
)
replicas = min(replicas, max)
replicas = max(replicas, min)
如果只配置 nodesPerReplica,就只按节点数计算;如果同时配置 coresPerReplica 和 nodesPerReplica,CPA 会取两者计算结果中更大的值。
例如 10 个可调度节点、nodesPerReplica: 2 时,目标副本数为 ceil(10 / 2) = 5;如果
nodesPerReplica: 0.667,目标副本数约为 ceil(10 / 0.667) = 15。最后再应用 min 和 max 边界。
安装:
# 添加 Helm 仓库
helm repo add cluster-proportional-autoscaler \
https://kubernetes-sigs.github.io/cluster-proportional-autoscaler
helm repo update
# 安装
helm install placeholder-autoscaler \
cluster-proportional-autoscaler/cluster-proportional-autoscaler \
-f placeholder-autoscaler-values.yaml \
--namespace default
步骤 4:配置业务 Pod 优先级
确保业务 Pod 优先级高于占位 Pod:
spec:
priorityClassName: system-cluster-critical # 或 high-priority
containers:
- name: app
resources:
requests:
cpu: "4000m"
memory: "8Gi"
效果验证
# 查看占位 Pod 分布
kubectl get pods -l app=placeholder -o wide
# 模拟业务 Pod 扩容
kubectl scale deployment business-app --replicas=15
# 观察占位 Pod 被抢占
kubectl get pods -l app=placeholder -w
# 观察业务 Pod 快速获得资源
kubectl get pods -l app=business -w
# 观察 CA 扩容节点
kubectl get nodes -w
扩缩容时序分析
10 节点集群,推荐策略(nodesPerReplica=2),扩容 15 个业务 Pod:
T0: 初始状态
- 10 节点,占位 Pod:5 个
- 业务 Pod:20 个运行中
T1 (+5秒): 业务 Pod 扩容
✓ 5 个业务 Pod 立即抢占占位 Pod(33% 即时响应)
- 10 个业务 Pod Pending
T2 (+10秒): CA 检测到 Pending
- 开始扩容 5 个节点
T3 (+2-5分钟): 稳定状态
- 15 节点,占位 Pod:8 个
- 业务 Pod:35 个运行
对比传统方式:
- 传统:15 个 Pod 全部等待 2-5 分钟
- 占位 Pod:5 个立即响应(33%),10 个等待 2-5 分钟
成本分析
| 集群规模 | 策略 | 占位 Pod | 占位资源 | 缓冲比例 | 年成本估算* |
|---|---|---|---|---|---|
| 10 节点 | 推荐 | 5 个 | 20C + 40GB | 16.7% | ~8,640 USD |
| 10 节点 | 水位线 | 15 个 | 60C + 120GB | 37.5% | ~25,920 USD |
| 30 节点 | 推荐 | 15 个 | 60C + 120GB | 16.7% | ~25,920 USD |
| 100 节点 | 推荐 | 50 个 | 200C + 400GB | 16.7% | ~86,400 USD |
* 按 0.05 USD/核/小时估算,实际成本取决于云服务商定价
注意:
- 资源预留成本:高(占用可调度资源)
- 实际运行成本:极低(pause 容器几乎不消耗资源)
监控与告警
关键指标:
# 占位 Pod 总数
kubectl get pods -l app=placeholder --no-headers | wc -l
# Pending 状态的占位 Pod
kubectl get pods -l app=placeholder --field-selector=status.phase=Pending
# 抢占事件
kubectl get events --field-selector reason=Preempted
告警规则(Prometheus):
# 占位 Pod 持续 Pending 超过 10 分钟(CA 可能异常)
- alert: PlaceholderPodsPendingTooLong
expr: count(kube_pod_status_phase{pod=~"placeholder-pods-.*", phase="Pending"}) > 5
for: 10m
annotations:
summary: "占位 Pod 持续 Pending,检查 CA 是否正常工作"
最佳实践
-
资源规格对齐:占位 Pod 的资源请求必须与业务 Pod 一致,如果有多种规格,需要部署多组占位 Pod
-
优先级设计:
- 业务关键 Pod:
system-cluster-critical(2000000000) - 业务普通 Pod:
high-priority(1000) - 占位 Pod:
placeholder-priority(-10)
- 业务关键 Pod:
-
缓冲比例选择:
- 突发流量大:激进策略(33% 缓冲)
- 平衡场景:推荐策略(17-20% 缓冲)
- 成本敏感:保守策略(10-13% 缓冲)
-
准确计算容量:
节点总资源 - 系统组件 = 可用资源,在测试环境验证实际可运行的 Pod 数量 -
节点异构处理:如果集群有多种规格节点,为每种规格部署独立的占位 Pod 和 CPA 实例
-
监控告警:持续监控占位 Pod Pending 数量,超过阈值说明 CA 可能异常
-
灰度验证:先在少量节点验证,确认抢占机制正常后再全面推广
-
动态调整:高峰时段提高缓冲,低峰时段降低缓冲(可通过 CronJob 调整 CPA 配置)
-
环境区分:生产环境使用推荐/水位线策略,测试环境使用保守策略或不使用
-
成本评估:根据实际扩容频率和响应速度提升,评估投资回报率
故障排查
占位 Pod 一直 Pending
可能原因:
- CA 未正常工作
- 节点规格限制(节点组已达上限)
- 资源配额限制
排查:
kubectl logs -n kube-system -l app=cluster-autoscaler -f
kubectl describe configmap cluster-autoscaler-status -n kube-system
业务 Pod 没有抢占占位 Pod
可能原因:
- 业务 Pod 优先级不高于占位 Pod
- 业务 Pod 没有配置 PriorityClass
排查:
kubectl get priorityclass
kubectl describe pod <business-pod> | grep Priority
kubectl get events --field-selector reason=Preempted
占位 Pod 数量不符合预期
可能原因:
- CPA 配置错误
- min/max 限制触发
排查:
kubectl logs -n default -l app=cluster-proportional-autoscaler -f
# 手动计算预期数量:ceil(节点数 / nodesPerReplica)
方案局限性
核心限制
业务 Pod 资源规格必须统一
占位 Pod 是固定规格(如 4C/8G),要求所有业务 Pod 资源请求相同或接近。
问题:
- 小规格 Pod(2C/4G)抢占大占位 Pod(4C/8G)→ 浪费 2C/4G 资源
- 大规格 Pod(8C/16G)无法抢占小占位 Pod(4C/8G)→ 必须等待 CA 扩容
解决方案:
- 按规格分类部署多组占位 Pod(小、中、大规格)
- 标准化业务 Pod 资源规格(如仅 2C/4G、4C/8G、8C/16G)
- 为每种规格部署独立的 CPA 实例