3. 诊断工具链与数据采集

复杂故障的难点往往不在于“没有工具”,而在于工具太多、数据太散:集群级对象状态在 etcd 与 API Server 里,节点级进程与内核行为在 journalctl、crictl、strace 里,网络路径在 tcpdump 与 Service/Endpoint 里,历史趋势与日志聚合在 Prometheus、Grafana、Loki 里,跨组件调用链在 OpenTelemetry 里。真正有效的诊断,是把这些工具按“证据层次”组合成一条可复现的采集链,而不是随机敲命令。

一、先建立证据分层模型

建议把采集对象分为四层,每层回答不同问题:

  • 声明层(期望状态):Deployment、StatefulSet、Route、PVC、Operator CR 等对象期望副本数、镜像、亲和性、资源限制。用 oc get -o yaml、oc describe 获取。
  • 控制层(协调状态):kube-controller-manager、scheduler、Operator 的 reconcile 结果,表现为 Conditions、Events、Leader 选举。用 oc get events --sort-by=.lastTimestamp、oc logs -n openshift-* 获取。
  • 执行层(实际状态):节点上的容器运行时、kubelet、CRI-O、systemd 单元、内核网络栈。用 crictl、journalctl、strace、tcpdump 获取。
  • 观测层(历史与关联):指标、日志、Trace。用 Prometheus 查询、Grafana 面板、Loki LogQL、OpenTelemetry Collector 导出数据获取。

分层之后,采集顺序就有了依据:先声明层确认“应该是什么”,再控制层看“协调到哪一步”,然后执行层看“节点上实际发生了什么”,最后观测层做时间轴对齐与趋势验证。这个顺序能避免一上来就抓包或 strace,导致大量噪声掩盖真正矛盾点。

二、组合使用的核心:时间对齐与对象锚定

组合工具链的关键动作是时间对齐和对象锚定。时间对齐要求所有采集都记录 UTC 时间戳,并在同一故障窗口内比对;对象锚定要求每条证据都能落到具体 Pod UID、Node 名、Container ID 或 Service IP 上。缺少这两点,Prometheus 的曲线、Loki 的日志和 tcpdump 的包就无法拼成因果链。

例如一个典型组合:某 Pod 反复重启。先用 oc get pod -o wide 拿到 Node 与 Pod UID;用 oc describe pod 看 Last State 与 Exit Code;用 oc logs --previous 看上一轮容器日志;若日志无输出,则到节点上执行 crictl ps -a 找到容器 ID,再用 crictl logs 与 journalctl -u kubelet 交叉验证;若怀疑 OOM 或 syscall 阻塞,用 strace -p 或 journalctl -k 查内核 OOM Killer 记录;最后用 Prometheus 的 container_memory_working_set_bytes 与 Loki 中该 Pod 的日志做时间轴对齐,确认是内存爬升后被杀,还是启动即失败。

三、must-gather:一次性固化现场

oc adm must-gather 是复杂故障的“现场快照”。它默认采集集群级资源、Operator 状态、节点日志、etcd 信息等。实际使用中建议按需指定镜像与采集范围:

oc adm must-gather \
  --dest-dir=/data/diag-$(date +%Y%m%d-%H%M) \
  --image-stream=openshift/must-gather \
  -- /usr/bin/gather_audit_logs

采集完成后,重点看 cluster-scoped-resources、namespaces、nodes、pods 目录下的 yaml 与日志。must-gather 的价值在于“可离线复盘”,但它的采集窗口是执行时刻,无法回放故障发生瞬间,因此必须与 Prometheus/Loki 的历史数据配合。

四、节点级工具的分工

journalctl 负责 systemd 与内核日志,常用过滤:journalctl -u kubelet --since "10 min ago"、journalctl -k -p err。crictl 负责 CRI 层,常用:crictl pods、crictl ps -a、crictl inspect、crictl logs。etcdctl 用于 etcd 健康与性能:etcdctl endpoint health、etcdctl endpoint status --write-out=table,关注 DB SIZE、RAFT TERM、RAFT INDEX 是否异常增长。tcpdump 用于抓取节点或 Pod 网络接口的包,建议先 tcpdump -i any -nn port 6443 -c 100 -w /tmp/api.pcap 限定范围。strace 用于追踪进程 syscall,例如 strace -f -p $(pgrep -f kubelet) -e trace=network,openat -T -tt,但生产环境需谨慎,避免高开销。

五、观测层:Prometheus、Grafana、Loki、OpenTelemetry

