Kubernetes 弹性伸缩完全指南:HPA 与 VPA 实战教程

前言

在 Kubernetes 集群中,弹性伸缩是应对业务流量波动的核心能力,能实现资源利用率与服务可用性的动态平衡。本文基于原 PDF 的逻辑结构,详细拆解水平自动伸缩(HPA)和垂直自动伸缩(VPA)的原理、部署流程及实战案例,所有操作均提供完整命令和配置示例,便于直接落地实践。

一、水平自动伸缩(HPA)

1.1 HPA 介绍

HPA(Horizontal Pod Autoscaler)通过监控指标自动调整 Deployment、ReplicaSet 等资源的 Pod 副本数,适配业务负载变化。

  • 适用场景:无状态服务的动态扩缩容,支持多类指标驱动
  • 不适用对象:DaemonSet 等无法扩缩的资源
  • 核心指标类型:
    • Resource metrics:CPU、内存利用率(基础指标)
    • Pod metrics:网络利用率、流量等 Pod 级指标
    • Object metrics:Ingress 每秒请求数等对象指标
    • Custom metrics:服务响应时间等自定义业务指标

1.2 依赖组件:metrics-server 部署

metrics-server 是 K8s 集群资源监控的核心组件,为 HPA 提供 CPU、内存等指标数据,部署步骤如下:

1.2.1 下载并修改配置文件
# 下载组件配置文件
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

# 编辑配置文件,添加 insecure-tls 参数(v0.8.0+版本必需)
vim metrics-server-components.yaml
1.2.2 关键配置修改

containers.args中添加--kubelet-insecure-tls,完整配置片段如下:

containers:
- args:
  - --cert-dir=/tmp
  - --secure-port=10250
  - --kubelet-insecure-tls  # 新增:跳过kubelet证书验证
  - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  - --kubelet-use-node-status-port
  - --metric-resolution=15s
  image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.8.0
1.2.3 部署并验证
# 应用配置
kubectl apply -f metrics-server-components.yaml

# 查看部署状态(需等待Pod就绪)
kubectl get pods -n kube-system | grep metrics-server

# 强制删除异常Pod(如有Terminating状态)
kubectl delete pod <异常Pod名称> -n kube-system --grace-period=0 --force

# 验证指标采集功能
kubectl top node  # 查看节点资源使用
kubectl top pod -n kube-system  # 查看Pod资源使用

1.3 HPA 基础案例实战

以 Nginx 服务为例,实现基于 CPU 利用率的自动扩缩容。

1.3.1 部署 Nginx 应用及服务

创建nginx01-svc.yaml配置文件:

# Deployment配置
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  # CPU请求值(HPA计算利用率的基准)
            memory: 100Mi
---
# Service配置(NodePort类型)
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx

应用配置并验证:

# 部署应用
kubectl apply -f nginx01-svc.yaml

# 查看Pod和Service状态
kubectl get pods,svc
# 预期输出:2个Nginx Pod运行,Service暴露NodePort(如80:30699/TCP)

# 访问验证(替换为实际NodeIP和NodePort)
curl http://192.168.18.129:30699
1.3.2 创建 HPA 规则

创建nginx-hpa.yaml配置文件:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
  namespace: default
spec:
  scaleTargetRef:  # 关联目标Deployment
    apiVersion: apps/v1
    kind: Deployment
    name: nginx  # 必须与Deployment名称一致
  minReplicas: 1  # 最小副本数
  maxReplicas: 10  # 最大副本数
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50  # CPU利用率阈值:超过50%扩容

应用 HPA 配置并验证:

# 部署HPA
kubectl apply -f nginx-hpa.yaml

# 查看HPA状态
kubectl get hpa
# 预期输出:TARGETS显示当前CPU利用率/目标值(如0%/50%)
1.3.3 压测验证自动扩容
# 安装压测工具
yum install -y httpd-tools

# 执行压测(并发10000,总请求100亿,替换为实际Service地址)
ab -c 10000 -n 10000000000 http://192.168.18.129:30699/

# 实时观测Pod数量变化
watch kubectl get pods
# 预期结果:Pod数量从2个扩容至9个左右
1.3.4 验证自动缩容

停止压测后,等待 5 分钟左右,HPA 会自动缩减副本数至最小阈值:

# 查看最终Pod状态
kubectl get pods
# 预期结果:Pod数量恢复至1个

1.4 进阶案例:基于 Prometheus 的自定义指标 HPA

通过 Prometheus 采集 Nginx 请求指标,实现基于 QPS 的自动扩缩容。

1.4.1 环境准备:部署 metallb(负载均衡)
# 部署metallb
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml

# 创建IP地址池配置(ippool.yaml)
cat > ippool.yaml << EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.18.240-192.168.18.250
EOF

# 创建二层通告配置(l2.yaml)
cat > l2.yaml << EOF
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
EOF

