K8s HPA 自动扩缩容与资源配额实战从 Metrics Server 到 ResourceQuota、LimitRange学习参考Metrics ServerHPA 官方文档资源配额限制范围业务流量白天高峰要 20 个 Pod半夜低谷只要 2 个——手动改副本数太累。K8s 提供了 **HPAHorizontal Pod Autoscaler**自动根据 CPU/内存负载扩缩 Pod 数量。但扩缩容的前提是你得先告诉 K8s 每个 Pod 该用多少资源requests/limits而多团队共享集群时还要用ResourceQuota给每个 namespace 分配总资源、用LimitRange给单个容器设默认值。本文从 Metrics Server 部署开始完整跑通 HPA CPU 扩缩、内存扩缩、scaleUp/scaleDown 精细管控最后讲 ResourceQuota 和 LimitRange。一、Metrics ServerK8s 的监控数据采集1.1 为什么需要 Metrics ServerK8s 本身不存储监控指标。你要知道节点和 Pod 的 CPU/内存用了多少HPA 要根据什么来扩缩容——都靠 Metrics Server。Metrics Server 通过Kubelet收集资源指标通过Metrics API暴露给 apiserver供 HPA/VPA 使用也支持kubectl top命令每 15 秒采集一次轻量每节点 1m CPU 2MB 内存不适用于长期存储要持久化监控请用 Prometheus1.2 部署 Metrics Server# 下载rootmaster30:~# wget -O metrics-server-components.yaml https://laoma.cloud/class/course-materials/softwares/stage03/metrics-server-components-v0.7.1.yaml# 修改镜像地址rootmaster30:~# sed -i s/registry.k8s.io/hub.laoma.cloud/g metrics-server-components.yaml# 部署rootmaster30:~# kubectl apply -f metrics-server-components.yamlrootmaster30:~# kubectl get pods -n kube-system | grep metricsmetrics-server-6556f8cb6c-78mhq1/1 Running045s注意测试环境需要加--kubelet-insecure-tls参数跳过 TLS 校验。1.3 使用# 查看节点资源使用rootmaster30:~# kubectl top nodeNAME CPU(cores)CPU% MEMORY(bytes)MEMORY% master30.laoma.cloud 97m4% 1328Mi35% worker31.laoma.cloud 49m2% 912Mi24% worker32.laoma.cloud 45m2% 926Mi24%# 查看 Pod 资源使用rootmaster30:~# kubectl top pods -n kube-systemNAME CPU(cores)MEMORY(bytes)calico-node-crfr7 27m 57Mi coredns-6d56c8448f-nhjpg 3m 12Mi kube-apiserver-master.laoma.cloud 46m 355Mi metrics-server-6556f8cb6c-78mhq 1m 11Mi单位1 CPU 1000m毫核二、HPA 基于 CPU 使用率自动扩缩容2.1 HPA 是什么HPA 根据定义的指标动态调整 Pod 数量支持 Deployment、ReplicaSet、StatefulSet。适用场景流量波动大的无状态服务Nginx、API、网关、微服务需要提高并发、削峰填谷核心特点不修改 Pod 配置只改副本数实时、秒级扩缩最常用、最稳定关键参数kube-controller-manager 控制参数默认值说明--horizontal-pod-autoscaler-sync-period15s指标采样周期--horizontal-pod-autoscaler-initial-readiness-delay30sPod 启动就绪窗口期不参与 HPA 计算--horizontal-pod-autoscaler-downscale-stabilization300s缩容冷却时间实验环境为了快速演示可以调小rootmaster30:~# vim /etc/kubernetes/manifests/kube-controller-manager.yaml# 增加# --horizontal-pod-autoscaler-sync-period10s# --horizontal-pod-autoscaler-initial-readiness-delay20s# --horizontal-pod-autoscaler-downscale-stabilization30s# 静态 Pod 自动重启生效2.2 创建 HPA# 准备 Deploymentrootmaster30:~# kubectl create deployment web --imagehub.laoma.cloud/library/nginx# 创建 HPA最少 2 个最多 5 个CPU 目标 80%rootmaster30:~# kubectl autoscale deployment web --max5 --min2 --cpu-percent80等价 YAMLautoscaling/v1只支持 CPUapiVersion:autoscaling/v1kind:HorizontalPodAutoscalermetadata:name:webspec:maxReplicas:5minReplicas:2scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:webtargetCPUUtilizationPercentage:80rootmaster30:~# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu:unknown/80%25234s# TARGETS 是 unknownTARGETS 显示 unknown 的两个条件必须安装 Metrics Server必须为 Pod 设置 resources.limitsHPA 按 limits 的百分比算2.3 给 Pod 设置资源限制rootmaster30:~# kubectl edit deployment web# 添加resources: limits: cpu: 100m memory: 200Mirootmaster30:~# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu:0%/80%25283s# TARGETS 正常显示了# 暴露 Servicerootmaster30:~# kubectl expose deployment web --port80 --target-port802.4 压测验证扩容# 监控窗口rootmaster30:~# \whiletruedoclear;date;echokubectl get hpa;echokubectl get pod;echokubectltoppodssleep1done# 上 CPU 压力rootmaster30:~# apt install -y apache2-utilsrootmaster30:~# while true; do ab -n 300000 -c 100 http://10.100.255.92/; sleep 1; done观察过程CPU 飙升到 100% 超过 80% 阈值约 20 秒后 HPA 自动新增 Pod2 → 3 → 4 → 5即使 CPU 持续高负载Pod 数不超过 maxReplicas5停止压测后负载降低Pod 数逐步缩回到 minReplicas2cpu: 100%/80% 2 5 2 → cpu: 95%/80% 2 5 3 → cpu: 99%/80% 2 5 4 → cpu: 100%/80% 2 5 5三、HPA 基于内存使用率自动扩缩容基于内存扩缩容需要用autoscaling/v2APIv1 只支持 CPU。3.1 准备会占内存的应用让 Nginx 返回一个大文件来占用内存# 在 worker 节点准备大文件[rootworker31 ~]# mkdir /www dd if/dev/zero of/www/big.img bs1M count200[rootworker32 ~]# mkdir /www dd if/dev/zero of/www/big.img bs1M count200apiVersion:apps/v1kind:Deploymentmetadata:name:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-name:nginximage:nginxvolumeMounts:-name:big-filemountPath:/usr/share/nginx/htmlresources:limits:cpu:100mmemory:200Mivolumes:-name:big-filehostPath:path:/www3.2 创建内存 HPAapiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:webspec:minReplicas:2maxReplicas:5scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:webmetrics:-resource:name:memorytarget:type:UtilizationaverageUtilization:60type:Resourcerootmaster30:~# kubectl apply -f hpa-mem.yamlrootmaster30:~# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory:2%/60%25113m3.3 压测# 访问大文件内存占用上来rootmaster30:~# while true; do ab -n 300000 -c 100 http://10.99.129.137/big.img; sleep 1; done观察内存使用率从 2% 飙到 86% 超过 60% 阈值Pod 从 2 逐步扩到 5。停止压测后缩回到 2。⚠️ 内存压力太大时 Pod 可能被 OOMKilled。四、HPA 精细管控scaleUp / scaleDown从 K8s 1.18 起可以通过spec.behavior精细控制扩缩容速度避免流量毛刺导致反复扩缩。4.1 扩容scaleUpspec:behavior:scaleUp:stabilizationWindowSeconds:0# 扩容稳定窗口默认0立刻扩policies:-type:Percentvalue:100# 每周期最多新增当前副本的100%翻倍periodSeconds:15-type:Podsvalue:4# 每周期最多新增4个PodperiodSeconds:15selectPolicy:Max# 两条策略取允许更多的默认扩容策略解读稳定窗口 0 秒指标一超标立刻扩每 15 秒最多翻倍 或 最多加 4 个 PodselectPolicy: Max取两条策略中允许扩容更多的那个4.2 缩容scaleDownspec:behavior:scaleDown:stabilizationWindowSeconds:300# 缩容稳定窗口默认300秒5分钟policies:-type:Percentvalue:100periodSeconds:15selectPolicy:Max默认缩容策略解读稳定窗口 5 分钟窗口内取最大期望副本数作为目标防止流量抖动反复删 Pod每 15 秒最多删 100% 副本理论上一次缩到 minReplicas4.3 扩缩容对比配置项scaleUp扩容scaleDown缩容stabilizationWindowSeconds 默认值03005 分钟稳定窗口取值逻辑窗口内取最小期望副本窗口内取最大期望副本默认 policies 条数2 条Percent Pods1 条仅 Percent触发阈值利用率 target × 1.1利用率 target × 0.9设计思想优先快速扩容保障业务谨慎缩容避免流量反弹4.4 平缓缩容配置示例spec:behavior:scaleDown:stabilizationWindowSeconds:600# 等10分钟确认低负载policies:-type:Percentvalue:10# 每分钟最多缩10%periodSeconds:60-type:Podsvalue:2# 每分钟最多删2个periodSeconds:60selectPolicy:Min# 保守取删得更少的五、VPAVertical Pod AutoscalerVPA 不改 Pod 数量而是动态调整 Pod 的 CPU/内存 requests 和 limits。适合资源配置不合理的服务、长期运行的稳定服务生效需要重启 Pod有侵入性不能和 HPA 同时对同一资源使用会冲突项目HPA 横向VPA 纵向扩缩方式增删 Pod 数量调整 CPU/内存 request/limit是否重启 Pod不重启一般需要重启适合负载流量波动大资源配置不合理生产使用非常普遍较少六、ResourceQuota命名空间资源配额多团队共享集群时有人会用超公平分配的资源量。ResourceQuota 对每个命名空间的资源消耗总量提供限制。6.1 两种配额类型对象数量限制 Pod、Service、ConfigMap、Secret、Deployment 等资源个数计算资源限制 CPU、内存、存储的总量6.2 创建对象数量配额rootmaster30:~# kubectl create quota myquota --hardpods2,services3,secrets5,persistentvolumeclaims10rootmaster30:~# kubectl get resourcequotasNAME AGE REQUEST LIMIT my-quota 10s persistentvolumeclaims:0/10, pods:0/2, secrets:1/5, services:0/3等价 YAMLapiVersion:v1kind:ResourceQuotametadata:name:myquotaspec:hard:pods:2services:3secrets:5persistentvolumeclaims:10测试创建 3 副本 Deployment但配额只允许 2 个 Podrootmaster30:~# kubectl create deployment web --imagenginx --replicas3rootmaster30:~# kubectl get allpod/web-96d5df5c8-dl7qk1/1 Running pod/web-96d5df5c8-j7fhh1/1 Running# 第3个 Pod 创建失败rootmaster30:~# kubectl describe rs web-96d5df5c8Error creating: podsxxxis forbidden: exceeded quota: myquota, requested:pods1, used:pods2, limited:pods26.3 创建计算资源配额apiVersion:v1kind:ResourceQuotametadata:name:myquotaspec:hard:requests.cpu:1000mrequests.memory:2048Milimits.cpu:1000mlimits.memory:2048Mi资源名称含义requests.cpu所有非终止 Pod 的 CPU 请求总和不超过此值requests.memory所有非终止 Pod 的内存请求总和不超过此值limits.cpu所有非终止 Pod 的 CPU 限制总和不超过此值limits.memory所有非终止 Pod 的内存限制总和不超过此值单位CPU 1 核 1000m内存 Ki/Mi/Gi1024 进制或 k/M/G1000 进制。⚠️关键规则如果命名空间设置了计算资源配额创建 Pod 时必须指定 requests 和 limits否则直接拒绝创建。七、requests 和 limits 到底是什么resources:requests:# 调度器保证的最少资源cpu:200mmemory:256Milimits:# cgroup 硬上限超过会被限流或 OOMKillcpu:500mmemory:512Mirequests调度器按 requests 选节点保证 Pod 能分到这么多资源limits运行时 cgroup 限制CPU 超过会被限流不会杀内存超过会被 OOMKill7.1 CPU 是可压缩资源CPU 超限不会杀进程只是被减速——调度时间片减少。# 限制 200m CPU实际跑满resources:limits:cpu:200mrootmaster30:~# kubectl top podsNAME CPU(cores)MEMORY(bytes)stress 201m 0Mi# 稳定在 200m 左右不会超过7.2 内存是不可压缩资源内存超限直接 OOMKillPod 重启# limit 256Mi但实际用 512Mresources:limits:memory:256Miargs:[-m,1,--vm-bytes,512M]rootmaster30:~# kubectl get pods -wNAME READY STATUS RESTARTS AGE stress0/1 OOMKilled1(2s ago)3s stress0/1 CrashLoopBackOff14slimits 总和可以超过节点总资源超卖CPU 是可压缩的超卖没问题。内存超卖时K8s 会先杀没设 request 的 Pod再杀超过 request 最多的 Pod。八、LimitRange给单个容器设默认值和上下限命名空间设了 ResourceQuota 后不写 requests/limits 的 Pod 直接拒绝。LimitRange 就是给这个命名空间设默认值和上下限。apiVersion:v1kind:LimitRangemetadata:name:mylimitspec:limits:-type:Containermax:# 容器最大不能超过memory:1024Micpu:1min:# 容器最小不能低于memory:128Micpu:100mdefault:# 默认 limitmemory:512Micpu:500mdefaultRequest:# 默认 requestmemory:256Micpu:200m规则不写 resources → 自动套用 default / defaultRequest写的 limit 小于 min 或大于 max → 拒绝创建只写 limit 不写 request → request limit只写 request 不写 limit → limit default必须满足min ≤ request ≤ limit ≤ maxrootmaster30:~# kubectl describe limitranges mylimitType Resource Min Max Default Request Default Limit ---- -------- --- --- --------------- ------------- Container cpu 100m1200m 500m Container memory 128Mi 1Gi 256Mi 512Mi限定资源类型TypeResource说明Containercpu、memory限定单个容器Podcpu、memory限定 Pod 所有容器总和PersistentVolumeClaimstorage限定 PVC 申请存储大小九、总结组件一句话Metrics ServerK8s 内置监控采集给 HPA 和kubectl top供数据HPA根据 CPU/内存负载自动增删 Pod 数量VPA自动调 Pod 的 requests/limits需要重启和 HPA 冲突ResourceQuota限制整个 namespace 的资源总量requests调度器保证的最少资源选节点用limitscgroup 硬上限CPU 超了限流内存超了 OOMKillLimitRange给 namespace 内容器设默认值和上下限环境清理kubectl delete ns metric quota
阅读完成 · 觉得有帮助?