K8s集群管理实战:从部署到自动伸缩
一个典型的翻车现场:电商大促前,运维把 Deployment 的 replicas 从 4 手改成 20,Pod 起来了,流量也进来了,结果 HPA 同时也在扩,两边打架,节点资源被挤爆,一批 Pod 卡在 Pending。问题不在 HPA,在于没人给扩容定过规则。这篇从一次部署开始,把资源请求、HPA、节点扩容串成一条线。
一、先把 Deployment 写对
自动伸缩的前提是 Pod 有明确的资源请求。没有 requests,HPA 算出来的利用率是空的,调度器也不知道该把 Pod 放哪。先看一个能用的 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
selector:
matchLabels: { app: api-server }
template:
metadata:
labels: { app: api-server }
spec:
containers:
- name: api
image: registry.example.com/api-server:1.8.2
ports: [{ containerPort: 8080 }]
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { cpu: 500m, memory: 512Mi }
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 10
关键点在 requests 和 limits 的比例。CPU 设成 1:2 是常见做法,留出突发空间;内存别这么干,内存超 limit 直接被 OOMKill,比例设 1:1 或 1:1.2 更稳。readinessProbe 必须配,否则新 Pod 还没准备好就被塞流量,扩容反而制造 502。
另一个坑:image 用了固定 tag 而不是 latest,这是故意的。滚动更新时 tag 不变,K8s 不会重新拉镜像,你会以为发布成功了其实跑的还是旧代码。
二、HPA 别只看 CPU
Deployment 跑起来之后,流量一涨,手动改副本数就跟不上了。上 HPA:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 30
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 60 }
- type: Pods
pods:
metric: { name: http_requests_per_second }
target: { type: AverageValue, averageValue: "200" }
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
这里的重点是 behavior。默认 HPA 缩容窗口是 5 分钟,扩容没有限制,流量尖峰时可能一口气翻几倍。scaleUp 设成 60 秒内最多翻倍,避免把节点打爆;scaleDown 留 300 秒稳定窗口,防止指标抖动导致副本数上上下下。自定义指标 http_requests_per_second 需要 Prometheus Adapter 把指标喂给 HPA,CPU 指标只是兜底。
踩坑提醒:averageUtilization: 60 是相对 requests 算的。requests 写 250m,60% 就是 150m 触发扩容。如果 requests 填得太小(比如 50m),HPA 会疯狂扩容,因为随便一点流量就超过阈值。
三、Pod 扩了,节点装不下怎么办
HPA 把副本从 3 拉到 30,节点只剩 2 个,多出来的 Pod 全 Pending。这时候需要节点层面的弹性,Cluster Autoscaler 或 Karpenter 二选一。
| 维度 | Cluster Autoscaler | Karpenter |
|---|---|---|
| 触发方式 | 监听 Pending Pod | 监听 Pending Pod + 直接调度 |
| 节点选择 | 按 NodeGroup 预设规格 | 按 Pod 需求实时挑实例类型 |
| 缩容 | 有空节点才缩 | 主动合并、驱逐 |
| 配置复杂度 | 低,云厂商托管常见 | 略高,需自建 CRD |
小集群用 Cluster Autoscaler 够用,配好 NodeGroup 的 min/max 就行。Karpenter 适合实例类型多、成本敏感的团队,它能在几十秒内起一台刚好装下 Pod 的机器,不用等 NodeGroup 预设。选型看一点:你的节点规格是不是长期固定?固定就 CA,不固定就 Karpenter。
四、把这条链接起来验证
部署完别急着庆祝,跑一遍压测看链路是否通:
kubectl get hpa api-server-hpa -w
kubectl get pods -l app=api-server -w
kubectl get nodes -w
三个窗口一起看。正常表现是:HPA 的 TARGETS 先涨,Pod 数跟着涨,节点数最后涨。如果 Pod 一直 Pending 而节点没动静,八成是 Cluster Autoscaler 的 NodeGroup 到了 max 上限,或者 Pod 的 requests 超过了任何可用实例规格——后者最隐蔽,Pod 永远调度不上去。
再补一条:给 HPA 加 minReplicas 不低于 3,单可用区挂掉时还有余量。大促前把 maxReplicas 提前调高,别等流量来了再改,HPA 读的是当前 spec,改完要几十秒才生效。
核心收获就一句:自动伸缩不是打开 HPA 就完事,requests 定基准、HPA 定节奏、节点扩容兜底,三者缺一个就会在流量高峰翻车。下一步动作很具体——先给现有 Deployment 补上 requests 和 readinessProbe,再开 HPA 观察一周指标,最后决定要不要接节点自动扩容。
本文关键词:Kubernetes、HPA、Cluster Autoscaler、Karpenter、Deployment