Kubernetes 监控体系¶
作者:JiangChong | 撰写时间:2026年07月
✅ 学完本文你将能够: ✅ 理解 K8s 监控体系的整体架构和指标管道 ✅ 使用 Prometheus 和 Grafana 搭建集群监控 ✅ 配置 HPA 实现水平自动扩缩容 ✅ 了解 VPA 和 Vertica 专属监控方案
前置阅读:请先读 Kubernetes 核心架构 和 Kubernetes Pod 与 Deployment 实战。本文沿用系列统一的实验环境:Kind 集群
k8s-playground。
0 本文概览¶
05 篇讲完了「谁能做什么」的安全控制,现在来解决另一个问题:集群跑起来了,但它健康状况怎么样?Pod 内存是不是快爆了?为什么请求突然变慢?HPA(HorizontalPodAutoscaler,水平自动扩缩容)到底触发了没有?
kubectl top node → 节点 CPU/内存够不够? → metrics-server
看历史趋势 + 设告警 → Prometheus + Grafana → 指标管道
流量涨了自动加 Pod → HPA(水平扩缩) → 自动伸缩
Pod 规格不合理 → VPA(VerticalPodAutoscaler,垂直扩缩)→ 资源优化
本文部分组件(Prometheus、Grafana、Loki)需要 Helm 部署,完整 Grafana + Loki + AlertManager 栈预计 2-4GB,Kind 单节点不做要求。Prometheus 单实例(~500MB)、metrics-server 和 HPA 均可在
k8s-playground中实操。什么是 Helm? 你可以理解为 K8s 的包管理器——就像
apt install nginx一键安装软件,helm install prometheus一键部署 Prometheus 及其所有依赖(ConfigMap、Deployment、Service、RBAC 等十几个 YAML 文件)。Helm Chart 就是这些 YAML 的打包模板。本文为了在 Kind 中跑通,直接用kubectl apply手写了最简配置,不走 Helm。如果要用 Helm 在 Kind 中部署监控栈,需要做三件事:
- 装 Helm:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash- 重建 Kind 集群:Helm Chart 部署的 Prometheus 也需要从 kubelet 抓指标,和 metrics-server 一样需要
--kubelet-insecure-tls。在kind-config.yaml里写入:- 重建集群:
kind delete cluster --name k8s-playground && kind create cluster --name k8s-playground --config ~/kind-config.yaml(⚠️ 会清掉现有所有实验数据)。然后helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace --set prometheus.server.persistentVolume.enabled=false。本文没有选择这条路,因为用单实例 Prometheus(后面 3.2 节)已经足够理解指标采集和查询的核心流程,不需要为了一套 UI 重建整个实验环境。
1. 监控全貌¶
一个完整的 K8s 监控体系,通常被概括为可观测性三大支柱:
┌──────────────────────────────────────────┐
│ 可观测性三大支柱 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 指标 │ │ 日志 │ │ 链路 │ │
│ │ (Metrics)│ │ (Logs) │ │ (Traces) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ │
│ │Prom │ │Loki/ │ │Jaeger │ │
│ │etheus │ │EFK │ │Tempo │ │
│ └───┬───┘ └───────┘ └───────┘ │
│ │ │
│ ┌───▼───┐ │
│ │Grafana│ ← 可视化层 │
│ └───────┘ │
└──────────────────────────────────────────┘
指标(Metrics) — 回答「发生了什么」。比如 CPU 使用率、内存占用量、Pod 重启次数。这些是随时间变化的数字,可以设阈值告警("CPU > 90% 通知我"),也是 HPA 自动扩缩容的依据。Prometheus 负责存储和查询这些数字,Grafana 负责把它们画成折线图和仪表盘。
日志(Logs) — 回答「为什么发生」。指标告诉你 CPU 飙了,但不知道是谁触发的。日志里有请求路径、参数、错误堆栈。Pod 重启后 kubectl logs 只能看到当前的输出,旧日志随 Pod 消失——所以需要 Loki 或 EFK 把日志集中存起来。
链路追踪(Traces) — 回答「卡在哪一步」。一个请求经过 Ingress → Service A → Service B → 数据库,链路追踪能告诉你 B 花了 3 秒而 A 只花了 50ms,瓶颈一目了然。分布式系统中尤其重要。
本文重点在指标这条线:从 cAdvisor 原始数据 → metrics-server 聚合 → Prometheus 存储 → Grafana 可视化 → HPA 自动伸缩。日志聚合见第 7 节,链路追踪见第 8 节。
2. 指标管道(Metrics Pipeline)¶
2.1 kubelet / cAdvisor — 底层指标源¶
每个节点上的 kubelet 集成了 cAdvisor 组件,自动采集该节点上所有容器的 CPU、内存、网络、磁盘等原始指标。
- Metrics Server 通过 kubelet 的
/metrics/resource端点获取资源指标(CPU/Memory)的聚合数据,这是kubectl top和 HPA 的数据来源 - Prometheus 通过 kubelet 的
/metrics/cadvisor端点获取容器级原始指标(container_*前缀),可做更细粒度的分析
2.2 metrics-server — 集群级聚合器¶
metrics-server 是 K8s 的轻量级核心指标管道,从每个 kubelet 收集资源使用数据,供 kubectl top、HPA 和 VPA 使用。
在 k8s-playground 中安装:
Kind 集群的 kubelet 使用自签证书,直接装 metrics-server 会因为 TLS 验证失败而无法采集数据。需要加 --kubelet-insecure-tls 参数:
- 一键部署 metrics-server(这个 YAML 包含 Deployment、Service、ServiceAccount、RBAC 等全套资源)
如果镜像拉不下来(国内网络),步骤 1 改为以下替代流程(步骤 2、3 照常执行):
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml kubectl patch deployment metrics-server -n kube-system \ --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'# ① docker pull 走 DaoCloud 加速器(02 篇已配) docker pull docker.m.daocloud.io/registry.k8s.io/metrics-server/metrics-server:v0.7.2 # ① # ② 导入 Kind 节点 docker save docker.m.daocloud.io/registry.k8s.io/metrics-server/metrics-server:v0.7.2 \ # ② | docker exec -i k8s-playground-control-plane ctr -n k8s.io images import - # ③ kubectl set image 换镜像(会触发滚动更新,冲掉之前的 --kubelet-insecure-tls) kubectl set image deployment/metrics-server -n kube-system \ # ③ metrics-server=docker.m.daocloud.io/registry.k8s.io/metrics-server/metrics-server:v0.7.2 # ④ 重新 patch --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"}]' - 等待 metrics-server Pod 就绪
- 验证 —— 能看到节点和 Pod 的资源用量了!
--kubelet-insecure-tls仅在 Kind/Minikube 等测试环境使用,生产环境必须用正经 CA 签发的 kubelet 证书。
数据管道:
注意: metrics-server 只存内存快照,无历史数据。需要历史趋势和告警就必须上 Prometheus。
# 清理(后面的实验不需要 metrics-server 时执行)
kubectl delete -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
3. Prometheus — 指标存储与告警¶
Prometheus 是 CNCF 毕业项目,也是 K8s 监控的事实标准。
3.1 架构概览¶
ServiceMonitor ──► 发现 target
│
应用 / 组件 ──► 暴露 /metrics ──► Prometheus Server
│
┌──────┴──────┐
│ │
TSDB AlertManager
│ │
Grafana ────► 通知渠道
│ (钉钉/PagerDuty/邮件)
┌───────┴───────┐
│ Dashboard │
3.2 动手实操:在 Kind 中部署最简 Prometheus¶
完整 kube-prometheus-stack(Prometheus + Grafana + AlertManager + node-exporter)资源占用 2-4GB,Kind 跑不动。但只跑 Prometheus 本身就够了——它能抓取指标、存储时序数据、提供 Web UI 查询。下面在 k8s-playground 中部署最精简版:
# 1. 创建 namespace 和 RBAC(Prometheus 需要读 kubelet metrics 端点)
kubectl create namespace monitoring
# namespace/monitoring created
kubectl create clusterrolebinding prometheus-default --clusterrole=cluster-admin \
--serviceaccount=monitoring:default
# clusterrolebinding.rbac.authorization.k8s.io/prometheus-default created
# 2. 创建 Prometheus ConfigMap(告诉它去抓哪些 /metrics)
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'kubelet-cadvisor'
scheme: https
tls_config:
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
static_configs:
- targets: ['k8s-playground-control-plane:10250']
EOF
# configmap/prometheus-config created
# 3. 部署 Prometheus
kubectl apply -f - << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v3.0.0
args: ["--config.file=/etc/prometheus/prometheus.yml"]
ports:
- containerPort: 9090
volumeMounts:
- name: config
mountPath: /etc/prometheus
volumes:
- name: config
configMap:
name: prometheus-config
EOF
# deployment.apps/prometheus created
# 如果拉不下来(国内网络),本机 crane pull + scp(方法同下面 Grafana 的步骤 1 中的注释)
# 4. 验证(等 Pod Running)
kubectl wait --for=condition=ready pod -l app=prometheus -n monitoring --timeout=60s
kubectl expose deployment prometheus --port=9090 -n monitoring
kubectl port-forward -n monitoring svc/prometheus 9090:9090
然后用 curl 验证 Prometheus 已经抓到了数据(无桌面的服务器环境用 curl 代替浏览器):
curl -s 'http://localhost:9090/api/v1/query?query=kubelet_running_containers' \
| python3 -m json.tool | head -20
# 返回 JSON 数据说明 Prometheus 已经从 kubelet 抓到了容器指标
# 有桌面环境的可以用浏览器打开 http://localhost:9090,在 Graph 页面输入 kubelet_running_containers 点 Execute

