🎓 Kubernetes HPA 实战教学:从安装到自动扩容

📋 前置检查

确保你有一个可用的 Kubernetes 集群(如 Minikube, Kind, K3s, 或云厂商集群),并且 kubectl 已配置好。

kubectl cluster-info

第一步:安装 Metrics Server (HPA 的心脏)

HPA 依赖 Metrics Server 来获取 CPU/内存数据。如果没有它,HPA 的目标值会显示 <unknown>

1. 直接安装 (推荐大多数环境)

运行以下命令直接部署官方最新版:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

2. 【重要】处理本地集群兼容性问题

如果你使用的是 Minikube, Kind, K3s自建的裸金属集群,默认的 Metrics Server 可能会因为证书问题无法工作。

请执行以下补丁命令,添加 --kubelet-insecure-tls 参数以跳过证书验证:

kubectl patch deployment metrics-server -n kube-system --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'

3. 验证安装

等待约 30-60 秒,然后检查状态:

kubectl get pods -n kube-system -l k8s-app=metrics-server
# 应显示 STATUS 为 Running

# 尝试获取节点指标(如果有数据输出,说明成功)
kubectl top nodes

如果 kubectl top nodes 显示数据,恭喜!核心组件已就绪。


第二步:部署你的应用 (Deployment + Service + HPA)

将你提供的 YAML 保存为 demo-hpa.yaml。我已经修复了其中的缩进格式问题,确保可以直接运行。

1. 创建文件

# ==============================================================================
# 资源 1: Deployment (应用部署)
# 定义运行什么容器、副本数量以及资源限制
# ==============================================================================
apiVersion: apps/v1           # API 版本:apps/v1 是 Deployment 的标准版本
kind: Deployment              # 资源类型:部署控制器
metadata:
  name: demo                  # 名称:后续 HPA 和 Service 将通过此名称引用它
  labels:
    app: demo                 # 标签:用于关联 Service 和 Pod 选择器
spec:
  replicas: 1                 # 【初始副本数】:启动时先运行 1 个 Pod
                              # HPA 会根据负载在这个数字和 maxReplicas 之间调整
  
  selector:                   # 【选择器】:告诉 Deployment 管理哪些 Pod
    matchLabels:
      app: demo               # 必须与下方 template.metadata.labels 一致
  
  strategy: {}                # 更新策略:默认为 RollingUpdate (滚动更新),生产环境建议显式配置
  
  template:                   # 【Pod 模板】:定义实际运行的 Pod 长什么样
    metadata:
      labels:
        app: demo             # 【关键】:Pod 的标签,必须匹配上面的 selector
    spec:
      containers:
      - name: nginx           # 容器名称
        image: nginx          # 镜像:使用官方 Nginx 镜像
        # ----------------------------------------------------------------------
        # 【核心配置】资源请求与限制 (Resources)
        # HPA 计算 CPU 使用率的公式:(当前使用量 / requests.cpu) * 100%
        # 如果这里不写 requests,HPA 将无法工作 (显示 <unknown>)
        # ----------------------------------------------------------------------
        resources:
          requests:
            cpu: "100m"       # 【基准值】:承诺给容器 100 毫核 (0.1 核)
                              # HPA 目标设为 1%,即当 CPU 使用 > 1m (100m * 1%) 时触发扩容
            memory: "128Mi"   # 内存请求:128 MiB
          limits:
            cpu: "500m"       # 【上限值】:容器最多能用 500 毫核,超过会被节流 (Throttling)
            memory: "256Mi"   # 内存上限:256 MiB,超过会被 OOM Kill
---
# ==============================================================================
# 资源 2: Service (服务暴露)
# 定义如何访问这些 Pod (负载均衡)
# ==============================================================================
apiVersion: v1                # API 版本:v1 是 Service 的标准版本
kind: Service                 # 资源类型:服务
metadata:
  name: demo                  # 名称:DNS 名称将是 demo.<namespace>.svc.cluster.local
  labels:
    app: demo                 # 标签:方便通过 label 筛选 Service
spec:
  type: LoadBalancer          # 【类型】:负载均衡器
                              # - 云环境:会创建一个公网 IP (ELB/SLB/NLB)
                              # - 本地环境 (Minikube/Kind):IP 会显示 <pending>,需用 nodePort 或 port-forward
  ports:
  - name: http                # 端口名称:可选,但推荐命名
    port: 80                  # 【服务端口】:ClusterIP 暴露的端口,也是 LB 监听的端口
    targetPort: 80            # 【目标端口】:流量转发到 Pod 内部容器的哪个端口 (Nginx 默认 80)
    protocol: TCP             # 协议:TCP
  selector:
    app: demo                 # 【关键】:将流量转发给带有 label "app=demo" 的 Pod
---
# ==============================================================================
# 资源 3: HorizontalPodAutoscaler (自动伸缩)
# 监控指标并自动调整 Deployment 的副本数
# ==============================================================================
apiVersion: autoscaling/v2    # API 版本:v2 支持更复杂的指标 (如自定义指标、多指标)
kind: HorizontalPodAutoscaler # 资源类型:水平 Pod 自动伸缩器
metadata:
  name: demo-hpa              # 名称:用于 kubectl get hpa demo-hpa