Prometheus 负责指标,常用查询如 rate(container_cpu_usage_seconds_total[5m])、kube_pod_container_status_restarts_total、etcd_server_leader_changes_seen_total。Grafana 用于把多个查询叠加到同一时间轴,便于发现“指标先变、日志后报错”的先后关系。Loki 用 LogQL 做日志聚合,例如 {namespace="app-prod"} |= "OOMKilled"。OpenTelemetry 则把应用 Trace 与集群事件关联,适合定位跨服务调用超时。

组合使用时,建议先固定一个“故障窗口”,例如 14:03:00–14:08:00 UTC,然后分别从 Prometheus、Loki、must-gather、节点日志中截取该窗口数据,最后用 OpenTelemetry Trace ID 或 Pod UID 做关联键。

复杂故障诊断工具链组合:证据分层与采集流向 声明层 · 期望状态 oc get -o yaml oc describe Deployment / PVC / CR oc get events Conditions 控制层 · 协调状态 kube-controller-manager scheduler Operator reconcile Leader 选举 Events 执行层 · 节点实际状态 journalctl kubelet / kernel crictl CRI-O / 容器 etcdctl 健康 / Raft tcpdump 网络包 strace syscall must-gather 现场快照 观测层 · 历史与关联 Prometheus 指标 / 告警 Grafana 时间轴叠加 Loki LogQL 聚合 OpenTelemetry Trace / 调用链 时间对齐 UTC 窗口 关联键:Pod UID / Node / Container ID / Trace ID

六、组合采集的实操顺序与判断依据

面对一个“服务 5xx 突增”的复杂故障,可按以下顺序组合:

  1. 用 Prometheus 查 sum(rate(haproxy_backend_http_responses_total{code="5xx"}[1m])) by (route) 定位受影响 Route。
  2. 用 oc get route 与 oc get endpoints 确认后端 Pod 是否 Ready,排除 Endpoint 为空。
  3. 用 Loki 查该 Route 对应 namespace 的日志,过滤 |= "error" 或 | json | status >= 500。
  4. 若日志显示连接超时,到节点执行 tcpdump -i any -nn host <backend-ip> and port 8080 -c 200 -w /tmp/backend.pcap,观察是否有 SYN 无 ACK。
  5. 若怀疑 DNS,用 oc exec 进入 Pod 执行 nslookup 或 dig,并检查 CoreDNS 日志。
  6. 若怀疑应用阻塞,用 strace -f -p <pid> -e trace=network -T -tt 观察 syscall 耗时。
  7. 最后用 oc adm must-gather 固化现场,并用 OpenTelemetry Trace 确认跨服务调用链中的瓶颈节点。

判断依据要具体:Endpoint 为空说明 Service 选择器或 Readiness 探针有问题;tcpdump 只有 SYN 说明网络策略或路由问题;strace 中 connect() 长时间阻塞说明后端不可达或队列满;Prometheus 中 etcd_server_leader_changes_seen_total 持续增长说明 etcd 不稳定,会连带 API Server 超时。

采集前先记录 UTC 时间窗口与故障对象 UID,所有命令输出重定向到独立目录,避免覆盖。生产环境慎用 strace 与全量 tcpdump,优先限定 PID、接口、端口和包数。

七、可直接使用的采集清单

层次工具关键命令/查询判断依据
声明层ococ get pod -o wide、oc describe podReady、Restart、Events、Conditions
控制层oc / Operator 日志oc get events --sort-by=.lastTimestamp调度失败、镜像拉取失败、探针失败
执行层journalctljournalctl -u kubelet --since "10 min ago"PLEG、OOM、CNI 报错
执行层crictlcrictl ps -a、crictl logs容器退出码、CRI 状态
执行层etcdctletcdctl endpoint status --write-out=tableDB SIZE、Raft Index、Leader 变化
执行层tcpdumptcpdump -i any -nn port 6443 -c 100 -w /tmp/api.pcapSYN/ACK、重传、RST
执行层stracestrace -f -p <pid> -e trace=network -T -ttconnect 阻塞、EAGAIN、超时
观测层Prometheusrate(container_cpu_usage_seconds_total[5m])CPU/内存/重启趋势
观测层Loki{namespace="app-prod"} |= "OOMKilled"错误日志时间分布
观测层OpenTelemetryTrace ID 关联跨服务调用链瓶颈

把这张清单与 must-gather 快照、Prometheus 时间轴、Loki 日志窗口放在一起,复杂故障就从“感觉哪里不对”变成“有证据链可复现、可验证、可复盘”的工程问题。