Appearance
日志监控
Kubernetes 的可观测性分三块:日志、指标和链路。日志来自容器 stdout/stderr 和节点文件,指标来自 node-exporter、kubelet/cAdvisor、kube-state-metrics 和应用 /metrics,链路来自应用内 instrumentation。这一篇重点在日志和指标的基础采集和 Prometheus 体系。
kubectl logs 和 kubectl top 适合现场快速排查,但不适合长期留存、聚合和历史趋势分析。长期方案通常是 DaemonSet 日志采集 + Prometheus 指标采集 + Grafana 可视化。
日志采集链路
容器输出到 stdout/stderr 的日志会被 containerd 写入节点的 /var/log/pods 和 /var/log/containers 目录。DaemonSet 形式的日志 Agent 挂载这些目录,读取文件后发送到后端。
应用往容器内文件写日志也是一种方式,但需要额外处理:挂载 volume 让文件持久化、配置日志轮转避免写爆容器层,并让采集 Agent 也能访问到这个文件路径。把主要日志输出到 stdout/stderr 是 K8s 生态里采集阻力最小的做法。
DaemonSet 采集
采集 Agent 以 DaemonSet 形式跑在每个节点上,挂载节点日志目录:
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
namespace: logging
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: agent
image: fluent/fluent-bit:3.2
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true # 日志采集只需要读权限
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers常见采集工具:
| 工具 | 定位 |
|---|---|
| Fluent Bit | 轻量 C 编写,K8s 生态使用广泛,适合作为节点 Agent |
| Vector | Rust 编写,转换和路由能力强 |
| Filebeat | Elastic Stack 体系专用 |
| Promtail | Loki 体系专用,和 Grafana 联动自然 |
EFK 和 Loki 两种后端
| 方案 | 索引方式 | 优势 | 代价 |
|---|---|---|---|
| EFK (Elasticsearch + Fluentd + Kibana) | 全文索引 | 全文检索能力极强,适合审计和复杂查询 | 资源占用高,运维量大 |
| Loki + Promtail + Grafana | 标签索引 | 低成本、和 Prometheus/Grafana 一套体系 | 不适合全文搜索,更偏向标签查询 |
中小规模集群从 Loki 起步运维压力更小。当有大量全文检索、审计合规检索、复杂通配符查询的需求时,再考虑 Elasticsearch。两者不是互斥的,也可以把核心业务日志送 Loki(轻量),审计和安全日志送 Elasticsearch(全文检索)。
日志里至少保留这些标签字段用于查询和聚合:
| 字段 | 用途 |
|---|---|
| namespace | 按环境或业务线过滤 |
| pod | 定位具体实例 |
| container | 区分业务容器和 sidecar |
| node | 隔离节点级问题 |
| app/version | 和 Deployment label 对应,方便对比发布前后日志 |
Prometheus 指标体系
Prometheus 通过 K8s API 做服务发现,自动找到要抓取的 Pod 和 Service,然后定期从 /metrics 端点拉取指标数据。
常见指标来源:
| 组件 | 提供什么 | 安装方式 |
|---|---|---|
| node-exporter | 节点 CPU、内存、磁盘、网络、文件系统 | DaemonSet |
| kubelet/cAdvisor | 容器级别的资源使用 | kubelet 内嵌 |
| kube-state-metrics | K8s 资源对象状态:Pod 阶段、Deployment 副本数、Node Condition | Deployment |
| 应用 /metrics | 业务 QPS、延迟、错误率、队列长度 | 应用代码内嵌 |
| Ingress/Gateway Controller | 入口 QPS、状态码、P50/P95/P99 | Controller 自带 |
| etcd | leader、fsync 延迟、DB 大小 | etcd 自带 |
Prometheus Operator 的 CRD
Prometheus Operator 把 Prometheus 生态的配置变成了几种 CRD:
| CRD | 作用 |
|---|---|
| ServiceMonitor | 通过 Service label 发现抓取目标 |
| PodMonitor | 直接通过 Pod label 发现抓取目标 |
| PrometheusRule | 告警规则和 recording rule |
| AlertmanagerConfig | 告警路由(微信、飞书、邮件、PagerDuty) |
ServiceMonitor 匹配的是 Service 的 label,不是 Pod 的 label——写错了就到不目标:
yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: demo-app
namespace: monitoring
labels:
release: kube-prometheus-stack # 这个 label 需要和 Prometheus 的 serviceMonitorSelector 匹配
spec:
namespaceSelector:
matchNames:
- demo
selector:
matchLabels:
app: demo-app # 匹配 demo namespace 下带 app=demo-app 标签的 Service
endpoints:
- port: metrics
path: /metrics
interval: 30sServiceMonitor 本身的 release label 必须和 Prometheus CR 里的 serviceMonitorSelector 对上。新建 ServiceMonitor 没被抓取时,先查这两边 label 是否匹配。
告警
yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: pod-alerts
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
groups:
- name: pod.rules
rules:
- alert: PodRestartingTooOften
expr: increase(kube_pod_container_status_restarts_total[15m]) > 3
for: 5m # 持续 5 分钟才触发,过滤短时抖动
labels:
severity: warning
annotations:
summary: "Pod 重启频繁"
description: "namespace={{ $labels.namespace }} pod={{ $labels.pod }} 在 15 分钟内多次重启"告警的 annotations 里带上 namespace、pod、container、node 这些标签。收到告警时无需再手动 kubectl get pods 定位——告警信息本身应该指向具体资源。
看板
K8s 相关的 Grafana Dashboard 通常按不同关注点分层:
| 看板 | 核心指标 |
|---|---|
| 集群总览 | 节点数、Pod 数、资源使用率 |
| Node 详情 | CPU、内存、磁盘、网络、inode、conntrack |
| Namespace 维度 | requests/limits/实际使用的对比 |
| Pod 级别 | 重启次数、OOM 次数、CPU/内存趋势 |
| API Server | 请求量、延迟分位数、错误率 |
| etcd | leader 稳定性、fsync/commit 延迟、DB 大小 |
| 入口网关 | QPS、状态码分布、P95/P99 延迟 |
Dashboard 要有下钻能力。只看总览的聚合数字,排查时还是要回到 PromQL。
标签基数
指标 label 的值集合太大时——比如把用户 ID、订单号、请求 trace ID 放进 metric label——Prometheus 会在内存里为每个 label 组合维护独立的 time series。高基数 label 是 Prometheus 的内存杀手。
日志里放这些高基数字段没问题,Loki/Elasticsearch 按需索引。指标 label 适合放 namespace、app、version、pod、node 这类有限集合的维度。
发布后排查超时问题时,日志和指标按同样的 label 组合(namespace + app + version + pod)联动,从 Grafana 面板点一下告警直接跳到对应时间段和 Pod 的日志——比在两个系统里分别搜索快很多。