# 应用配置
kubectl apply -f ippool.yaml -f l2.yaml

# 验证metallb状态
kubectl get pods -n metallb-system
1.4.2 部署 Ingress Nginx
# 下载部署文件(v1.13.2版本)
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.2/deploy/static/provider/cloud/deploy.yaml

# 编辑配置,修改externalTrafficPolicy为Cluster
vim deploy.yaml
# 找到347行左右,将Local改为Cluster:
# externalTrafficPolicy: Cluster

# 应用配置
kubectl apply -f deploy.yaml

# 验证部署(需等待5分钟左右)
kubectl get pods,svc -n ingress-nginx
# 预期输出:Ingress Controller Pod运行,Service分配IP(如192.168.18.240)
1.4.3 部署 Prometheus(Helm 方式)
1.4.3.1 安装 Helm
# 下载Helm二进制包(v3.19.0)
wget https://get.helm.sh/helm-v3.19.0-linux-amd64.tar.gz

# 解压并安装
tar zxvf helm-v3.19.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/

# 验证Helm版本
helm version
1.4.3.2 部署 Prometheus Stack
# 添加Prometheus仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 创建配置文件目录
mkdir promedir && cd promedir

# 导出默认配置并修改
helm show values prometheus-community/kube-prometheus-stack --version 77.6.2 > kube-prometheus-stack.yaml

# 启用全量服务发现(4138行左右)
vim kube-prometheus-stack.yaml
# 修改:serviceMonitorSelectorNilUsesHelmValues: false

# 部署Prometheus(命名为kps,创建monitoring命名空间)
helm install kps prometheus-community/kube-prometheus-stack --version 77.6.2 -f ./kube-prometheus-stack.yaml -n monitoring --create-namespace --debug

# 验证部署
kubectl get pods -n monitoring -l "release=kps"
1.4.4 配置 Prometheus 访问(Ingress)
# 创建Prometheus Ingress配置
cat > prometheus-ingress.yaml << EOF
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
EOF

# 创建Grafana Ingress配置
cat > grafana-ingress.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-grafana
  namespace: monitoring
spec:
  ingressClassName: nginx
  rules:
  - host: grafana.abner.com
    http:
      paths:
      - path: "/"
        pathType: Prefix
        backend:
          service:
            name: kps-grafana
            port:
              number: 80
EOF

# 应用配置
kubectl apply -f prometheus-ingress.yaml -f grafana-ingress.yaml

# 配置本地DNS(Windows修改hosts文件)
# 编辑 C:\Windows\System32\drivers\etc\hosts,添加:
# 192.168.18.240 prometheus.abner.com
# 192.168.18.240 grafana.abner.com
1.4.5 部署带监控的 Nginx 应用
1.4.5.1 配置 Nginx 监控
# 创建Nginx配置文件(启用status模块)
mkdir nginxdir && cd nginxdir
cat > nginx.conf << EOF
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;
    }
  }
}
EOF

# 创建ConfigMap
kubectl create configmap nginx-config --from-file=nginx.conf
1.4.5.2 部署 Nginx 及 Exporter

创建nginx.yaml配置文件:

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
        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
        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

应用配置并验证:

kubectl apply -f nginx.yaml
kubectl get pods  # 预期:2个双容器Pod运行(nginx + exporter)
1.4.6 配置 Prometheus 采集 Nginx 指标
# 编辑Prometheus配置文件
vim 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
  - source_labels: [__meta_kubernetes_namespace]
    target_label: namespace
  - source_labels: [__meta_kubernetes_pod_name]
    target_label: pod

# 更新Prometheus配置
helm upgrade kps prometheus-community/kube-prometheus-stack --version 77.6.2 -f ./kube-prometheus-stack.yaml -n monitoring

# 验证采集状态(浏览器访问http://prometheus.abner.com,查看Status->Targets)
1.4.7 部署 Prometheus-adapter(自定义指标桥接)
# 下载适配器配置
helm show values prometheus-community/prometheus-adapter --version 5.1.0 > prometheus-adapter.yaml

# 编辑配置文件
vim prometheus-adapter.yaml

# 修改Prometheus地址(37行左右)
prometheus:
  url: http://kps-kube-prometheus-stack-prometheus.monitoring.svc.cluster.local
  port: 9090

# 添加自定义指标规则(129行左右)
rules:
  default: true
  custom:
  - seriesQuery: 'nginx_http_requests_total{namespace!="",pod!=""}'
    resources:
      overrides:
        namespace: {resource: "namespace"}
        pod: {resource: "pod"}
    name:
      as: "nginx_http_requests"
    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

# 验证自定义指标
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/nginx_http_requests" | jq .
1.4.8 创建基于多指标的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
  namespace: default
spec:
  maxReplicas: 5
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-with-exporter
  metrics:
  # CPU利用率指标
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  # 内存使用指标
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 150Mi
  # 自定义QPS指标
  - type: Pods
    pods:
      metric:
        name: nginx_http_requests
      target:
        type: AverageValue
        averageValue: 50

