系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布
14 · 集群实战:Metrics Server 部署与 HPA / Ingress 实测
前两篇我们分别在「监控日志与 HPA 弹性伸缩」「容器网络」中建立了理论认知。本篇把理论搬到真实 4 节点集群(1 Master + 3 Worker,Kubernetes v1.28.2)上跑通三件最常被问到的事:
kubectl top为什么一开始用不了?—— 部署Metrics Server;- HPA 到底能不能自动扩缩?—— 用 CPU 压测把它从 2 副本打到 10 副本;
- Ingress 七层路由怎么暴露业务?—— 用ingress-nginx把真实 nginx 通过域名
nginx.local暴露出来。
所有输出均来自真实集群,踩坑一并记录。
┌──────────────────────────────────────────┐ │ K8s Cluster │ kubectl top ─────────▶ │ Metrics Server (Deployment, 1 replica) │ HPA 读取指标 ◀────────│ │ 请求 kubelet /stats/summary │ │ ▼ │ │ kubelet (每个节点, :10250) ◀── cAdvisor │ │ │ HPA 控制器 ── 监控 CPU% ─▶ Deployment/hpa-demo ── 扩/缩 ──▶ Pod×N │ │ │ 外部浏览器 ── Host:nginx.local ─▶ ingress-nginx(NodePort 30080) │ │ 七层路由 │ ▼ │ Service/nginx ──▶ Pod(nginx:1.25) │ └──────────────────────────────────────────┘一、部署 Metrics Server:让kubectl top说话
Kubernetes 自身不采集资源指标。kubectl top的数据来自Metrics Server(metrics.k8s.io API),它周期性地从每个节点的 kubelet 拉取/stats/summary。
1.1 镜像与参数改造(国内环境必做)
官方components.yaml默认从registry.k8s.io拉镜像,国内不可达。两处必改:
# 1) 镜像换成阿里云同步源sed-i's#registry.k8s.io/metrics-server/metrics-server:v0.7.2#registry.aliyuncs.com/google_containers/metrics-server:v0.7.2#g'components.yaml# 2) 自签证书 + 跳过 kubelet TLS 校验(演示环境最简方案)# 在 args 段追加:# - --kubelet-insecure-tls# - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname踩坑 1:必须加
--kubelet-insecure-tls。否则 Metrics Server 用 kubelet 的 API 证书做校验,而我们的 kubelet 证书 SAN 里只有内网 IP,校验会失败,日志报x509: certificate is valid for ... not ...。
1.2 部署与验证(真实输出)
kubectl apply-fcomponents.yaml等待约 1 分钟,APIService 变为AVAILABLE=True:
NAME SERVICE AVAILABLE AGE v1beta1.metrics.k8s.io kube-system/metrics-server True 54s此时kubectl top正式可用:
$ kubectltopnodes NAME CPU(cores)CPU% MEMORY(bytes)MEMORY% ecs-eb8a-0001 76m0% 1139Mi7% ecs-eb8a-0002 18m0% 714Mi4% ecs-eb8a-0003 16m0% 777Mi5% ecs-eb8a-0004 17m0% 692Mi4% $ kubectltoppods --all-namespaces --sort-by=cpu NAMESPACE NAME CPU(cores)MEMORY(bytes)kube-system kube-apiserver-ecs-eb8a-0001 32m 232Mi kube-system etcd-ecs-eb8a-0001 11m 36Mi kube-system kube-controller-manager-ecs-eb8a-0001 7m 45Mi kube-system metrics-server-5c5f7f897b-57wcz 5m 16Mi default nginx-6cbd46d475-5slfj 1m 7Mi踩坑 2:刚
apply完立刻kubectl top会报Metrics API not available。这是正常的——APIService 聚合注册 + Metrics Server 首次拉取需要几十秒,等它AVAILABLE即可。
二、HPA 自动伸缩实测:2 副本 → 10 副本
HPA(HorizontalPodAutoscaler,水平Pod自动伸缩器)是「声明式 API + 控制器模式」最直观的落地:你声明「CPU 平均利用率不超过 50%」,控制器持续比对实际值并调谐副本数。
2.1 工作负载 + HPA 定义
apiVersion:apps/v1kind:Deploymentmetadata:{name:hpa-demo}spec:replicas:2template:spec:containers:-name:hpa-demoimage:nginx:1.25# 已离线分发到所有节点resources:requests:{cpu:100m,memory:64Mi}# 不设 limit,压测时可真实冲高---apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:{name:hpa-demo}spec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:hpa-demominReplicas:2maxReplicas:10metrics:-type:Resourceresource:name:cputarget:{type:Utilization,averageUtilization:50}# 目标:每副本 CPU ≈ 50mbehavior:scaleDown:stabilizationWindowSeconds:60# 缩容稳定窗口,避免抖动HPA 计算的分母是你声明的 requests.cpu。所以
requests.cpu不写,HPA 就无法算利用率——这是新手最常见的「HPA 不生效」原因。
2.2 压测:在两个 Pod 里制造 CPU 忙循环
forpin$(kubectl get pods-lapp=hpa-demo-ojsonpath='{.items[*].metadata.name}');dokubectlexec$p--sh-c'nohup sh -c "while true; do : ; done" >/dev/null 2>&1 &'done2.3 真实结果:自动扩容到上限
负载施加约 100 秒后:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE hpa-demo Deployment/hpa-demo 200%/50% 2 10 10 2m44s $ kubectl get pods -l app=hpa-demo -o wide NAME READY STATUS NODE hpa-demo-dcd8589df-6dzrn 1/1 Running ecs-eb8a-0003 hpa-demo-dcd8589df-qgsq9 1/1 Running ecs-eb8a-0004 hpa-demo-dcd8589df-bp7tz 1/1 Running ecs-eb8a-0004 hpa-demo-dcd8589df-c9vt4 1/1 Running ecs-eb8a-0003 hpa-demo-dcd8589df-mcrdw 1/1 Running ecs-eb8a-0003 hpa-demo-dcd8589df-rh76r 1/1 Running ecs-eb8a-0002 hpa-demo-dcd8589df-rl97d 1/1 Running ecs-eb8a-0004 hpa-demo-dcd8589df-s2nmw 1/1 Running ecs-eb8a-0002 hpa-demo-dcd8589df-vjp4t 1/1 Running ecs-eb8a-0004 hpa-demo-dcd8589df-wjjjf 1/1 Running ecs-eb8a-0002TARGETS 从0%/50%升到200%/50%,副本数从 2 自动扩到 10(命中maxReplicas),且均衡散落到 3 个 Worker 节点。这正是「控制器调谐循环」:
┌────────────┐ 读取 ┌──────────────────┐ 实际状态 ──▶│ HPA 控制器 │◀────────│ Metrics Server │ (CPU 200%) └─────┬──────┘ └──────────────────┘ │ 调谐(Reconcile) ▼ ┌────────────┐ 计算 desired = current/target × replicas │ Deployment │──▶ 副本 2 → 10 └────────────┘停掉负载、等待缩容稳定窗口(60s)后,副本自动回缩到minReplicas=2。扩容快、缩容稳,这就是 HPA 的设计哲学。
三、Ingress 七层路由实测:用域名暴露真实 nginx
ClusterIP / NodePort 只能做四层(IP+端口)暴露。要在80 端口用不同域名/路径路由到不同服务,必须靠Ingress(七层)。
3.1 部署 ingress-nginx(精简版,规避 webhook 复杂度)
采用旧版 controller(v1.10.1,阿里云镜像),以 NodePort 30080/30443 暴露,便于从节点公网 IP 直接验证:
apiVersion:apps/v1kind:Deploymentmetadata:{name:nginx-ingress-controller,namespace:ingress-nginx}spec:template:spec:containers:-name:nginx-ingress-controllerimage:registry.aliyuncs.com/google_containers/nginx-ingress-controller:v1.10.1args:-/nginx-ingress-controller---default-backend-service=default/nginx# 复用 default/nginx 作默认后端---ingress-class=nginx后端应用与 Ingress 规则:
apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:nginxannotations:kubernetes.io/ingress.class:nginxnginx.ingress.kubernetes.io/rewrite-target:/spec:ingressClassName:nginxrules:-host:nginx.localhttp:paths:-path:/pathType:Prefixbackend:{service:{name:nginx,number:80}}3.2 验证外部访问(真实输出)
# 经 ingress-nginx 的 NodePort(30080)+ Host 头访问curl-H'Host: nginx.local'http://<节点公网IP>:30080/返回真实 nginx 默认页:
<!DOCTYPEhtml><html><head><title>Welcome to nginx!</title></head><body><h1>Welcome to nginx!</h1></body></html>3.3 踩坑合集(本篇最值钱的部分)
踩坑 3(404):缺 IngressClass。
只写ingressClassName: nginx但集群里没有对应的 IngressClass 资源,controller 不接纳该 Ingress,请求落到 controller 内部默认 404。解决:补一个IngressClass(见deploy/ingress-class.yaml,controller: k8s.io/ingress-nginx)。
踩坑 4(503):RBAC 缺
endpointslices。
旧版 controller 通过EndpointSlices(discovery.k8s.io)获取后端端点。若 ClusterRole 只授权了老endpoints,日志会报Service "default/nginx" does not have any active Endpoint,外部访问得到 503。必须补:-apiGroups:["discovery.k8s.io"]resources:["endpointslices"]verbs:["list","watch"]-apiGroups:["coordination.k8s.io"]# 领导者选举需要resources:["leases"]verbs:["get","list","watch","create","update","patch"]
踩坑 5(镜像拉取):演示镜像走离线分发。
公共镜像在 Worker 上拉取不稳。本集群把nginx:1.25在 Master 用ctr images export导出为 tar,经内网 HTTP 分发到 3 个 Worker 后ctr images import并打标准 tagdocker.io/library/nginx:1.25,Pod 用标准名即可命中本地镜像,彻底绕开公网拉取。
四、小结 / 核心要点
- Metrics Server 是
kubectl top与 HPA 的前置依赖,国内部署务必改镜像源 + 加--kubelet-insecure-tls。 - HPA 玩的是「声明式期望 vs 实际状态」的调谐:本实测把 2 副本在 CPU 压测下自动扩到 10,停载后回缩到 2。
- HPA 生效前提是 Pod 声明了
requests.cpu,否则无法计算利用率。 - Ingress 提供七层路由,但旧版 ingress-nginx 对
IngressClass与endpointslicesRBAC 有硬性要求,漏掉任何一个都会以 404/503 形式坑你。 - 镜像分发是国内部署第一痛点:离线
export/import+ 打标准 tag 是最稳妥的兜底方案。
配套部署脚本与 YAML 见仓库deploy/:metrics-components.yaml、hpa-demo.yaml、ingress-nginx-legacy.yaml、ingress-class.yaml、nginx-app-ingress.yaml。