一、HPA 水平自动伸缩(Horizontal Pod Autoscaler)
1.1 概念
HPA 基于 CPU 利用率(或其他自定义指标)自动扩缩 Deployment / ReplicaSet / ReplicationController 的 Pod 副本数 ;由 K8s API 资源 + 控制器实现,控制器周期性调整副本数,使 Pod 平均指标与目标值匹配。
-
不适用于 DaemonSet(无法扩缩)。
-
生产四类指标: 表格
指标类型 说明 示例 Resource metrics CPU / 内存利用率(metrics-server 提供) CPU 50% Pod metrics Pod 维度指标(网络利用率、流量) 出站带宽 Object metrics 特定对象指标(Ingress 每秒请求数) requests/sec Custom metrics 自定义监控(服务响应时间、QPS) nginx QPS,需 Prometheus Adapter
1.2 前置:部署 metrics-server(提供 CPU / 内存指标)
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml
# 换国内镜像源
sed -i 's/registry.k8s.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml
⚠️ 报错案例 :v0.8.0 后安全要求高,必须加
--kubelet-insecure-tls,否则拉不到 kubelet 指标、探针失败:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-insecure-tls # 添加
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --metric-resolution=15s
kubectl apply -f metrics-server-components.yaml
# 个别 Pod 长时间 0/1:直接 kubectl delete pod 让 Deployment 重建
kubectl top nodes
kubectl top pods
1.3 案例一:CPU 指标 HPA 实战
① 部署 nginx 应用 + Service
apiVersion: apps/v1
kind: Deployment
metadata: {labels: {app: nginx}, name: nginx, namespace: default}
spec:
replicas: 2
selector: {matchLabels: {app: nginx}}
template:
metadata: {labels: {app: nginx}}
spec:
containers:
- name: nginx
image: nginx:1.26-alpine
imagePullPolicy: IfNotPresent
resources:
requests: {cpu: 200m, memory: 100Mi} # HPA 利用率按 requests 算,必须写 requests
---
apiVersion: v1
kind: Service
metadata: {name: nginx, namespace: default}
spec:
type: NodePort
ports: [{port: 80, targetPort: 80}]
selector: {app: nginx}
② 创建 HPA(autoscaling/v2,CPU 超 50% 扩容,1~10 副本)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: nginx-hpa, namespace: default}
spec:
scaleTargetRef: # 必须与 Deployment 名称一致
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 平均 CPU 利用率 >50% 触发扩容
kubectl apply -f nginx-hpa.yaml
kubectl get hpa # TARGETS 0%/50%,MINPODS 1,MAXPODS 10
③ 压测与观测
yum install -y httpd-tools
ab -c 10000 -n 10000000000 http://192.168.18.129:30699/
# 另开终端 watch kubectl get pods 观测:2 → 9 个 Pod
# 压测停止约 5 分钟后自动缩回 1 个
缩容有冷却窗口(stabilization),指标回落后不会立刻降副本。
二、Prometheus + HPA 自定义指标(按 QPS 伸缩)
2.1 前置环境调整
# 验证 metrics-server
kubectl top pods -n kube-system
# kube-proxy 开启 strictARP(MetalLB IPVS 必需)
kubectl edit configmap kube-proxy -n kube-system # ipvs.strictARP: true
kubectl rollout restart daemonset kube-proxy -n kube-system
2.2 部署 MetalLB(LoadBalancer 地址池,二层模式)
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml
ippool.yaml(地址池):
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata: {name: first-pool, namespace: metallb-system}
spec:
addresses: ["192.168.18.240-192.168.18.250"]
l2.yaml(二层通告):
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata: {name: example, namespace: metallb-system}
kubectl apply -f ippool.yaml -f l2.yaml
kubectl get ipaddresspool -n metallb-system
2.3 部署 ingress-nginx(v1.13.2)
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.2/deploy/static/provider/cloud/deploy.yaml
# 把 externalTrafficPolicy: Local 改为 Cluster(实验环境)
kubectl apply -f deploy.yaml
# 等 5 分钟,controller Service 获 EXTERNAL-IP 192.168.18.240
2.4 Helm 安装 kube-prometheus-stack(Prometheus + Grafana)
# 装 Helm3.19.0:解压把 helm 移到 /usr/bin/
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm show values prometheus-community/kube-prometheus-stack --version 77.6.2 > kube-prometheus-stack.yaml
关键修改:
serviceMonitorSelectorNilUsesHelmValues: false(关闭只监控本 chart,才能发现所有应用 ServiceMonitor)。
helm install kps prometheus-community/kube-prometheus-stack --version 77.6.2 \
-f ./kube-prometheus-stack.yaml -n monitoring --create-namespace
Ingress 暴露 Prometheus / Grafana:
# prometheus-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: {name: ingress-prometheus, namespace: monitoring}
spec:
ingressClassName: nginx
rules:
- host: prometheus.abner.com
http:
paths:
- path: /
pathType: Prefix
backend: {service: {name: kps-kube-prometheus-stack-prometheus, port: {number: 9090}}}
# grafana-ingress.yaml 同理 host: grafana.abner.com → kps-grafana:80
kubectl apply -f prometheus-ingress.yaml -f grafana-ingress.yaml
# Windows hosts 加 192.168.18.240 prometheus.abner.com / grafana.abner.com
# Grafana 账号在 Secret 里 base64 解码:
kubectl get secret kps-grafana -n monitoring -o yaml
echo -n "YWRtaW4=" | base64 --decode # admin
echo -n "cHJvbS1vcGVyYXRvcg==" | base64 --decode # prom-operator
2.5 部署带 exporter 的 nginx 应用
① nginx.conf 开启 stub_status
worker_processes 1;
events { worker_connections 1024; }
http {
server {
listen 80;
location / { root /usr/share/nginx/html; index index.html; }
location /basic_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
}
kubectl create configmap nginx-config --from-file=nginx.conf
② 双容器 Pod:nginx + nginx-prometheus-exporter
apiVersion: apps/v1
kind: Deployment
metadata: {name: nginx-with-exporter, namespace: default}
spec:
replicas: 2
selector: {matchLabels: {app: nginx}}
template:
metadata: {labels: {app: nginx}}
spec:
containers:
- name: nginx
image: nginx:1.26-alpine
imagePullPolicy: IfNotPresent
ports: [{containerPort: 80}]
volumeMounts:
- {mountPath: /etc/nginx/nginx.conf, subPath: nginx.conf, name: nginx-config-volume}
resources: {requests: {cpu: 100m, memory: 100Mi}}
- name: nginx-prometheus-exporter
image: nginx/nginx-prometheus-exporter:latest
args: ["-nginx.scrape-uri=http://localhost/basic_status"]
ports: [{containerPort: 9113, name: exporter-port}] # 端口名供 relabel 筛选
resources: {requests: {cpu: 50m, memory: 100Mi}}
volumes:
- name: nginx-config-volume
configMap: {name: nginx-config}
---
apiVersion: v1
kind: Service
metadata: {name: nginx, namespace: default}
spec:
type: NodePort
ports: [{port: 80, targetPort: 80}]
selector: {app: nginx}
③ Prometheus 抓取配置(additionalScrapeConfigs)
在 kube-prometheus-stack.yaml 中追加:
additionalScrapeConfigs:
- job_name: 'nginx'
kubernetes_sd_configs: [{role: pod}]
relabel_configs:
- source_labels: [__meta_kubernetes_pod_container_port_name]
action: keep
regex: exporter-port # 只保留端口名为 exporter-port 的端点
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
helm upgrade kps prometheus-community/kube-prometheus-stack --version 77.6.2 \
-f ./kube-prometheus-stack.yaml -n monitoring
2.6 部署 prometheus-adapter(关键桥接组件)
作用 :HPA/VPA 无法直接读 Prometheus,Adapter 把 Prometheus 指标转成 K8s 自定义指标 API(custom.metrics.k8s.io)供 HPA 查询。
helm show values prometheus-community/prometheus-adapter --version 5.1.0 > prometheus-adapter.yaml
修改:
prometheus:
url: http://kps-kube-prometheus-stack-prometheus.monitoring.svc.cluster.local.
port: 9090
rules:
default: true
custom:
- seriesQuery: 'nginx_http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name: {as: "nginx_http_requests"} # xxx_total → xxx
metricsQuery: 'sum(rate(nginx_http_requests_total{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
helm install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoring --version 5.1.0 -f ./prometheus-adapter.yaml
# 验证自定义指标 API
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/nginx_http_requests" | jq
2.7 三指标 HPA(CPU + 内存 + QPS)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: nginx-hpa, namespace: default}
spec:
minReplicas: 1
maxReplicas: 5
scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: nginx-with-exporter}
metrics:
- type: Resource
resource: {name: cpu, target: {type: Utilization, averageUtilization: 70}}
- type: Resource
resource: {name: memory, target: {type: AverageValue, averageValue: 150Mi}}
- type: Pods
pods:
metric: {name: nginx_http_requests}
target: {type: AverageValue, averageValue: 50}
注意类型差异:CPU 用
Utilization(占 requests 百分比);内存 / QPS 用AverageValue(绝对值);QPS 指标 type 是Pods。
kubectl apply -f hpa.yaml
kubectl describe hpa nginx-hpa # 看三指标 current/target 与 Conditions
ab -c 1000 -n 1000000 http://10.96.221.125/
扩容事件(SuccessfulRescale):
New size: 2; reason: cpu resource utilization above target
New size: 4; reason: pods metric nginx_http_requests above target
New size: 5; reason: pods metric nginx_http_requests above target
压测停止约 12 分钟后自动缩回 1(All metrics below target)。
三、VPA 垂直 Pod 自动伸缩(Vertical Pod Autoscaler)
3.1 概念与优缺点
VPA 根据资源使用率自动调整单个 Pod 的 CPU / 内存 requests (纵向加资源,而非加 Pod 数)。适合无法靠加副本扩容的场景(如数据库)。
- 优点:按需分配资源、节点利用率高;自动安排到合适节点;免去压测定 requests;减少人工维护。
- 缺点 :不能与 HPA 同时使用。
3.2 部署前置(所有节点)
# ① 升级 openssl11 并软链
yum install -y openssl-devel openssl11 openssl11-devel
rm -rf $(which openssl) && ln -s /usr/bin/openssl11 /usr/bin/openssl
openssl version # OpenSSL 1.1.1k
# ② git 低于 2.23 需源码升级到 2.28.0(vpa-up.sh 用了 git switch)
yum remove git -y
yum groupinstall "Development Tools" -y
yum install gettext-devel perl-CPAN perl-devel zlib-devel curl-devel expat-devel -y
tar zxvf git-2.28.0.tar.gz && cd git-2.28.0
make prefix=/usr/local all && make prefix=/usr/local install
rm -rf /usr/bin/git && ln -s /usr/local/bin/git /usr/bin/git
3.3 安装 VPA
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler/hack
bash vpa-up.sh
自动部署三组件:vpa-recommender(算推荐值)、vpa-updater(驱逐 / 更新)、vpa-admission-controller(准入注入),并生成 TLS 证书。
3.4 updateMode 四种模式(重点对比)
表格
| 模式 | 行为 |
|---|---|
| Off | 只收集数据、显示推荐值,不修改 Pod(只观察) |
| Initial | 仅 Pod 创建时应用一次推荐,启动后不再调整 |
| Recreate | 生成新推荐就终止旧 Pod、重建新 Pod(短暂中断) |
| Auto | 默认;尽量在线调整,无法在线调整则重建 Pod |
3.5 案例 A:updateMode: Off(只给推荐)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata: {name: nginx-vpa-off, namespace: default}
spec:
targetRef: {apiVersion: apps/v1, kind: Deployment, name: dep-nginx01}
updatePolicy: {updateMode: "Off"}
resourcePolicy:
containerPolicies:
- containerName: "nginx"
minAllowed: {cpu: "250m", memory: "100Mi"}
maxAllowed: {cpu: "2000m", memory: "2048Mi"}
kubectl get vpa # MODE=Off,CPU 250m MEM 131072k,PROVIDED=True
压测前后推荐值不变,Pod requests 仍是原来的 100m/250Mi------Off 只展示不生效。
3.6 案例 B:updateMode: Auto(驱逐重建应用推荐值)
spec:
targetRef: {kind: Deployment, name: dep-nginx02}
updatePolicy: {updateMode: "Auto"}
resourcePolicy: {containerPolicies: [{containerName: nginx,
minAllowed: {cpu: "250m", memory: "100Mi"},
maxAllowed: {cpu: "2000m", memory: "2048Mi"}}]}
kubectl apply -f nginx-vpa-auto.yaml
# Pod 被重建(新 Pod 名出现),describe 看到 requests 变为推荐值
# cpu: 250m, memory: 131072k ← 自动垂直扩容
kubectl describe pod <新pod>
四、HPA vs VPA 对比表
表格
| 对比项 | HPA 水平伸缩 | VPA 垂直伸缩 |
|---|---|---|
| 伸缩方向 | 增加 / 减少 Pod 数量 | 调整单 Pod 的 CPU / 内存 requests |
| 适用 | 无状态 Web、服务(加副本分担流量) | 数据库等不便加副本的应用 |
| 指标来源 | CPU / 内存(metrics-server)或自定义(Prometheus Adapter) | 历史资源使用率推荐 |
| 典型触发 | CPU>50%、QPS>50 | 实际使用率偏离推荐 |
| 能否同用 | 可与 VPA 冲突,不能同用 | 不能与 HPA 同用 |
| 生效方式 | 改 Deployment 副本数 | Off 观察 / Auto 重建 / Recreate 重建 / Initial 仅创建时 |
五、全链路组件关系
metrics-server ──→ Resource 指标(CPU/内存) ──→ HPA
nginx exporter ──→ Prometheus 抓取 ──→ prometheus-adapter ──→ custom.metrics API ──→ HPA(QPS)
VPA recommender ──→ updater/admission ──→ Pod requests
思维导图
