实战 Runbook:给应用加 HPA 自动扩缩 + Ingress 域名 + Hubble 流量观测
07 把 MySQL+Go+Vue 应用通过 CI/CD 发布上线了。这篇在它之上加三样生产常用能力,都在真机跑通:
- Hubble:看服务之间到底在怎么通信(frontend→backend→mysql)。
- HPA:CPU 一高自动扩 Pod,降下来自动缩。
- Ingress:用域名访问,不再记 NodePort 端口号。
HPA 和 Ingress 都写进部署 repo 走 GitOps(ArgoCD 自动同步),和 07 一脉相承。
1. Hubble:看 demo 的服务间流量
Cilium 装的时候已经开了 Hubble(hubble.enabled/relay/ui),这里只是用起来。
装 hubble CLI(在 cp-1)+ 暴露 UI:
HUBBLE_VERSION=v1.16.5
curl -sL --fail --remote-name-all https://github.com/cilium/hubble/releases/download/${HUBBLE_VERSION}/hubble-linux-amd64.tar.gz
tar xzf hubble-linux-amd64.tar.gz && mv hubble /usr/local/bin/
# Hubble UI 暴露成 NodePort(浏览器看拓扑图)
kubectl patch svc hubble-ui -n kube-system \
-p '{"spec":{"type":"NodePort","ports":[{"port":80,"targetPort":8081,"nodePort":31235}]}}'
观测 demo 命名空间的流量(先打点流量,再 observe):
for i in $(seq 1 8); do curl -s -o /dev/null http://<节点>:31000/api/items; done
cilium hubble port-forward & # 后台转发到 hubble-relay:4245
hubble observe --namespace demo --last 25 -o compact
真实输出(能看到服务间真实调用链):
... 10.244.0.98:51926 (remote-node) <> demo/frontend-…:80 (ID:6156) to-overlay FORWARDED (TCP SYN)
... demo/frontend-…:35112 (ID:6156) -> demo/backend-…:8080 (ID:1505) to-endpoint FORWARDED (TCP ACK,PSH)
... demo/frontend-…:35112 (ID:6156) <- demo/backend-…:8080 (ID:1505) to-overlay FORWARDED (TCP ACK,PSH)
怎么读:
demo/frontend-… (ID:6156) -> demo/backend-… (ID:1505):前端 nginx 把/api反代给后端,这条就是它。ID是 Cilium 给每个 workload 的身份号,策略就是按身份匹配的。FORWARDED:放行(没有 NetworkPolicy 拦)。如果有策略拦截会是DROPPED,hubble observe --verdict DROPPED专看被拒的。to-overlay/to-endpoint:包是出本节点走 VXLAN,还是已到目标 endpoint。
看不到
backend -> mysql:3306?正常。后端用的是长连接池,建连一次后复用,不会每个 HTTP 请求都新建一条 mysql 连接——这本身就是 Hubble 帮你确认的事实。重启 backend 时才会看到新建的 mysql 连接。
UI:http://<节点>:31235,能看到 namespace 内的服务拓扑和实时流。
2. HPA:CPU 自动扩缩
2.1 先装 metrics-server(HPA 的前提)
HPA 按 CPU/内存扩缩,数据来自 metrics.k8s.io,由 metrics-server 提供。本集群没有,先装:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.2/components.yaml
# kubeadm 的 kubelet 用自签 serving 证书,metrics-server 默认会校验失败 → 加这个 flag
kubectl patch deploy metrics-server -n kube-system --type=json \
-p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
kubectl rollout status deploy/metrics-server -n kube-system
kubectl top nodes # 能出数 = metrics API 通了
真实输出:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-cp-1 555m 6% 3687Mi 47%
k8s-w-1 2698m 33% 3524Mi 44%
...
⚠️ 不加
--kubelet-insecure-tls,metrics-server 日志会刷x509: certificate signed by unknown authority,kubectl top一直报error: Metrics API not available。kubeadm 集群几乎都要加(除非开了 kubelet serving 证书的 CSR 签发)。
2.2 给 backend 加 HPA(GitOps)
写进部署 repo demo-deploy/manifests/05-backend-hpa.yaml:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: backend, namespace: demo }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: backend }
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 50 }
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # 演示用,立即扩;生产可留默认缓冲
policies: [{ type: Pods, value: 2, periodSeconds: 15 }]
🔑 HPA + GitOps 的冲突坑:backend Deployment 原来写了
replicas: 2。如果保留,ArgoCD 的selfHeal会不停把副本数改回 2,和 HPA 抢控制权。必须从 Deployment 的 git manifest 里删掉replicas字段,让 HPA 独占副本数(ArgoCD 对"manifest 里没写的字段"不做 diff,于是不再打架)。
# demo-deploy 里删 replicas 行 + 加 HPA,提交推送
sed -i '/^ replicas: 2$/d' manifests/03-backend.yaml
git add -A && git commit -m "feat: backend HPA + 去掉固定 replicas" && git push
ArgoCD 默认每 3 分钟才轮询一次 Git。想立刻生效,强制硬刷新:
kubectl annotate app demo -n argocd argocd.argoproj.io/refresh=hard --overwrite
2.3 压测看扩容
# 起一个并发压测 pod,狂打 backend
kubectl run load -n demo --image=busybox:1.36 --restart=Never -- /bin/sh -c \
'for i in $(seq 1 20); do (while true; do wget -q -O- http://backend:8080/api/items >/dev/null 2>&1; done) & done; sleep 600'
watch kubectl get hpa,deploy backend -n demo
真实扩容过程:
压测前: cpu 2%/50% replicas=2 ← 空闲,停在 min
[15s] replicas→6 (期望) ready=2 ← CPU 飙升,HPA 决定扩到 max
[45s] deploy replicas=4 ready=2 ← 新 Pod 在起
[60s] deploy replicas=6 ready=4
[75s] deploy replicas=6 ready=6 ← 扩满 6 个
停掉压测(kubectl delete pod load -n demo)后,约 5 分钟(缩容默认稳定窗)自动缩回 min=2。
backend 的 CPU request 是
50m,目标 50% 即25m。阈值低、压测一上来就打满 max=6,演示效果明显。生产按真实容量设 request 和阈值。
3. Ingress:用域名访问
NodePort 要记端口号、不好记也不专业。Ingress 让你用域名+路径路由到后端 Service。
3.1 装 ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.kind=DaemonSet \
--set controller.service.type=NodePort \
--set controller.service.nodePorts.http=32080 \
--set controller.service.nodePorts.https=32443 \
--set 'controller.tolerations[0].operator=Exists'
⚠️ 为什么不用 80 端口 / hostPort(本环境实测踩坑): 一开始想用
controller.hostPort.enabled=true让 ingress 直接监听节点 80,结果内网外网 80 都不通。原因有二:①节点hostname -I只有内网 IP,公网 IP 是云商 NAT;②云防火墙只转发 NodePort 段(30000-32767),80 没放行(实测公网:80 → 000、公网:31000 → 200)。 所以这种 IDC/云环境,ingress 也得走 NodePort(如32080)。生产环境前面会有 LB 把:80/:443映射到 ingress,那时才用标准端口。
3.2 加 Ingress 资源(GitOps)
写进 demo-deploy/manifests/06-ingress.yaml,用 nip.io 免配 DNS(*.<IP>.nip.io 自动解析到该 IP):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: { name: demo, namespace: demo }
spec:
ingressClassName: nginx
rules:
- host: demo.154.201.73.31.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: frontend, port: { number: 80 } }
git add -A && git commit -m "feat: 加 Ingress 域名" && git push
kubectl annotate app demo -n argocd argocd.argoproj.io/refresh=hard --overwrite # 立即同步
kubectl get ingress demo -n demo
3.3 验证域名访问
D=http://demo.154.201.73.31.nip.io:32080
curl -s $D/ | grep -o '<title>[^<]*</title>' # <title>Demo CI/CD v3 发布演示</title>
curl -s $D/api/info # {"app":"demo-backend","version":"3.0.0"}
curl -s -X POST $D/api/items -d '{"name":"经Ingress域名写入"}' # 201
实测全部通:浏览器开 http://demo.154.201.73.31.nip.io:32080 就能用应用,请求链路是 域名 → ingress-nginx → frontend Service → nginx 反代 → backend → MySQL。
4. 三件加完后的 demo 全貌
kubectl get all,ingress,hpa -n demo
pod/backend-… ×2 pod/frontend-… ×2 pod/mysql-…
service/backend service/frontend service/mysql
deployment/backend deployment/frontend deployment/mysql
horizontalpodautoscaler/backend ← CPU 50%,2~6
ingress/demo (demo.154.201.73.31.nip.io)
ArgoCD demo: Synced / Healthy ← HPA、Ingress 都纳入 GitOps
入口速查:
| 能力 | 入口 |
|---|---|
| 应用(域名) | http://demo.154.201.73.31.nip.io:32080 |
| 应用(NodePort 直连) | http://<节点>:31000 |
| Hubble UI(服务拓扑) | http://<节点>:31235 |
| 扩缩观察 | kubectl get hpa backend -n demo -w |
5. 踩坑汇总
| 坑 | 现象 | 解法 |
|---|---|---|
| metrics-server 证书 | kubectl top 报 Metrics API not available | 加 --kubelet-insecure-tls(kubeadm 自签 kubelet 证书) |
| HPA 与 GitOps 抢副本 | ArgoCD selfHeal 不停把副本改回固定值 | 从 Deployment 的 git manifest 删掉 replicas 字段,交给 HPA |
| ArgoCD 不立即同步 | 改完 Git 等好几分钟才生效 | kubectl annotate app <app> -n argocd argocd.argoproj.io/refresh=hard --overwrite |
| ingress 80 端口不通 | hostPort 80 内外网都连不上 | 云只转发 NodePort 段 + 公网是 NAT;ingress 走 NodePort(32080),生产用 LB 映射 80 |
HPA cpu: <unknown> | 刚建 HPA targets 显示 unknown | 等 ~30s 首次采集;持续 unknown 才查 metrics-server |