k8s 可观测性

导读
Kubernetes 集群中工作负载动态调度、频繁更迭,传统的登录节点排障方式已无法跟上节奏。可观测性通过日志、指标、链路三个维度,将集群内部状态转化为可查询、可告警的数据。本文围绕三支柱模型展开,覆盖日志采集、Prometheus 监控栈、Event 查询、链路追踪与告警体系的实战配置。
核心概念
可观测性的三支柱各有分工。日志(Logs)记录离散事件,以文本形式承载上下文细节,适合排查具体错误。指标(Metrics)是按时间序列采样的数值数据,低存储成本、高聚合能力,适合趋势分析与阈值告警。链路(Tracing)追踪单个请求在多个微服务间的调用路径,定位跨服务延迟瓶颈。三者互补:指标告诉你出了问题,链路告诉你问题在哪,日志告诉你为什么。
分布式链路追踪在微服务架构中尤为关键。一个外部请求可能依次经过网关、认证服务、业务逻辑服务和数据库,任一环节的延迟都会拖慢整体响应。Jaeger 和 OpenTelemetry 是当前主流方案,通过在 HTTP 头中注入 trace context(trace ID 和 span ID)实现跨服务传播。每个服务创建 span 并上报到 collector,最终拼装成完整的调用树。
Kubernetes 场景下,Prometheus 是事实标准的指标系统,采用 pull 模式主动抓取目标端点。它通过服务发现机制找到 Pod、Node、Service 等目标,定时调用 /metrics 接口拉取数据,存入内置的时序数据库(TSDB)。配套组件各司其职:Grafana 负责可视化,Alertmanager 负责告警路由与抑制,kube-state-metrics 暴露 Deployment、Pod 等资源对象的状态指标,cAdvisor 提供容器维度的 CPU、内存、网络数据。
图 1 展示了 Prometheus 监控栈的整体拓扑。kubelet 内嵌 cAdvisor,两者共同暴露节点与容器维度的指标端点。Prometheus 从 API Server 获取服务发现信息后,按配置间隔主动拉取这些端点的数据,写入本地 TSDB。Grafana 和 Alertmanager 作为消费者,分别从 TSDB 读取数据用于展示和告警判断。kube-state-metrics 独立部署,补充资源对象层面的状态信息,弥补 cAdvisor 只关注运行时指标的不足。
图解原理
指标采集链路的起点是 cAdvisor。它在每个节点上以常驻进程运行,通过 cgroups 和 Linux 内核接口收集容器的资源使用数据。kubelet 将 cAdvisor 的数据与自身汇总的节点指标合并,统一暴露在 https://<node-ip>:10250/metrics 端点上。Prometheus 按抓取配置中定义的间隔(默认 15 秒)向该端点发起 HTTP GET 请求,获取 plain text 格式的指标数据。
抓取到的原始数据经过解析后写入 TSDB。Prometheus 支持通过 recording rules 预计算高频查询,将复杂 PromQL 的结果提前固化,降低查询时的计算压力。告警规则同样基于 PromQL 表达,当表达式持续满足指定时长(for 子句)后,Prometheus 将告警推送到 Alertmanager,由后者负责去重、分组和路由分发。
图 2 刻画了从数据产生到可视化展示的完整链路。cAdvisor 持续向 kubelet 上报容器指标,kubelet 聚合后通过 /metrics 端点对外提供。Prometheus 以轮询方式拉取数据,写入 TSDB 后供 Grafana 查询。整个链路中数据始终单向流动,Prometheus 不会向目标推送任何指令,这种 pull 架构降低了采集端的复杂度,也便于通过防火墙策略控制访问。
动手实操
查看容器日志
kubectl logs 是最直接的日志查看方式。指定 Pod 名称即可获取标准输出内容,追加 -f 参数可实时跟踪。多容器 Pod 需要用 -c 指定目标容器,否则命令会报错提示选择。--previous 参数在容器崩溃重启后查看上一次的日志,是排查 CrashLoopBackOff 的关键手段。
# 查看 Pod 日志
kubectl logs nginx-deploy-7c5b9b6f6d-x9k2m
# 跟踪实时日志
kubectl logs -f nginx-deploy-7c5b9b6f6d-x9k2m
# 查看多容器 Pod 中指定容器
kubectl logs nginx-deploy-7c5b9b6f6d-x9k2m -c sidecar
# 查看最近 1 小时的日志
kubectl logs --since=1h nginx-deploy-7c5b9b6f6d-x9k2m
# 查看前一个容器的日志(崩溃后排查)
kubectl logs --previous nginx-deploy-7c5b9b6f6d-x9k2m容器日志存储在节点 /var/log/containers/ 目录下,以软链接指向 /var/log/pods/ 中的实际文件。节点重启或日志轮转后,历史日志可能丢失。生产环境需要部署节点级采集方案,将日志持久化到外部存储。
部署 Fluent Bit 采集节点日志
Fluent Bit 以 DaemonSet 方式运行在每个节点,挂载日志目录并转发到后端存储。相比 Fluentd,Fluent Bit 用 C 语言编写,内存占用更低,适合资源受限的节点。以下配置将容器日志采集后转发到 Elasticsearch:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: kube-system
labels:
k8s-app: fluent-bit
spec:
selector:
matchLabels:
k8s-app: fluent-bit
template:
metadata:
labels:
k8s-app: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.2.0
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc/
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config
configMap:
name: fluent-bit-configConfigMap 定义采集规则和输出目标。tail 插件监控 /var/log/containers/*.log 路径下的所有日志文件,Parser 指定解析格式。Mem_Buf_Limit 控制内存缓冲上限以防 OOM,Skip_Long_Lines 跳过超长行避免解析器阻塞。以下是完整的配置示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: kube-system
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Log_Level info
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Mem_Buf_Limit 10MB
Skip_Long_Lines On
[OUTPUT]
Name es
Match *
Host elasticsearch.logging.svc.cluster.local
Port 9200
Index k8s-logs
Type _doc部署 Prometheus 监控栈
使用 kube-prometheus-stack Helm chart 一步部署 Prometheus、Grafana、Alertmanager 及默认的告警规则。该 chart 内置了 node-exporter、kube-state-metrics 和 ServiceMonitor 配置,开箱即用。部署前确认集群已安装 Helm 3 并添加 prometheus-community 仓库:
# 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 安装到 monitoring 命名空间
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace
# 查看 Pod 状态
kubectl get pods -n monitoring安装完成后,将 Grafana Service 改为 NodePort 以便从集群外部访问。Prometheus 和 Alertmanager 同理,按需暴露端口。默认账号为 admin,密码可通过 Secret 获取:
kubectl patch svc kube-prometheus-stack-grafana -n monitoring \
-p '{"spec":{"type":"NodePort"}}'
# 获取初始密码
kubectl get secret -n monitoring kube-prometheus-stack-grafana \
-o jsonpath="{.data.admin-password}" | base64 -d ; echoGrafana 内置了 kube-prometheus-stack 预配置的数据源和仪表盘。登录后可直接查看节点资源使用率、Pod 状态、API Server 延迟等关键面板。数据源指向集群内的 Prometheus 实例,无需手动配置即可开始查询。
配置告警规则
Prometheus 告警规则定义在 PrometheusRule CRD 中,kube-prometheus-stack 会自动识别并加载。规则包含 expr(PromQL 表达式)、for(持续时间)和 annotations(描述信息)三部分。以下示例覆盖节点高 CPU 和容器频繁重启两个场景:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: node-alerts
namespace: monitoring
labels:
prometheus: k8s
spec:
groups:
- name: node.rules
rules:
- alert: NodeHighCPUUsage
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "节点 CPU 使用率过高"
description: "节点 {{ $labels.instance }} CPU 使用率超过 80%,持续 5 分钟。"
- alert: PodCrashLooping
expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
for: 1m
labels:
severity: critical
annotations:
summary: "容器频繁重启"
description: "Pod {{ $labels.pod }} 在 1 小时内重启超过 5 次。"Alertmanager 负责告警的去重、分组和路由。通过 Secret 配置通知接收者。group_by 按标签聚合关联告警,group_wait 控制首次发送前的等待时间,repeat_interval 防止未恢复告警重复通知:
apiVersion: v1
kind: Secret
metadata:
name: alertmanager-config
namespace: monitoring
type: Opaque
stringData:
alertmanager.yaml: |
global:
resolve_timeout: 5m
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'default'
receivers:
- name: 'default'
webhook_configs:
- url: 'https://hooks.slack.com/services/xxx'查询 Event
Event 是 Kubernetes 内置的事件机制,记录资源生命周期中的状态变更。Pod 调度失败、镜像拉取错误、探针检测失败等都会生成 Event,默认保留 1 小时。kubectl describe 输出尾部的 Events 段是排查 Pod 异常的第一入口:
# 查看某 Pod 的所有事件
kubectl get events --field-selector involvedObject.name=<pod-name>
# 按类型筛选 Warning 事件
kubectl get events --field-selector type=Warning
# 按时间排序查看命名空间事件
kubectl get events -n default --sort-by=.metadata.creationTimestamp
# 以 YAML 格式查看事件详情
kubectl get event <event-name> -o yamlEvent 中包含 reason(事件原因)、message(描述信息)、source(事件来源组件)等字段。结合 kubectl describe pod <name> 可以快速定位调度失败和探针异常的根因。生产环境中建议将 Event 导出持久化,弥补默认 1 小时 TTL 的局限。
分布式链路追踪概览
在 Kubernetes 微服务场景中,单个请求可能跨越多个 Pod 和 Service。OpenTelemetry 提供统一的 SDK 和 instrumentation 标准,将 trace context 通过 W3C Trace Context 规范在 HTTP 头中传播。部署 OpenTelemetry Collector 作为 DaemonSet 或 Deployment,接收各服务上报的 span 数据并导出到 Jaeger 等后端:
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
namespace: tracing
spec:
selector:
matchLabels:
app: otel-collector
template:
metadata:
labels:
app: otel-collector
spec:
containers:
- name: otel-collector
image: otel/opentelemetry-collector-contrib:0.90.0
ports:
- containerPort: 4317
name: otlp-grpc
- containerPort: 4318
name: otlp-http
- containerPort: 16686
name: jaeger-ui应用侧需引入 OpenTelemetry SDK,在发起 HTTP 请求时自动注入 trace context,接收请求时提取并续接。Jaeger UI 以火焰图形式展示调用链,每个 span 记录服务名、操作名、耗时和标签,帮助快速定位慢调用节点。追踪数据与指标、日志关联后,可以从告警直接跳转到对应时间段的调用链和日志,实现三支柱联动。
常见问题与避坑
指标端点抓取超时。cAdvisor 在容器数量过多的节点上响应变慢,Prometheus 默认 10 秒超时可能不够。在 ServiceMonitor 中调大 scrapeTimeout,或为节点添加资源限制防止单节点过载。抓取间隔不宜低于 10 秒,过高的频率会给 API Server 和 kubelet 带来压力。
日志采集丢数据。Fluent Bit 的 tail 插件默认从文件末尾开始读取,重启后已读取的偏移量记录在 position 文件中。若 position 文件丢失,会重复采集或遗漏数据。切勿将 position.db 放在 emptyDir 上,Pod 重启后偏移量清零会导致数据丢失。应将 position 文件挂载到 hostPath 或持久化卷。
告警风暴。一条 Service 故障可能触发数十条关联告警,淹没关键信息。在 Alertmanager 中配置 group_by 按标签分组,设置 group_wait 延迟发送,利用 inhibit_rules 抑制下级告警。例如节点 Down 时应抑制该节点上所有 Pod 的告警,避免重复通知。
Event 过期丢失。Event 默认 TTL 为 1 小时,事后排查时往往已不存在。部署 kube-event-exporter 将 Event 导出到 Elasticsearch 或对象存储,延长留存周期以支持历史回溯。
小结与进阶
可观测性的核心在于用日志、指标、链路三维度数据还原系统真实状态,Prometheus 监控栈提供了从采集到告警的完整工具链。掌握三支柱的采集机制和告警配置,是构建可靠运维体系的基础。下一篇将深入 Helm 包管理,学习如何用模板化方式管理复杂应用的部署与升级。