这只是验证流程。生产环境部署用 Operator + ServiceMonitor(见 3.3 节),能自动发现新 target、管理告警规则。
| 组件 | 采集内容 | 重要性 |
|---|---|---|
| kubelet | 节点和容器资源指标 | 必选 |
| kube-state-metrics | K8s 对象状态(Pod、Deployment、Node 等) | 必选 |
| node-exporter | 节点 OS 级别指标(磁盘、网络、CPU 负载) | 必选 |
| kube-apiserver | API Server 请求延迟和错误率 | 高 |
| kube-scheduler | 调度决策指标 | 中等 |
| kube-controller-manager | 控制器调谐指标 | 中等 |
| etcd | etcd 磁盘 fsync 延迟 | 关键 |
Prometheus 的工作方式是拉取(Scrape):每隔一段时间(默认 15 秒),它去每个组件的
/metrics端点抓取当前的指标数据。这与传统的"组件主动上报"(Push)模式不同。
3.3 ServiceMonitor — 自动发现 target¶
前面 3.2 节用的是 static_configs 手动指定 kubelet IP——这在生产环境中不可维护:每次新增一个 Vertica 实例、每次 Pod IP 变化,都得改 ConfigMap 重载 Prometheus。
ServiceMonitor 解决这个问题: 它是 Prometheus Operator 提供的一种 CRD(Custom Resource Definition),告诉 Prometheus「去带 app: vertica-exporter 标签的 Service 上抓 /metrics」。Operator 自动发现新 Pod,Prometheus 配置自动更新,完全不需要手动改 ConfigMap。
apiVersion: monitoring.coreos.com/v1 # Prometheus Operator 提供的 CRD
kind: ServiceMonitor # 告诉 Operator 发现了新 target
metadata:
name: vertica-monitor
namespace: monitoring # ServiceMonitor 本身所在的 namespace
spec:
namespaceSelector: # 去哪些 namespace 找 target
matchNames:
- vertica # 只在 vertica namespace 找
selector: # 找带哪些 label 的 Service
matchLabels:
app: vertica-exporter # 匹配 app=vertica-exporter 的 Service
endpoints: # 怎么抓这个 target
- port: metrics # 抓 Service 上叫 metrics 的端口
interval: 15s # 每 15 秒抓一次
scrapeTimeout: 10s # 单次抓取超时 10 秒
和我们 3.2 节的手动配置对比:
| 3.2 节 static_configs | ServiceMonitor | |
|---|---|---|
| 发现方式 | 手写 IP + 端口 | 按标签自动发现 |
| 新增 target | 改 ConfigMap + 重载 | 自动 |
| 需要 Operator | 否 | 是(prometheus-operator) |
| 适用场景 | 测试/固定 target | 生产环境 |
ServiceMonitor 需要 Prometheus Operator 支持(Kind 中没有,生产环境通过
kube-prometheus-stackHelm Chart 部署)。3.2 节的手动方式让你理解了底层原理——Operator 本质上是帮你自动生成scrape_configs。
动手验证:手动模拟 ServiceMonitor 的效果
在现有 Prometheus 上加一个新 target,看看 static_configs 和 ServiceMonitor 的对应关系:
# 1. 给 nginx Pod 加一个最简单的 /metrics(用 nginx 内置的 stub_status)
kubectl exec bot-test -n dev -- /bin/sh -c \
"cat > /etc/nginx/conf.d/status.conf << 'EOF'
server { listen 8080; location /metrics { stub_status; } }
EOF
nginx -s reload"
# 2. 创建 Service,打上标签
kubectl expose pod bot-test -n dev --port=8080 --name=nginx-metrics
kubectl label service nginx-metrics -n dev app=nginx-exporter
# 3. 更新 Prometheus ConfigMap,加一条 static_configs
kubectl patch configmap prometheus-config -n monitoring --type merge -p '
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "kubelet-cadvisor"
scheme: https
tls_config:
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
static_configs:
- targets: ["k8s-playground-control-plane:10250"]
- job_name: "nginx-exporter" # ← 新增的 job
static_configs:
- targets: ["nginx-metrics.dev.svc.cluster.local:8080"]
'
# 4. 重载 Prometheus 配置
kubectl rollout restart deployment prometheus -n monitoring
kubectl wait --for=condition=ready pod -l app=prometheus -n monitoring --timeout=60s
# 5. 验证 —— 新 target 已出现
curl -s 'http://localhost:9090/api/v1/targets' | python3 -m json.tool | grep -A3 'nginx-exporter'
# ⚠️ 注意:nginx stub_status 输出的不是 Prometheus 格式(是纯文本),
# 所以 target 会出现但 scrape 状态会显示为 DOWN(scrape error)。
# 生产环境中需要 nginx-prometheus-exporter 作为适配层转换格式。
# 这里的实验目的只是展示 static_configs 的 target 发现机制,
# 不影响对 ServiceMonitor 工作原理的理解。
上面步骤 3 的 scrape_configs 如果改用 ServiceMonitor 表达,就是:
# 不需要手写 static_configs,Operator 自动扫描 dev namespace 里
# 所有带 app=nginx-exporter 标签的 Service
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: nginx-monitor
namespace: monitoring
spec:
namespaceSelector:
matchNames: [dev]
selector:
matchLabels:
app: nginx-exporter
endpoints:
- port: "8080"
interval: 15s
对比:static_configs 要手动写
targets: [IP:Port];ServiceMonitor 只需要写selector.matchLabels,Operator 自动扫到匹配的 Service 并解析其 ClusterIP + Port。
# 清理 ServiceMonitor 演示
kubectl delete service nginx-metrics -n dev
kubectl label pod bot-test -n dev app-
4. Grafana — 可视化¶
在 k8s-playground 中部署 Grafana(~200MB,连已有 Prometheus):
# 1. 部署 Grafana
kubectl apply -f - << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:11.3.0
ports:
- containerPort: 3000
env:
- name: GF_AUTH_ANONYMOUS_ENABLED
value: "true" # 测试环境免登录
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: monitoring
spec:
selector:
app: grafana
ports:
- port: 3000
EOF
# 如果拉不下来(国内 Docker Hub 超时),用 crane(02 篇已装)在本机拉 + scp 传 Debian:
# MacBook: crane pull grafana/grafana:11.3.0 grafana.tar
# scp grafana.tar k8s@<debian-ip>:~/
# Debian: docker load -i grafana.tar
# docker save grafana/grafana:11.3.0 \
# | docker exec -i k8s-playground-control-plane ctr -n k8s.io images import -
# 2. 等待就绪 + 暴露端口
kubectl wait --for=condition=ready pod -l app=grafana -n monitoring --timeout=120s
kubectl expose deployment grafana --port=3000 -n monitoring 2>/dev/null
kubectl port-forward -n monitoring svc/grafana --address=0.0.0.0 3001:3000
浏览器打开 http://<debian-ip>:3001,配置和数据源见下节。
4.1 连接 Prometheus + 用 Explore 画图表¶
Grafana 11 界面直达数据源(需要使用admin/admin登陆):http://<debian-ip>:3001/datasources → Add new data source → Prometheus → URL 填 http://prometheus.monitoring.svc.cluster.local:9090 → Save & test(应返回绿色成功)。
然后验证:左侧菜单 → 🧭 Explore → 选 Prometheus → 输入框填 up → 右上角 Run query → 看到绿线说明数据源正常。
我们只部署了最简 Prometheus(kubelet 指标),没有 node_exporter 和 kube-state-metrics。Grafana Dashboard 市场(如 ID
1860、315)依赖这些额外 exporter 的指标,在当前环境中会全部空白。用 Explore 自建图表对我们的环境更实用:
在 Explore 里输入以下 PromQL,每试一个点 Run query,看到数据后点 Add to dashboard 保存:
up → 哪些 target 在抓(2 条线 = prometheus + kubelet)
kubelet_running_containers → 集群运行中的容器数
rate(kubelet_started_containers_total[5m]) → 过去 5 分钟每秒启动容器速率
prometheus_http_requests_total → Prometheus 收到的 HTTP 请求总数

