1. 安装 KEDA

使用 Helm 安装 KEDA(推荐指定版本以确保兼容性):

helm upgrade --install keda kedacore/keda \
  --namespace keda --create-namespace \
  --version 2.9.0

✅ 此命令会部署 KEDA Operator、Metrics Server 和相关 CRDs。


2. 部署 podinfo 应用(带 metrics 暴露)

podinfo 是一个轻量级示例应用,自带 /metrics 接口(暴露 http_requests_total 等指标)。

# podinfo-dep.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: podinfo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: podinfo
  template:
    metadata:
      labels:
        app: podinfo
      annotations:
        prometheus.io/scrape: "true"   # 原生 Prometheus 采集注解(非必需,若用 ServiceMonitor 可省略)
    spec:
      containers:
        - name: podinfod
          image: stefanprodan/podinfo:0.0.1
          imagePullPolicy: Always
          command:
            - ./podinfo
            - -port=9898
            - -logtostderr=true
            - -v=2
          ports:
            - containerPort: 9898
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /readyz
              port: 9898
            initialDelaySeconds: 1
            periodSeconds: 2
          livenessProbe:
            httpGet:
              path: /healthz
              port: 9898
            initialDelaySeconds: 1
            periodSeconds: 3
          resources:
            requests:
              memory: "32Mi"
              cpu: "1m"
            limits:
              memory: "256Mi"
              cpu: "100m"
          volumeMounts:
            - name: metadata
              mountPath: /etc/podinfod/metadata
              readOnly: true
      volumes:
        - name: metadata
          downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels
              - path: "annotations"
                fieldRef:
                  fieldPath: metadata.annotations

部署:

kubectl apply -f podinfo-dep.yaml

3. 配置 Prometheus 采集指标(使用 ServiceMonitor

⚠️ 仅适用于 已部署 Prometheus Operator(如 KubeSphere、kube-prometheus-stack)的环境。

3.1 创建 Service(必须命名端口)

# svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: podinfo
  labels:
    app: podinfo
spec:
  type: NodePort
  selector:
    app: podinfo
  ports:
    - name: metrics           # ← 关键:必须命名
      port: 9898
      targetPort: 9898
      nodePort: 31198         # 可选,用于外部访问
      protocol: TCP

3.2 创建 ServiceMonitor

# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: podinfo-servicemonitor
  namespace: default
  labels:
    release: prometheus          # 必须匹配 Prometheus 实例的 serviceMonitorSelector
spec:
  selector:
    matchLabels:
      app: podinfo              # 匹配 Service 的 labels
  namespaceSelector:
    matchNames:
      - default                 # 允许监控 default 命名空间
  endpoints:
    - port: metrics            # 必须与 Service 的 port.name 一致
      path: /metrics
      interval: 30s
      scheme: http

应用:

kubectl apply -f svc.yaml
kubectl apply -f servicemonitor.yaml

4. 创建 KEDA ScaledObject(基于 Prometheus QPS)

# scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: podinfo-prometheus-qps
  namespace: default
spec:
  scaleTargetRef:
    name: podinfo               # 要扩缩的 Deployment 名称
  pollingInterval: 15           # 每 15 秒查询一次 Prometheus
  cooldownPeriod: 30            # 缩容冷却时间(秒)
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus-k8s.kubesphere-monitoring-system.svc:9090
        metricName: http_requests_qps
        query: |
          scalar(sum(rate(http_requests_total{namespace="default", job="podinfo"}[2m])))
        threshold: "2"          # QPS > 2 时扩容

💡 提示:

  • serverAddress 需替换为你集群中 Prometheus 的 内部 Service 地址
  • job="podinfo" 需在 Prometheus UI 中确认实际标签

部署并验证:

kubectl apply -f scaledobject.yaml
kubectl get hpa  # 应看到 keda-hpa-podinfo-prometheus-qps

5. 压测与观察扩缩容行为

5.1 安装压测工具 hey

从 Go 1.17 起,go get 不再用于安装可执行程序。使用:

go install github.com/rakyll/hey@latest

确保 $GOPATH/binPATH 中:

export PATH="$PATH:$(go env GOPATH)/bin"

5.2 发起压测

hey -n 10000 -q 10 -c 5 http://<K8S_PUBLIC_IP>:31198/

5.3 观察扩容

kubectl get pod -w -l app=podinfo

应看到副本数自动增加。

5.4 观察缩容

停止压测后:

kubectl get pod -w -l app=podinfo
kubectl describe hpa keda-hpa-podinfo-prometheus-qps

6. 缩容延迟解释

即使指标已低于阈值,缩容仍可能延迟,原因如下:

cooldownPeriod: 30

  • 作用:每次缩容后,30 秒内禁止再次缩容(KEDA 层面防抖)。
  • 注意:不影响首次缩容触发时间。

✅ HPA downscale-stabilization(默认 5 分钟)

  • 来源kube-controller-manager 的参数 --horizontal-pod-autoscaler-downscale-stabilization=5m
  • 作用:HPA 会保留过去 5 分钟内建议的最大副本数,并以此作为当前目标,防止因瞬时流量下降而过早缩容。
  • 这是缩容延迟约 5 分钟的主要原因
  • 普通用户无法修改,需集群管理员调整。

📌 结论:在生产环境中,即使 cooldownPeriod=30缩容生效通常仍需等待约 5 分钟,这是 Kubernetes 的防抖设计,不是配置错误


7. 附:关键验证命令

# 查看资源状态
kubectl get deploy,svc,pod,hpa,scaledobject -l app=podinfo

# 查看 ScaledObject 详情
kubectl get scaledobject podinfo-prometheus-qps -o yaml

# 查看 HPA 事件(含缩容原因)
kubectl describe hpa keda-hpa-podinfo-prometheus-qps

# 查看 KEDA Operator 日志
kubectl logs -n keda -l app=keda-operator

# 验证 Prometheus 指标(集群内)
kubectl run -it --rm debug --image=busybox --restart=Never -- sh
# 在容器内执行:
wget -qO- "http://prometheus-k8s.kubesphere-monitoring-system.svc:9090/api/v1/query?query=sum(rate(http_requests_total{namespace=\"default\",job=\"podinfo\"}[2m]))"