监控在 Kubernetes 里的位置比传统主机时代微妙得多。节点是动态的,Pod 是易失的,一个 Pod 被驱逐后重新调度到另一台节点,原来的指标上下文就丢了。靠 SSH 上去看日志这套流程在这里根本走不通。可观测性不是一个工具,而是把”系统现在怎么样、为什么会变成这样、接下来会怎样”这三类问题,拆给不同工具去回答的体系。本文按 Metrics、Logs、Traces 三条线展开 Prometheus + Grafana + Loki + Jaeger 的协作,以及告警规则怎么从指标映射回真实故障。
一、可观测性三大支柱
1.1 指标、日志、链路追踪
| 支柱 | 工具 | 用途 |
|---|---|---|
| Metrics | Prometheus | 时间序列指标,资源使用率 |
| Logs | Loki/ELK | 日志聚合分析 |
| Traces | Jaeger | 请求链路追踪 |
二、Prometheus 架构
2.1 组件架构
Prometheus 的核心是 pull 模型:Server 主动去 Exporter 拉指标,不依赖应用推数据。这套机制在 Kubernetes 里尤其契合,因为 Service Discovery 能自动发现新 Pod 的 metrics 端点,节点一扩容,新节点上的 node_exporter 立刻被抓到,不需要人工登记。
图里几个组件的分工:Prometheus Server 拉取并存储时序数据,Alertmanager 负责告警去重和路由,Pushgateway 接收短任务推上来的指标(batch job 跑完就退出的场景),Exporters 是各类数据源的适配器。Kubernetes 集群里最常用的三个 Exporter 是 Node Exporter(节点硬件)、kube-state-metrics(K8s 对象状态)和 cAdvisor(容器运行时)。
2.2 Kubernetes 部署
在 Kubernetes 上跑 Prometheus,社区主流路径是 kube-prometheus-stack(基于 Prometheus Operator),把 Prometheus、Alertmanager、Grafana、Exporters 一起以 CRD 方式管理。下面是一个最小化的 Prometheus 自定义资源:
# Prometheus OperatorapiVersion: monitoring.coreos.com/v1kind: Prometheusmetadata: name: k8s-prometheus namespace: monitoringspec: replicas: 2 retention: 15d serviceAccountName: prometheus serviceMonitorSelector: matchLabels: team: monitoring resources: requests: cpu: 200m memory: 512Mi limits: cpu: 1000m memory: 2Gi storage: volumeClaimTemplate: spec: storageClassName: ssd resources: requests: storage: 50Gi三、核心指标
3.1 Node 指标
下面这些指标来自 node_exporter 和 cAdvisor,告警阈值只是常见起点,线上要按节点角色和负载特性微调。
| 实际指标名 | 含义 | 告警阈值参考 |
|---|---|---|
node_cpu_seconds_total | CPU 各 mode 累计秒数(含 idle) | 由 idle 反推使用率 > 80% |
node_memory_MemAvailable_bytes | 可用内存 | 使用率 > 85% |
node_filesystem_avail_bytes | 文件系统可用空间 | 使用率 > 90% |
node_network_receive_bytes_total | 网卡累计接收字节 | 速率突变 |
node_network_transmit_bytes_total | 网卡累计发送字节 | 速率突变 |
注意指标名后缀:node_exporter 用 _total 表示单调递增计数器,用 _bytes 表示当前值的 Gauge。counter 需要配 rate()/increase() 才有意义,gauge 可以直接读。下面几条 PromQL 正好展示了这两类指标的查询差异。
# CPU 使用率:1 减去所有 core 的 idle 占比100 - (sum(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100)
# 内存使用率:用 MemAvailable 反推100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# 磁盘使用率100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 1003.2 Pod 指标
Pod 层指标主要来自 kube-state-metrics(K8s 对象状态)和 cAdvisor(容器运行时),两者职责不同:kube-state-metrics 回答”对象应该是什么样”,cAdvisor 回答”容器实际用了多少”。
| 实际指标名 | 来源 | 用途 |
|---|---|---|
kube_pod_container_resource_requests | kube-state-metrics | 资源请求,调度依据 |
kube_pod_container_resource_limits | kube-state-metrics | 资源限制,限流依据 |
kube_pod_status_phase | kube-state-metrics | Pod 当前 phase |
container_cpu_usage_seconds_total | cAdvisor | CPU 累计使用(counter) |
container_memory_working_set_bytes | cAdvisor | 实际驻留内存(gauge,OOM 判定依据) |
kube_pod_container_status_restarts_total | kube-state-metrics | 重启计数(counter) |
# Pod CPU 使用率sum(rate(container_cpu_usage_seconds_total[5m])) by (pod, namespace)
# Pod 内存使用sum(container_memory_working_set_bytes) by (pod, namespace)
# Pod 重启次数increase(kube_pod_container_status_restarts_total[1h])3.3 K8s 组件指标
控制面组件的指标通过 --metrics-bind-address 或 /metrics 暴露,下面几条 histogram 查询用来定位延迟分布而非平均值,P99 才是 SLI 关心的那部分尾部。
# API Server 请求延迟(P99)histogram_quantile(0.99, rate(apiserver_request_duration_seconds_bucket[5m]))
# Scheduler 调度延迟(P95)histogram_quantile(0.95, rate(scheduler_e2e_scheduling_duration_seconds_bucket[5m]))
# etcd 磁盘写入延迟(WAL fsync P99)histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m]))本文版本基线为 Kubernetes 1.21/1.22。scheduler_e2e_scheduling_duration_seconds 在 1.23 之后被 scheduler_scheduling_attempt_duration_seconds 替代,升级集群后指标名会变,Dashboard 和告警规则要同步改。
四、Grafana Dashboard
4.1 常用 Dashboard
Grafana 的 Dashboard 通常以 JSON 形式导入,在 Kubernetes 里可以直接挂 ConfigMap,让 Dashboard 跟着集群走、随版本管理。
# Import dashboard via JSONapiVersion: v1kind: ConfigMapmetadata: name: grafana-dashboard-k8s namespace: monitoringdata: k8s-cluster.json: | { "dashboard": { "title": "Kubernetes Cluster", "uid": "k8s-cluster", "panels": [ { "title": "CPU 使用率", "type": "graph", "targets": [ { "expr": "sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace)", "legendFormat": "{{namespace}}" } ] }, { "title": "内存使用", "type": "graph", "targets": [ { "expr": "sum(container_memory_working_set_bytes) by (namespace)", "legendFormat": "{{namespace}}" } ] } ] } }4.2 关键 Panel 配置
# Kubernetes 集群总览apiVersion: v1kind: ConfigMapmetadata: name: cluster-overview namespace: monitoringdata: dashboard.json: | { "panels": [ { "title": "节点数", "type": "stat", "gridPos": {"h": 8, "w": 6}, "targets": [ {"expr": "count(kube_node_info)"} ] }, { "title": "Pod 总数", "type": "stat", "gridPos": {"h": 8, "w": 6}, "targets": [ {"expr": "sum(kube_pod_info)"} ] }, { "title": "CPU 分配率", "type": "gauge", "gridPos": {"h": 8, "w": 6}, "targets": [ {"expr": "sum(kube_pod_container_resource_requests_cpu_cores) / sum(kube_node_status_allocatable_cpu_cores) * 100"} ] }, { "title": "内存分配率", "type": "gauge", "gridPos": {"h": 8, "w": 6}, "targets": [ {"expr": "sum(kube_pod_container_resource_requests_memory_bytes) / sum(kube_node_status_allocatable_memory_bytes) * 100"} ] } ] }五、告警规则
5.1 告警规则定义
Prometheus Operator 用 PrometheusRule CRD 把告警规则做成 Kubernetes 对象,跟着集群一起版本管理。下面这份示例覆盖了组件存活、节点资源、Pod 重启三类高频告警。
apiVersion: monitoring.coreos.com/v1kind: PrometheusRulemetadata: name: k8s-alerts namespace: monitoringspec: groups: - name: kubernetes rules: # K8s 组件告警 - alert: K8sApiserverDown expr: up{job="kube-apiserver"} == 0 for: 5m labels: severity: critical annotations: summary: "API Server is down" description: "API Server has been down for more than 5 minutes"
- alert: K8sNodeNotReady expr: kube_node_status_condition{condition="Ready",status="true"} == 0 for: 10m labels: severity: warning annotations: summary: "Node {{ $labels.node }} is not ready" description: "Node {{ $labels.node }} has been not ready for 10 minutes"
# 资源告警 - alert: HighCPUUsage expr: sum(rate(container_cpu_usage_seconds_total[5m])) by (node) > 0.8 for: 5m labels: severity: warning annotations: summary: "High CPU usage on {{ $labels.node }}" description: "Node {{ $labels.node }} CPU usage is above 80%"
- alert: HighMemoryUsage expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.85 for: 5m labels: severity: warning annotations: summary: "High Memory usage on {{ $labels.node }}"
# Pod 告警 - alert: PodRestartingTooMuch expr: rate(kube_pod_container_status_restarts_total[1h]) > 0.05 for: 5m labels: severity: warning annotations: summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} restarting too much"5.2 Alertmanager 配置
Alertmanager 负责告警的去重、分组和路由,决定一条告警最终发到哪个接收器。Operator 用 AlertmanagerConfig 把收件人配置也做成 K8s 对象,下面示例把 critical 告警邮件发到 ops 组,同时推到 Slack 的 alerts 频道。
apiVersion: monitoring.coreos.com/v1kind: AlertmanagerConfigmetadata: name: team-config namespace: monitoringspec: receivers: - name: "default" emailConfigs: - to: "ops-team@example.com" headers: subject: "[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}" - name: "slack" slackConfigs: - channel: "#alerts" apiUrl: "https://hooks.slack.com/xxx" title: "[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}" text: | {{ range .Alerts }} *Alert:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} {{ end }}六、自定义指标
6.1 Prometheus Operator
内置的 Exporter 只覆盖集群层指标,应用自己的业务指标得自己暴露。Prometheus Operator 用 ServiceMonitor 声明”抓哪些 Service 后面的 Pod、走哪个端口、什么间隔”,避免在每个 Prometheus 实例上手写 scrape 配置。Operator 看到 ServiceMonitor 后自动转成 Prometheus 的 scrape job。
# ServiceMonitorapiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: my-app namespace: monitoring labels: team: monitoringspec: selector: matchLabels: app: my-app endpoints: - port: metrics interval: 30s path: /metrics namespaceSelector: matchNames: - production6.2 应用暴露指标
应用侧暴露指标的标准做法是在 HTTP 服务里挂一个 /metrics 端点,按 Prometheus 文本格式输出。下面用 Python 的 prometheus_client 演示三种最常见指标类型:Counter(只增不减的计数器)、Histogram(延迟分布)、Gauge(可增可减的当前值)。
# Python 应用暴露 Prometheus 指标from prometheus_client import Counter, Histogram, Gauge
# 定义指标REQUEST_COUNT = Counter( 'http_requests_total', 'Total HTTP requests', ['method', 'endpoint', 'status'])
REQUEST_LATENCY = Histogram( 'http_request_duration_seconds', 'HTTP request latency', ['method', 'endpoint'])
ACTIVE_CONNECTIONS = Gauge( 'http_active_connections', 'Active HTTP connections')
# 使用指标@app.route('/api/users')def get_users(): REQUEST_COUNT.labels(method='GET', endpoint='/api/users', status='200').inc() with REQUEST_LATENCY.labels(method='GET', endpoint='/api/users').time(): # 处理请求 pass return jsonify(users)七、监控与故障排查联动
7.1 从监控到排查
完善的监控体系是故障排查的基础。当告警触发时,快速定位问题的流程如下:
常见告警与排查方向:
| 告警类型 | 排查方向 | 相关工具 |
|---|---|---|
| HighCPUUsage | 应用性能、资源配额 | pprof、top |
| HighMemoryUsage | 内存泄漏、缓存策略 | jmap、pprof |
| PodRestarting | 应用崩溃、健康检查 | kubectl logs |
| K8sNodeNotReady | 节点资源、网络 | describe node |
7.2 日志与链路追踪
当指标异常时,需要结合日志和链路追踪进行深度分析:
# 查看相关 Pod 日志kubectl logs -n <namespace> <pod-name> --tail=100
# 查看链路追踪 (Jaeger)# 访问 Jaeger UI,根据 trace ID 查询完整调用链八、总结
把全文组件和它们的协作关系收束成一张图,可观测性体系的骨架就清楚了:指标、日志、链路三条采集线汇到统一展示层,告警挂在采集线上做主动通知,故障排查反过来从这里取数据。
| 组件 | 作用 | 关键配置 |
|---|---|---|
| Prometheus | 指标收集存储 | ServiceMonitor |
| Grafana | 可视化展示 | Dashboard |
| Alertmanager | 告警管理 | Route/Receiver |
| Loki | 日志聚合 | Promtail |
| Jaeger | 链路追踪 | Instrument |
监控方法论的选择不是越多越好,而是按观察对象匹配。节点这类资源型对象用 USE 方法(Utilization、Saturation、Errors)三件套覆盖;在线服务用 RED 方法(Rate、Errors、Duration)关注吞吐和延迟;SRE 体系的 Four Golden Signals(延迟、流量、错误、饱和度)则把前两者整合成一套通用语言。挑一套真正能驱动告警决策的方法落实,比三套都挂在墙上强。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