完整监控栈(Helm 部署
kube-prometheus-stack)会自带 50+ 现成 Dashboard。本文选最简方案是为了在 Kind 中跑通核心流程——Explore 和 Dashboard 本质上查的是同一套 PromQL,学会 Explore 就不会被缺少 exporter 卡住。
# 清理
kubectl delete deployment grafana prometheus -n monitoring
kubectl delete service grafana prometheus -n monitoring
kubectl delete configmap prometheus-config -n monitoring
kubectl delete clusterrolebinding prometheus-default
kubectl delete ns monitoring
4.2 告警规则(Alert Rules)¶
以下是一些 K8s 核心告警规则的 PromQL 示例:
# 告警规则配置片段
groups:
- name: kubernetes-critical
rules:
# Pod 反复重启
- alert: KubePodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[5m]) * 60 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 反复重启"
# 节点不可用
- alert: NodeNotReady
expr: kube_node_status_condition{condition="Ready",status="true"} == 0
for: 5m
labels:
severity: critical
# CPU throttling 阈值高
# 这条 PromQL 的含义:`rate(...[5m])` 计算过去 5 分钟的平均每秒增长率;`cfs_throttled_seconds` 是容器被 CPU 限流的总秒数。所以整体含义是:过去 5 分钟,容器的 CPU 每秒被限流了多少秒。
- alert: ContainerCPUThrottling
expr: rate(container_cpu_cfs_throttled_seconds_total[5m]) / rate(container_cpu_cfs_periods_total[5m]) > 0.2
for: 10m
labels:
severity: warning
# 磁盘空间不足
- alert: NodeDiskPressure
expr: node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.1
for: 5m
labels:
severity: critical
# etcd fsync 延迟
- alert: EtcdSlowDisk
expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.5
for: 5m
labels:
severity: critical
5. HPA(HorizontalPodAutoscaler)— 水平自动扩缩容¶
HPA(HorizontalPodAutoscaler,水平 Pod 自动扩缩容器)根据监控指标自动调整 Pod 副本数。前提是 metrics-server 已安装(上一步 2.2 刚配好)。
5.1 动手实操:给 nginx Deployment 加自动扩缩¶
# 1. 创建一个 Deployment + 设定 CPU 资源请求(HPA 必须基于 requests 计算百分比)
kubectl apply -f - << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hpa
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: nginx-hpa
template:
metadata:
labels:
app: nginx-hpa
spec:
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: "100m" # HPA 的利用率 = 实际用量 / 这个值
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
EOF
# 2. 创建 HPA:目标 CPU 利用率 50%,副本数 1-5
kubectl autoscale deployment nginx-hpa --cpu=50% --min=1 --max=5 -n dev
# horizontalpodautoscaler.autoscaling/nginx-hpa autoscaled
# 3. 查看 HPA 状态(初始 TARGETS 显示 <unknown>/50% 是正常的,等 30 秒刷新)
kubectl get hpa -n dev
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
# nginx-hpa Deployment/nginx-hpa cpu: 0%/50% 1 5 1 69s
# 4. 生成负载 —— 在另一个终端跑一个 busybox 持续请求 nginx
kubectl expose deployment nginx-hpa --port=80 -n dev
kubectl run load-generator --image=busybox:1.36 -n dev -- /bin/sh -c \
"while true; do for i in 1 2 3 4 5; do wget -q -O- http://nginx-hpa; done; sleep 0.2"
# 5. 等 1-2 分钟后再看 HPA —— 副本数应该涨了
kubectl get hpa -n dev -w
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
# nginx-hpa Deployment/nginx-hpa cpu: 33%/50% 1 5 1 11m
# nginx-hpa Deployment/nginx-hpa cpu: 91%/50% 1 5 1 11m
# nginx-hpa Deployment/nginx-hpa cpu: 29%/50% 1 5 2 11m
# nginx-hpa Deployment/nginx-hpa cpu: 21%/50% 1 5 2 11m
# nginx-hpa Deployment/nginx-hpa cpu: 44%/50% 1 5 2 12m
# nginx-hpa Deployment/nginx-hpa cpu: 46%/50% 1 5 2 12m
# nginx-hpa Deployment/nginx-hpa cpu: 39%/50% 1 5 2 12m
# nginx-hpa Deployment/nginx-hpa cpu: 22%/50% 1 5 2 12m
# nginx-hpa Deployment/nginx-hpa cpu: 43%/50% 1 5 2 13m
# nginx-hpa Deployment/nginx-hpa cpu: 46%/50% 1 5 2 13m
# nginx-hpa Deployment/nginx-hpa cpu: 38%/50% 1 5 2 13m
# nginx-hpa Deployment/nginx-hpa cpu: 20%/50% 1 5 2 13m
# nginx-hpa Deployment/nginx-hpa cpu: 34%/50% 1 5 2 14m
# nginx-hpa Deployment/nginx-hpa cpu: 46%/50% 1 5 2 14m
# nginx-hpa Deployment/nginx-hpa cpu: 36%/50% 1 5 2 14m
# nginx-hpa Deployment/nginx-hpa cpu: 20%/50% 1 5 2 14m
# nginx-hpa Deployment/nginx-hpa cpu: 31%/50% 1 5 2 15m
# nginx-hpa Deployment/nginx-hpa cpu: 40%/50% 1 5 2 15m
# nginx-hpa Deployment/nginx-hpa cpu: 43%/50% 1 5 2 15m
# nginx-hpa Deployment/nginx-hpa cpu: 22%/50% 1 5 2 15m
# nginx-hpa Deployment/nginx-hpa cpu: 26%/50% 1 5 2 16m
# nginx-hpa Deployment/nginx-hpa cpu: 43%/50% 1 5 2 16m
# nginx-hpa Deployment/nginx-hpa cpu: 47%/50% 1 5 2 16m
# nginx-hpa Deployment/nginx-hpa cpu: 23%/50% 1 5 2 16m
# nginx-hpa Deployment/nginx-hpa cpu: 24%/50% 1 5 2 17m
# nginx-hpa Deployment/nginx-hpa cpu: 41%/50% 1 5 2 17m
当 CPU 降到 50% 以下后,HPA 会逐步缩容回 1 个副本。整个过程自动发生,不需要人工介入——这就是 HPA 的核心价值。
# 清理
kubectl delete hpa nginx-hpa -n dev
kubectl delete deployment nginx-hpa -n dev
kubectl delete pod load-generator -n dev
5.2 CPU/Memory 指标¶
上面的实操用的是 CPU 利用率,HPA 也支持同时监控多个指标。以下展示一个更复杂的 HPA 配置(注意这是 Vertica 场景的示例,依赖 VerticaDB Operator,不在当前 playground 中):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vertica-query
namespace: vertica
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: StatefulSet
name: vertica-sc
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Pod CPU 平均使用率达到 70% 触发扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
5.3 自定义指标(Custom Metrics)¶
HPA v2 API 支持四种指标类型:
| 指标类型 | 来源 | 典型场景 |
|---|---|---|
Resource |
Metrics Server(CPU/Memory) | 基础扩缩容 |
Pods |
Prometheus Adapter(Pod 级自定义指标) | 应用 QPS、连接数 |
Object |
Prometheus Adapter(K8s 对象级指标) | Ingress 请求率、Service 延迟 |
External |
外部系统(消息队列长度、云监控指标) | Kafka lag、SQS 队列深度 |
多指标取最大值: 当 HPA 配置了多个指标(如同时用 CPU 和 Memory),HPA 为每个指标分别计算期望副本数,然后取最大值。这确保所有约束都能满足。
使用 Prometheus Adapter 接入任意 Prometheus 指标:
metrics:
- type: Pods
pods:
metric:
name: vertica_query_concurrency
target:
type: AverageValue
averageValue: 50
HPA 工作原理:
例如:当前 3 个 Pod,CPU 平均使用率 80%,目标 70%:
容忍度(Tolerance): HPA 内置 10% 的容忍度(
tolerance: 0.1),只有当期望副本数与当前副本数的偏差超过 10% 时才会真正执行扩缩容。例如当前 3 个 Pod,期望 3.2 个(偏差 6.7%),HPA 不会触发扩容。该机制避免了指标微小波动导致的频繁扩缩。
5.4 HPA 使用注意事项¶
--horizontal-pod-autoscaler-sync-period:HPA 评估周期(默认 15s)behavior.scaleDown.stabilizationWindowSeconds:缩容稳定窗口(默认 300s / 5min),防止指标抖动导致频繁缩容- 不要同时使用 HPA 和 VPA 对同一指标扩缩容 — 两者会产生冲突
- 以上 HPA 实操针对无状态服务(如 nginx),扩缩容只是加减 Pod 副本。对于有状态数据库(如 Vertica):扩容需要新节点加入集群协调,缩容需要 drain 数据,直接使用 HPA 可能引发数据一致性问题。建议使用 Operator 管理的自定义扩缩容策略。
6. VPA(VerticalPodAutoscaler)— 垂直自动扩缩容¶
HPA 解决的是「副本数不够」——流量大了加 Pod。但还有一个问题 HPA 解决不了:每个 Pod 的 CPU/Memory 配额该设多少? 设大了浪费资源,设小了被 OOM 杀掉或被限流。手动调?每个服务的负载模式不同、每天每时都变,根本调不过来。
VPA(VerticalPodAutoscaler,垂直 Pod 自动扩缩容器)自动做这件事:根据 Pod 的历史资源使用情况推荐最优值,甚至可以自动重建 Pod 应用新配额。
HPA vs VPA 一句话: HPA 横向加 Pod(水平),VPA 纵向改 Pod 大小(垂直)。两者不能同时管同一个资源的同一个指标——HPA 改副本数,VPA 改 Pod 规格,互相干扰。
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: vertica-vpa
spec:
targetRef: # VPA 的目标对象
apiVersion: apps/v1
kind: StatefulSet
name: vertica-sc
updatePolicy:
updateMode: "Off" # 先设 Off 只看推荐不动手,确认合理后改 Auto
resourcePolicy:
containerPolicies:
- containerName: vertica # 对哪个容器生效
minAllowed: # 安全下限(避免 VPA 建议太小导致 OOM)
cpu: "2"
memory: 8Gi
maxAllowed: # 安全上限(避免 VPA 建议太大浪费资源)
cpu: "16"
memory: 64Gi
四种模式:
| 模式 | 行为 | 适用场景 |
|---|---|---|
Off |
仅生成推荐值,不动 Pod | 先观察、评估 |
Initial |
只在新 Pod 创建时设推荐值 | 保守策略 |
Auto |
自动重建 Pod 以应用新值 | 信任 VPA 后启用 |
Recreate |
同 Auto,运行中的 Pod 也立即重建 | 激进策略 |
VPA 的 Recommender 组件依赖 metrics-server 的历史数据(至少运行 24 小时后才能给出可靠推荐),Updater 负责实际执行 Pod 重建,Admission Controller 在 Pod 创建时拦截并注入推荐资源值。Kind 单节点部署 VPA 可行但意义不大——推荐结果需要大量历史数据支撑。
查看 VPA 推荐(部署后):
kubectl describe vpa vertica-vpa -n vertica
# Recommendation:
# Container Recommendations:
# Container Name: vertica
# Lower Bound: cpu: 200m, memory: 4Gi
# Target: cpu: 500m, memory: 8Gi ← 推荐值
# Upper Bound: cpu: 1, memory: 16Gi
7. 日志聚合¶
指标告诉你「发生了什么」(CPU 飙升),但查原因需要日志——「哪个请求触发的?参数是什么?」。K8s 中 kubectl logs 有两个致命短板:
- Pod 重启即销毁:Pod 被 OOM 杀掉后重新调度,旧 Pod 的日志跟着一起消失——你永远看不到它崩溃前最后输出了什么
- 跨 Pod 搜索无力:一个请求经过了 Ingress → Service A → Service B,分别在三处输出日志,
kubectl logs只能逐个 Pod 翻
日志聚合把分散的日志统一收集、存储、提供搜索——Pod 重启了不丢,跨 Pod 一行命令搜出来。
7.1 EFK / Loki Stack¶
| 方案 | 组件 | 特点 | 适用 |
|---|---|---|---|
| EFK | Elasticsearch + Fluent Bit + Kibana | 全文索引每一行日志,查询快但吃资源 | 大厂,预算足 |
| Loki | Loki + Promtail + Grafana | 只索引标签(namespace/pod/label),日志内容不建索引 | 中小集群,与现有 Grafana 集成 |
Promtail 以 DaemonSet 运行在每个节点上,tail 每个容器的日志文件,推送给 Loki。Loki 和 Prometheus 共享 Grafana 界面——同一个页面上看指标图表的下面就是相关日志,值班排查不再需要切三个窗口。
helm upgrade --install loki grafana/loki-stack \
--namespace monitoring \
--set promtail.enabled=true \
--set grafana.enabled=false
Loki 和 Grafana 一样,完整部署需要 ~1GB+ 资源,Kind 环境不做实操要求。核心概念:日志经过 DaemonSet(Promtail)→ 中央存储(Loki)→ Grafana 搜索界面。
8. 链路追踪(Traces)¶
指标告诉你「哪里慢了」,日志告诉你「发生了什么错误」,但当请求经过 Ingress → Service A → Service B → Service C → Database 这样一条长链时,你需要知道每一步各花了多少时间——这就是链路追踪。
例如:一次 API 请求花了 500ms,Prometheus 只能告诉你「这个接口 P99 延迟 500ms」,但不知道是 Service A 慢还是 Database 慢。Traces 把它拆成 A: 20ms → B: 30ms → DB: 430ms → 「原来是数据库查询占了大部分时间」。
每条 trace 由多个 span 组成(一个 span = 一次调用),共享一个
traceID。在 Jaeger UI 里看到的瀑布图就是 spans 按时间排列的可视化结果。
K8s 生态中常用的方案:
| 方案 | 来源 | 特点 | 与 Grafana 集成 |
|---|---|---|---|
| Jaeger | Uber 开源 | CNCF 毕业,UI 最成熟,社区最大 | 需单独部署 |
| Tempo | Grafana 出品 | 与 Loki/Prometheus 统一界面,存对象存储成本低 | ✅ 原生 |
| Zipkin | Twitter 开源 | 最老牌,轻量级 | 需单独部署 |
链路追踪需要应用代码埋点(在每次 HTTP/gRPC 调用时创建 span 并传递 traceID),不像指标和日志那样基础设施层就能采集。目前行业标准库是 OpenTelemetry(OTel)——一套 SDK 同时输出 traces + metrics + logs 到任意后端(Jaeger、Tempo、Zipkin 都支持)。本文不做实操。
9. Vertica on K8s 监控¶
前面讲的都是 K8s 通用监控。Vertica 有自己的专属指标——节点状态、查询耗时、ROS 容器数——这些需要 Vertica Exporter 来暴露。下面回到本系列的核心场景。
9.1 Vertica Prometheus Exporter¶
Vertica 官方提供 Prometheus Exporter,作为 Sidecar 容器和 Vertica Pod 部署在一起,通过 localhost 连接 Vertica 进程,暴露 /metrics 端点。再加上 3.3 节的 ServiceMonitor,整个链路是:
Vertica Pod (vertica-exporter sidecar, port 9724)
│
▼
Service (带标签 app: vertica-exporter)
│
▼
ServiceMonitor (自动发现标签) → Prometheus scrape → Grafana Dashboard
# VerticaDB CR 配置(需 VerticaDB Operator 支持)
spec:
sidecars:
- name: vertica-exporter
image: vertica/prometheus-exporter:latest
env:
- name: VERTICA_HOST
value: "localhost" # 和 Vertica 进程在同一个 Pod,localhost 直连
ports:
- containerPort: 9724
name: metrics # ServiceMonitor 按这个端口名匹配
9.2 关键监控指标¶
| 类别 | 指标 | 告警条件示例 |
|---|---|---|
| 集群状态 | vertica_node_up |
= 0 → 节点(集群)宕机 |
| 查询性能 | vertica_query_runtime_seconds |
P99 > 60s → 长查询阻塞 |
| 存储 | vertica_storage_used_bytes |
> 80% 配额 → 扩容 |
| 并发 | vertica_active_sessions |
> resource_pool 上限 → 排队 |
| 资源池 | vertica_resource_pool_memory_used |
> 90% → OOM 风险 |
| ROS | vertica_ros_containers_count |
> 1024 → pushback,Tuple Mover 跟不上 |
结合 Kubernetes RBAC 权限与安全 中的 RBAC 配置,确保 Prometheus 有权限跨 Namespace 采集 Vertica 指标。Prometheus 直接通过 kubelet API(端口 10250)抓取容器指标,认证依赖 bearer token(由 ServiceAccount 自动挂载),不需要额外的
pods/proxyRBAC 权限——只需确保 ServiceAccount 所在的 Namespace 能访问目标 kubelet 即可(3.2 节用了cluster-admin,生产环境建议改为最小权限的 ClusterRole)。
10. 总结¶
| 层次 | 工具 | 用途 | 本文实操 |
|---|---|---|---|
| 核心指标 | metrics-server | 基础 CPU/Memory,供 HPA、kubectl top |
✅ 已部署 |
| 指标存储 | Prometheus | 全量指标、历史数据 | ✅ 已部署 + Explore |
| 可视化 | Grafana | 折线图、仪表盘 | ✅ 已部署 + 连 Prometheus |
| 自动扩缩 | HPA + VPA | 水平和垂直弹性伸缩 | HPA ✅ / VPA 概念 |
| 日志 | Loki / EFK | 日志聚合与搜索 | 概念 |
| 链路 | Jaeger / Tempo | 分布式追踪 | 概念 |
| 数据库监控 | Vertica Exporter + ServiceMonitor | Vertica 专属指标 | 概念 |
三条核心 PromQL 记住就够用:
rate(metric_total[5m]) # 计算每秒增长率
histogram_quantile(0.99, ...) # 计算 P99 分位数
sum(rate(...)) by (pod) # 按 Pod 聚合
→ 继续阅读 Kubernetes 二进制部署实战 了解如何从零搭建生产级 K8s 集群。