mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1743 字
5 分钟
Kubernetes 监控与可观测性
2021-12-09

监控在 Kubernetes 里的位置比传统主机时代微妙得多。节点是动态的,Pod 是易失的,一个 Pod 被驱逐后重新调度到另一台节点,原来的指标上下文就丢了。靠 SSH 上去看日志这套流程在这里根本走不通。可观测性不是一个工具,而是把”系统现在怎么样、为什么会变成这样、接下来会怎样”这三类问题,拆给不同工具去回答的体系。本文按 Metrics、Logs、Traces 三条线展开 Prometheus + Grafana + Loki + Jaeger 的协作,以及告警规则怎么从指标映射回真实故障。

一、可观测性三大支柱#

1.1 指标、日志、链路追踪#

graph TB subgraph "可观测性三大支柱" A["Metrics 指标"] --> D["Prometheus"] B["Logs 日志"] --> E["Loki/ELK"] C["Traces 链路"] --> F["Jaeger/Tempo"] end subgraph "统一展示" D --> G["Grafana"] E --> G F --> G end
支柱工具用途
MetricsPrometheus时间序列指标,资源使用率
LogsLoki/ELK日志聚合分析
TracesJaeger请求链路追踪

二、Prometheus 架构#

2.1 组件架构#

Prometheus 的核心是 pull 模型:Server 主动去 Exporter 拉指标,不依赖应用推数据。这套机制在 Kubernetes 里尤其契合,因为 Service Discovery 能自动发现新 Pod 的 metrics 端点,节点一扩容,新节点上的 node_exporter 立刻被抓到,不需要人工登记。

graph TB subgraph "Prometheus 生态" A["Prometheus Server"] --> B["Alertmanager"] A --> C["Pushgateway"] A --> D["Exporters"] D --> E["Node Exporter"] D --> F["Kube-state-metrics"] D --> G["cAdvisor"] end subgraph "服务发现" H["Kubernetes SD"] --> A end

图里几个组件的分工: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 Operator
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: k8s-prometheus
namespace: monitoring
spec:
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_totalCPU 各 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) * 100

3.2 Pod 指标#

Pod 层指标主要来自 kube-state-metrics(K8s 对象状态)和 cAdvisor(容器运行时),两者职责不同:kube-state-metrics 回答”对象应该是什么样”,cAdvisor 回答”容器实际用了多少”。

实际指标名来源用途
kube_pod_container_resource_requestskube-state-metrics资源请求,调度依据
kube_pod_container_resource_limitskube-state-metrics资源限制,限流依据
kube_pod_status_phasekube-state-metricsPod 当前 phase
container_cpu_usage_seconds_totalcAdvisorCPU 累计使用(counter)
container_memory_working_set_bytescAdvisor实际驻留内存(gauge,OOM 判定依据)
kube_pod_container_status_restarts_totalkube-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]))
Note

本文版本基线为 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 JSON
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboard-k8s
namespace: monitoring
data:
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: v1
kind: ConfigMap
metadata:
name: cluster-overview
namespace: monitoring
data:
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/v1
kind: PrometheusRule
metadata:
name: k8s-alerts
namespace: monitoring
spec:
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/v1
kind: AlertmanagerConfig
metadata:
name: team-config
namespace: monitoring
spec:
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。

# ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
team: monitoring
spec:
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
interval: 30s
path: /metrics
namespaceSelector:
matchNames:
- production

6.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 从监控到排查#

完善的监控体系是故障排查的基础。当告警触发时,快速定位问题的流程如下:

1
告警触发
2
查看 Grafana
3
分析指标趋势
4
关联日志
5
定位根因
6
执行修复

常见告警与排查方向

告警类型排查方向相关工具
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 查询完整调用链

八、总结#

把全文组件和它们的协作关系收束成一张图,可观测性体系的骨架就清楚了:指标、日志、链路三条采集线汇到统一展示层,告警挂在采集线上做主动通知,故障排查反过来从这里取数据。

graph TB A["监控体系"] --> B["指标收集"] A --> C["日志收集"] A --> D["链路追踪"] B --> E["Prometheus"] C --> F["Loki/ELK"] D --> G["Jaeger"] E --> H["Alertmanager"] H --> I["告警通知"] E --> J["Grafana"] F --> J G --> J A --> K["故障排查"] K --> L["根因分析"] L --> M["快速恢复"]
组件作用关键配置
Prometheus指标收集存储ServiceMonitor
Grafana可视化展示Dashboard
Alertmanager告警管理Route/Receiver
Loki日志聚合Promtail
Jaeger链路追踪Instrument

监控方法论的选择不是越多越好,而是按观察对象匹配。节点这类资源型对象用 USE 方法(Utilization、Saturation、Errors)三件套覆盖;在线服务用 RED 方法(Rate、Errors、Duration)关注吞吐和延迟;SRE 体系的 Four Golden Signals(延迟、流量、错误、饱和度)则把前两者整合成一套通用语言。挑一套真正能驱动告警决策的方法落实,比三套都挂在墙上强。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Kubernetes 监控与可观测性
https://blog.souloss.cn/posts/kubernetes/k8s-observability/
作者
Souloss
发布于
2021-12-09
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时