痛点:Cluster Autoscaler 扩缩太慢,业务高峰白等 3 分钟
跑 Kubernetes 集群的运维都遇到过这个场景:流量突增,Pod 因为资源不足处于 Pending 状态,Cluster Autoscaler (CA) 开始工作——但从检测到 Pending Pod、选择 Node Group、调用云厂商 API 创建实例、到节点 Ready,整个链路动辄 3-5 分钟。对于电商大促、直播间涌入等场景,这个延迟直接等于丢用户。
CA 的核心问题不止是慢:
- Node Group 预定义限制:你必须提前规划好哪些机型组合放在哪个 ASG 里,灵活性差
- 扩缩决策粗粒度:CA 按 Node Group 为单位扩容,无法按 Pod 实际需求精确选型
- 缩容保守:默认 10 分钟冷却期,闲置节点占着资源烧钱
AWS 在 2023 年将 Karpenter 捐赠给 CNCF 并升级为通用方案后,2025-2026 年它已经成为 EKS/GKE/AKS 上节点弹性的事实标准。今天用实操带你完成迁移。
方案:Karpenter — Just-in-Time 节点供给
Karpenter 的设计哲学是 "按 Pod 需求即时供给节点":
| 对比维度 | Cluster Autoscaler | Karpenter |
|---|---|---|
| 扩容触发 | 定时轮询 Pending Pod(10-60s) | Watch API Server,秒级响应 |
| 机型选择 | 预定义 Node Group 固定机型 | 实时从数十种机型中智能选优 |
| 缩容策略 | 整个节点空闲后等待冷却期 | 主动整合(Consolidation),持续优化 |
| 配置复杂度 | 多个 ASG + Launch Template | 一个 NodePool CRD 搞定 |
| 成本优化 | 手动配 Spot 比例 | 自动混合 On-Demand/Spot,选最便宜可用实例 |
实操步骤
第 1 步:安装 Karpenter(以 EKS 为例)
# 环境变量
export KARPENTER_VERSION="1.2.0"
export CLUSTER_NAME="prod-cluster"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export TEMPOUT=$(mktemp)
# 创建 Karpenter 所需 IAM 角色和 SQS 中断队列
curl -fsSL "https://raw.githubusercontent.com/aws/karpenter-provider-aws/v${KARPENTER_VERSION}/website/content/en/docs/getting-started/getting-started-with-karpenter/cloudformation.yaml" > "${TEMPOUT}"
aws cloudformation deploy \
--stack-name "Karpenter-${CLUSTER_NAME}" \
--template-file "${TEMPOUT}" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "ClusterName=${CLUSTER_NAME}"
# 用 Helm 安装 Karpenter
helm registry logout public.ecr.aws || true
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version "${KARPENTER_VERSION}" \
--namespace kube-system \
--set "settings.clusterName=${CLUSTER_NAME}" \
--set "settings.interruptionQueue=Karpenter-${CLUSTER_NAME}" \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--wait
第 2 步:定义 NodePool 和 EC2NodeClass
# nodepool.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"] # 优先 Spot,Spot 不可用自动切 OD
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"] # 计算/通用/内存型均可
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["5"] # 只用第 6 代及以上实例
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "1000" # 集群 CPU 上限
memory: 2000Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 60s # 空闲 60 秒即触发整合
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
role: "KarpenterNodeRole-${CLUSTER_NAME}"
amiSelectorTerms:
- alias: al2023@latest # Amazon Linux 2023 最新 AMI
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "${CLUSTER_NAME}"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "${CLUSTER_NAME}"
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
iops: 3000
throughput: 125
kubectl apply -f nodepool.yaml
第 3 步:验证弹性伸缩效果
# 部署一个压测 Deployment,触发扩容
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 0
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: "1"
memory: 1.5Gi
EOF
# 扩到 20 副本,观察 Karpenter 秒级创建节点
kubectl scale deployment inflate --replicas=20
# 观察节点创建过程(通常 30-90 秒内完成)
kubectl get nodes -w
# 查看 Karpenter 日志确认选型逻辑
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -c controller --tail=50
第 4 步:迁移后移除旧 Cluster Autoscaler
# 确认所有工作负载已迁移到 Karpenter 管理的节点
kubectl get nodes -L karpenter.sh/nodepool
# 缩容旧的 Managed Node Group(逐步排空)
aws eks update-nodegroup-config \
--cluster-name $CLUSTER_NAME \
--nodegroup-name old-managed-ng \
--scaling-config minSize=0,maxSize=0,desiredSize=0
# 卸载 Cluster Autoscaler
helm uninstall cluster-autoscaler -n kube-system
避坑指南
1. Spot 中断处理必须配 SQS 队列
Karpenter 依赖 SQS 接收 EC2 Spot 中断通知和实例维护事件,提前 2 分钟优雅迁移 Pod。如果没配 interruptionQueue,Spot 回收时 Pod 会被强杀。
# 验证 SQS 队列正常接收事件
aws sqs get-queue-attributes \
--queue-url "https://sqs.us-east-1.amazonaws.com/${AWS_ACCOUNT_ID}/Karpenter-${CLUSTER_NAME}" \
--attribute-names ApproximateNumberOfMessages
2. PDB(Pod Disruption Budget)配置不当导致缩容卡死
Karpenter 的 Consolidation 机制会主动迁移 Pod 以释放节点,如果你的 PDB 设置了 maxUnavailable: 0,节点永远无法被清空。建议:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: "80%" # 而非 maxUnavailable: 0
selector:
matchLabels:
app: my-app
3. 节点启动耗时优化:AMI 预热 + 快速启动
默认节点从创建到 Ready 需要 60-90 秒,瓶颈在拉取容器镜像。两个加速手段:
- 自定义 AMI 预烘焙:把大镜像(如 GPU 驱动、ML 框架)烤进 AMI
- EC2 Fast Launch:预创建 EBS 快照加速启动
# EC2NodeClass 中启用 Fast Launch
spec:
amiSelectorTerms:
- id: ami-xxxx # 预烘焙的自定义 AMI
总结
Karpenter 相比 Cluster Autoscaler 的核心提升:扩容从分钟级降到秒级,成本通过智能选型 + 主动整合平均降低 30-50%。
迁移建议:
- 先在非生产环境跑 1-2 周,观察选型和 Consolidation 行为
- 生产环境用
disruption.budgets限制同时中断的节点百分比 - 监控
karpenter_nodes_created、karpenter_pods_startup_duration_seconds两个核心指标 - 如果跑 GPU 工作负载,单独建一个 NodePool 限制
instance-family: ["p", "g"]
Karpenter 已是 CNCF Incubating 项目,多云支持逐步完善。现在迁移,正是时候。