应用并验证:

# 部署HPA
kubectl apply -f hpa.yaml

# 查看HPA状态
kubectl get hpa

# 压测验证(并发1000,总请求100万)
ab -c 1000 -n 1000000 http://10.96.221.125/  # 替换为实际Service ClusterIP

# 实时观测Pod扩容
watch kubectl get pods
# 预期结果:Pod从2个扩容至5个,12分钟后自动缩容至1个

二、垂直自动伸缩(VPA)

2.1 VPA 介绍

VPA(Vertical Pod Autoscaler)通过调整 Pod 的 CPU、内存请求值实现垂直扩缩容,适用于数据库等无法水平扩容的有状态服务。

  • 核心特性:自动优化资源配置,避免资源浪费或不足

  • 优缺点

    • 优点:提高节点资源利用率,无需手动设置资源阈值
    • 缺点:不支持与 HPA 同时使用,扩缩容可能重启 Pod(服务短暂中断)
  • 更新模式(updateMode)

    • Off:仅显示资源推荐,不执行更新
    • Initial:仅在 Pod 创建时应用推荐
    • Recreate:删除旧 Pod 并创建新 Pod 应用推荐
    • Auto:优先在线更新,失败则重建 Pod(默认模式)

2.2 VPA 部署

2.2.1 环境准备:升级依赖组件
# 升级openssl(所有节点执行)
wget -O /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo
yum install -y openssl-devel openssl11 openssl11-devel
rm -rf `which openssl`
ln -s /usr/bin/openssl11 /usr/bin/openssl
openssl version  # 验证版本(需为1.1.1+)

# 升级git(需2.23+版本)
yum remove git -y
yum groupinstall "Development Tools" -y
yum install gettext-devel perl-CPAN perl-devel zlib-devel curl-devel expat-devel -y
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.28.0.tar.gz
tar zxvf git-2.28.0.tar.gz
cd git-2.28.0/
make prefix=/usr/local all
make prefix=/usr/local install
ln -s /usr/local/bin/git /usr/bin/git
git --version  # 验证版本
2.2.2 部署 VPA 组件
# 克隆VPA源码
mkdir vpa && cd vpa
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler/

# 部署VPA
bash ./hack/vpa-up.sh

# 验证部署状态
kubectl get pods -n kube-system | grep vpa
# 预期输出:vpa-admission-controller、vpa-recommender、vpa-updater三个Pod运行

2.3 VPA 实战案例

2.3.1 案例 1:updateMode=“Off”(仅查看推荐)
# 创建Nginx应用(nginx01.yaml)
cat > nginx01.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-nginx01
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.26-alpine
        resources:
          requests:
            cpu: 100m
            memory: 250Mi
---
apiVersion: v1
kind: Service
metadata:
  name: nginx01-svc
  namespace: default
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
EOF

# 部署应用
kubectl apply -f nginx01.yaml

# 创建VPA配置(nginx-vpa-off.yaml)
cat > nginx-vpa-off.yaml << EOF
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"
EOF

# 应用VPA配置
kubectl apply -f nginx-vpa-off.yaml

# 查看资源推荐
kubectl get vpa
# 预期输出:CPU推荐250m,内存推荐131072k

# 压测后验证推荐值(无变化)
ab -c 1000 -n 1000000 http://10.106.168.28/  # 替换为Service ClusterIP
kubectl get vpa
2.3.2 案例 2:updateMode=“Auto”(自动更新资源)
# 创建Nginx应用(nginx02.yaml)
cat > nginx02.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-nginx02
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.26-alpine
        resources:
          requests:
            cpu: 100m
            memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
  name: nginx02-svc
  namespace: default
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
EOF

# 部署应用
kubectl apply -f nginx02.yaml

# 创建VPA配置(nginx-vpa-auto.yaml)
cat > nginx-vpa-auto.yaml << EOF
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: nginx-vpa-auto
  namespace: default
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: dep-nginx02
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: "nginx"
      minAllowed:
        cpu: "250m"
        memory: "100Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "2048Mi"
EOF

# 应用VPA配置
kubectl apply -f nginx-vpa-auto.yaml

# 查看VPA状态
kubectl get vpa

# 验证Pod重建及资源更新
watch kubectl get pods  # 观察Pod重建过程
kubectl describe pod <新Pod名称> | grep Requests  # 验证CPU/内存已更新为推荐值

三、总结

  • HPA:适合无状态服务,通过增加 Pod 副本数应对流量波动,支持多指标组合,无服务中断风险。
  • VPA:适合有状态服务,通过优化资源配置提高利用率,需注意更新模式对服务的影响。
  • 生产建议:根据服务类型选择伸缩方案,优先使用 HPA;关键服务需提前测试 VPA 的资源推荐合理性,避免扩缩容异常。

更多推荐