spec:
  # 1. 【绑定目标】:告诉 HPA 要控制哪个 Deployment
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo                # 必须与上方 Deployment 的 metadata.name 一致

  # 2. 【副本范围】:伸缩的边界
  minReplicas: 1              # 【最小副本】:即使没负载,也至少保留 1 个 Pod (保证高可用)
  maxReplicas: 10             # 【最大副本】:负载再高,最多只扩到 10 个 (防止资源耗尽/费用爆炸)

  # 3. 【触发指标】:什么情况下扩容?
  metrics:
  - type: Resource            # 指标类型:资源指标 (CPU/内存)
    resource:
      name: cpu               # 监控对象:CPU
      target:
        type: Utilization     # 目标类型:利用率 (百分比)
        averageUtilization: 1 # 【阈值】:平均 CPU 利用率超过 1% 时触发扩容
                              # 注意:因为 requests=100m,1% = 1m CPU。
                              # 这是一个非常敏感的测试值,真实生产环境通常设为 50%-80%。

2. 应用配置

kubectl apply -f demo-hpa.yaml

3. 观察初始状态

此时应该只有 1 个 Pod。

kubectl get pods -l app=demo
kubectl get hpa demo-hpa

注意:刚创建时,HPA 的 TARGETS 列可能显示 <unknown>,这是正常的,等待 1-2 分钟让 Metrics Server 采集数据。


第三步:获取访问地址

由于你配置了 LoadBalancer,我们需要找到访问 IP。

场景 A:云环境 (AWS, GCP, Azure)

EXTERNAL-IP 会显示一个公网 IP:

kubectl get svc demo
# 记下 EXTERNAL-IP 列的 IP 地址

场景 B:本地环境 (Minikube / Kind / 裸金属)

EXTERNAL-IP 通常会一直显示 <pending>。请使用以下方法获取访问地址:

方法 1:使用 NodePort (推荐)
修改 Service 为 NodePort 并获取端口:

kubectl patch svc demo -p '{"spec":{"type":"NodePort"}}'
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
NODE_PORT=$(kubectl get svc demo -o jsonpath='{.spec.ports[0].nodePort}')
echo "访问地址: http://$NODE_IP:$NODE_PORT"
TARGET_URL="http://$NODE_IP:$NODE_PORT"

方法 2:端口转发 (仅限本机测试)

kubectl port-forward svc/demo 8080:80 &
TARGET_URL="http://127.0.0.1:8080"

(假设我们后续使用 $TARGET_URL 变量代表你的访问地址)


第四步:见证奇迹 (压力测试与自动扩容)

现在我们要制造一点点 CPU 负载。由于你的阈值设为 1%,非常轻微的负载就会触发扩容。

1. 开启两个终端窗口

终端 A (监控窗口):
实时观察 Pod 数量和 HPA 状态。

# watch 命令每秒刷新一次
watch -n 1 "kubectl get pods -l app=demo && echo '---' && kubectl get hpa demo-hpa"

终端 B (攻击窗口):
使用 ab 发起压力测试。
(如果没装 ab,参考上一条回复进行安装,或使用 docker 运行)

# 替换 <YOUR_URL> 为你在第三步获取的地址,例如 http://192.168.1.5:30000
# 发送 2000 个请求,并发 50
ab -n 2000 -c 50 <YOUR_URL>/

2. 观察现象

终端 A 中,你应该会看到以下过程:

  1. 初始状态: REPLICAS1TARGETS 显示类似 0%/1%
  2. 压力开始: 当 ab 开始运行,CPU 使用率瞬间上升。
  3. 触发扩容:
    • TARGETS 变为 xx%/1% (红色或高数值)。
    • 几秒到几十秒后,REPLICAS1 变成 2, 3, 4... 直到 CPU 平均值降回 1% 以下或达到最大限制。
  1. 压力结束: ab 运行完毕后,CPU 下降。
  2. 自动缩容: 等待约 3-5 分钟(默认冷却时间),REPLICAS 会逐渐变回 1

预期输出示例

NAME                    READY   STATUS    RESTARTS   AGE
demo-xxxxxxxx-abcde     1/1     Running   0          2m
demo-xxxxxxxx-fghij     1/1     Running   0          10s   <-- 新 Pod 出现了!
demo-xxxxxxxx-klmno     1/1     Running   0          5s    <-- 又出现了!

NAME       REFERENCE          TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
demo-hpa   Deployment/demo    150%/1%    1         10        3          5m

(注意:TARGETS 显示 150% 是因为负载很高,而阈值仅为 1%)


第五步:清理环境

实验结束后,删除所有资源:

kubectl delete -f demo-hpa.yaml

💡 常见问题排查 (Troubleshooting)

问题现象

可能原因

解决方案

HPA TARGETS 显示 <unknown>

Metrics Server 未工作或证书报错

1. 检查 kubectl top nodes

是否有数据
2. 执行第一步中的 patch

命令添加 --kubelet-insecure-tls

Pod 不扩容

负载不够大 或 阈值太高

你的阈值是 1%,应该很容易触发。检查 kubectl top pods

看 CPU 是否真的在增加。

Service 无法访问

LoadBalancer 未分配 IP

本地环境请使用 NodePort

port-forward

(见第三步)。

扩容太慢

默认冷却时间

HPA 默认有 300 秒的缩容冷却期,扩容通常较快(取决于调度速度)。

现在,你已经成功搭建了一套具备自动弹性伸缩能力的 Kubernetes 应用架构!🎉

更多推荐