AI Infra 训练营
总览
  • 总览
  • 完整安装
  • 核心 K8s
  • Cilium 网络
  • Longhorn 存储
  • 监控日志
  • CI / GitOps
  • 安全准入
  • CI/CD 实战(MySQL+Go+Vue)
  • HPA/Ingress/Hubble 实战
  • 面试速查 + 真实踩坑
  • Day 0 · 新手接管 Runbook
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
命令手册
HiHuo 主站
GitHub
总览
  • 总览
  • 完整安装
  • 核心 K8s
  • Cilium 网络
  • Longhorn 存储
  • 监控日志
  • CI / GitOps
  • 安全准入
  • CI/CD 实战(MySQL+Go+Vue)
  • HPA/Ingress/Hubble 实战
  • 面试速查 + 真实踩坑
  • Day 0 · 新手接管 Runbook
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
命令手册
HiHuo 主站
GitHub
  • 实操 Runbook

    • Runbook 总览:从零部署、查看、调试
    • 完整安装总 Runbook:5 台 Ubuntu 到可用平台
    • 核心 K8s Runbook:apiserver / etcd / kubelet / containerd / HAProxy
    • Cilium 网络 Runbook:安装、查看、调试
    • Longhorn 存储 Runbook:安装、查看、调试
    • 监控日志 Runbook:Prometheus / Grafana / Loki / Alertmanager
    • CI / GitOps Runbook:Harbor / Gitea / Jenkins / Kaniko / ArgoCD
    • 安全准入 Runbook:RBAC / PSA / Kyverno / ResourceQuota
    • 实战 Runbook:MySQL + Go + Vue 全链路 CI/CD 真实发布
    • 实战 Runbook:给应用加 HPA 自动扩缩 + Ingress 域名 + Hubble 流量观测
    • 面试速查:这套平台 + 高频问答 + 真实踩坑

实战 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
在 GitHub 上编辑此页
Prev
实战 Runbook:MySQL + Go + Vue 全链路 CI/CD 真实发布
Next
面试速查:这套平台 + 高频问答 + 真实踩坑