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 做关联键。
六、组合采集的实操顺序与判断依据
面对一个“服务 5xx 突增”的复杂故障,可按以下顺序组合:
- 用 Prometheus 查
sum(rate(haproxy_backend_http_responses_total{code="5xx"}[1m])) by (route)定位受影响 Route。 - 用
oc get route与oc get endpoints确认后端 Pod 是否 Ready,排除 Endpoint 为空。 - 用 Loki 查该 Route 对应 namespace 的日志,过滤
|= "error"或| json | status >= 500。 - 若日志显示连接超时,到节点执行
tcpdump -i any -nn host <backend-ip> and port 8080 -c 200 -w /tmp/backend.pcap,观察是否有 SYN 无 ACK。 - 若怀疑 DNS,用
oc exec进入 Pod 执行nslookup或dig,并检查 CoreDNS 日志。 - 若怀疑应用阻塞,用
strace -f -p <pid> -e trace=network -T -tt观察 syscall 耗时。 - 最后用
oc adm must-gather固化现场,并用 OpenTelemetry Trace 确认跨服务调用链中的瓶颈节点。
判断依据要具体:Endpoint 为空说明 Service 选择器或 Readiness 探针有问题;tcpdump 只有 SYN 说明网络策略或路由问题;strace 中 connect() 长时间阻塞说明后端不可达或队列满;Prometheus 中 etcd_server_leader_changes_seen_total 持续增长说明 etcd 不稳定,会连带 API Server 超时。
七、可直接使用的采集清单
| 层次 | 工具 | 关键命令/查询 | 判断依据 |
|---|---|---|---|
| 声明层 | oc | oc get pod -o wide、oc describe pod | Ready、Restart、Events、Conditions |
| 控制层 | oc / Operator 日志 | oc get events --sort-by=.lastTimestamp | 调度失败、镜像拉取失败、探针失败 |
| 执行层 | journalctl | journalctl -u kubelet --since "10 min ago" | PLEG、OOM、CNI 报错 |
| 执行层 | crictl | crictl ps -a、crictl logs | 容器退出码、CRI 状态 |
| 执行层 | etcdctl | etcdctl endpoint status --write-out=table | DB SIZE、Raft Index、Leader 变化 |
| 执行层 | tcpdump | tcpdump -i any -nn port 6443 -c 100 -w /tmp/api.pcap | SYN/ACK、重传、RST |
| 执行层 | strace | strace -f -p <pid> -e trace=network -T -tt | connect 阻塞、EAGAIN、超时 |
| 观测层 | Prometheus | rate(container_cpu_usage_seconds_total[5m]) | CPU/内存/重启趋势 |
| 观测层 | Loki | {namespace="app-prod"} |= "OOMKilled" | 错误日志时间分布 |
| 观测层 | OpenTelemetry | Trace ID 关联 | 跨服务调用链瓶颈 |
把这张清单与 must-gather 快照、Prometheus 时间轴、Loki 日志窗口放在一起,复杂故障就从“感觉哪里不对”变成“有证据链可复现、可验证、可复盘”的工程问题。