Kubernetes 上手实战(9):日志监控与排障

发布时间:2026/8/25 7:09:35
Kubernetes 上手实战(9):日志监控与排障 上一篇把应用打成可追踪的 Helm release但“发布成功”只是运行起点。本篇建立从症状到证据的排障顺序对象条件与事件解释控制面日志解释单次请求指标解释趋势并用临时调试容器处理精简镜像。一、痛点信息很多证据链却容易断Kubernetes 故障常表现为 Pending、CrashLoopBackOff、ImagePullBackOff、NotReady、请求超时或延迟升高。状态名不是根因。Pending 可能来自资源、卷或亲和性CrashLoop 可能是进程退出、探针杀死或 OOM。有效方法是先缩小故障层再收集该层证据而不是随机执行一长串命令。建议顺序为确认 context 和时间范围看 Deployment、Pod 与 Service 概览读 conditions按时间查看 events看当前和 previous 容器日志检查资源指标最后才进入容器或节点。事件有保留期且可能聚合不是永久审计。日志也会随 Pod 删除或节点轮转消失生产必须集中采集。应用日志写 stdout/stderr由容器运行时和节点代理收集。每条日志最好包含时间、级别、服务、版本、请求 ID、错误类别与必要上下文避免令牌、Cookie 和个人数据。JSON 结构化日志更易查询但堆栈需要保持为一个逻辑事件采集器要正确处理多行。二、原理日志、指标和追踪回答不同问题指标是按时间聚合的数值适合告警和趋势日志是离散事件适合解释细节分布式追踪连接跨服务的一次请求。Prometheus 通过抓取指标端点保存时间序列Alertmanager 路由告警Grafana 展示并非数据源。OpenTelemetry 提供遥测生成与传输的通用组件但部署了 Collector 不代表自动获得高质量信号。监控先围绕用户结果设计请求率、错误率、延迟以及资源饱和度。CPU、内存、重启和 Pending 是解释信号。告警应表达需要行动的风险例如错误预算消耗过快而不是任何瞬时 CPU 超过 80%。每个告警附带仪表盘、查询、runbook 和责任团队。下面的独立清单故意创建一个启动后失败的 Pod以及一个正常 Deployment便于练习 current/previous logs、事件和端点检查。故障资源放在debug-labnamespace避免污染业务。apiVersion:v1kind:Namespacemetadata:name:debug-lab---apiVersion:v1kind:Podmetadata:name:crash-demonamespace:debug-lablabels:app:crash-demospec:containers:-name:appimage:busybox:1.36.1command:[sh,-c]args:-|echo {level:info,message:starting} sleep 2 echo {level:error,message:intentional failure} 2 exit 17---apiVersion:apps/v1kind:Deploymentmetadata:name:web-observenamespace:debug-labspec:replicas:2selector:matchLabels:app:web-observetemplate:metadata:labels:app:web-observespec:containers:-name:webimage:nginx:1.27.4-alpineresources:requests:cpu:20mmemory:32Milimits:memory:64MireadinessProbe:httpGet:path:/port:80periodSeconds:5三、实现用脚本保存一次排障快照下面脚本不会修改故障对象只应用实验清单并把诊断输出写到时间戳目录。previous logs 在容器至少重启一次后才存在因此脚本先等待如果尚无 previous 实例显式记录原因而非失败退出。#!/usr/bin/env bashset-euopipefail kubectl apply-fdebug.yamlsnapshot_dir./debug-snapshot-$(date-u%Y%m%dT%H%M%SZ)mkdir-p${snapshot_dir}sleep12kubectl cluster-info${snapshot_dir}/cluster-info.txtkubectl get pods-ndebug-lab-owide${snapshot_dir}/pods.txtkubectl get deployment-ndebug-lab-oyaml${snapshot_dir}/deployments.yamlkubectl describe pod crash-demo-ndebug-lab${snapshot_dir}/crash-describe.txtkubectl get events-ndebug-lab\--sort-by.metadata.creationTimestamp${snapshot_dir}/events.txtkubectl logs crash-demo-ndebug-lab\--timestamps${snapshot_dir}/crash-current.log21||truekubectl logs crash-demo-ndebug-lab\--previous--timestamps${snapshot_dir}/crash-previous.log21||truekubectltoppods-ndebug-lab${snapshot_dir}/top.txt21||truekubectl get pod crash-demo-ndebug-lab\-ojsonpath{.status.containerStatuses[0].lastState.terminated.exitCode}{\n}\${snapshot_dir}/exit-code.txtgrep-q^17$${snapshot_dir}/exit-code.txtgrep-qintentional failure${snapshot_dir}/crash-previous.logechodiagnostic snapshot saved to${snapshot_dir}预期 exit code 为 17previous log 包含 intentional failure。若是 137通常需结合lastState.terminated.reason和节点事件判断 OOM 或强制终止若是探针重启describe 中会有 Unhealthy 事件。退出码只是一条线索不能脱离上下文下结论。精简镜像可能没有 shell、curl 或 ps不应为排障把工具永久塞进生产镜像扩大攻击面。可用kubectl debug添加 ephemeral container共享 Pod 的部分命名空间进行诊断它仍受权限和审计约束。网络排障也可创建短命工具 Pod完成后删除。四、踩坑观测系统自身也会失控无界高基数标签会拖垮指标系统。不要把 user_id、request_id 或完整 URL 放入 Prometheus label它们适合日志或 trace。日志也要设置保留、采样和费用预算。采集失败应有自身监控避免“应用没日志”被误判为“没有错误”。只看平均延迟会掩盖尾部问题应看分位数或直方图并确认 bucket 与 SLO 匹配。直接对分位数做跨实例平均没有统计意义。重启计数上升要区分发布造成的正常重建与同一容器反复崩溃可关联 reason、时间窗口和 Deployment revision。排障现场避免立刻删除 Pod因为删除会丢失部分证据先保存 describe、日志、事件、对象 YAML 与相关指标时间窗。也不要把包含 Secret 环境、token 或客户数据的完整快照随意上传工单采集脚本必须有脱敏和访问控制。五、验证让 runbook 可以被另一人执行为常见故障建立小型演练错误镜像、缺少 ConfigMap、readiness 失败、内存超限、Service 无端点和 PVC Pending。每个演练记录症状、最短确认命令、根因证据、恢复动作与防复发措施。由未参与编写的人执行才能验证 runbook 是否真的清晰。生产观测的验收不是“装了 Prometheus”而是一次异常能从告警跳到相关 dashboard再按 namespace、release、Pod 和请求 ID 下钻并在数据保留期内完成复盘。时间同步、版本标签和统一关联 ID 是这条链路的基础。下一篇将把前九篇合并成生产交付清单镜像供应链、权限与网络隔离、可用性预算、渐进发布、备份、观测、成本和回滚共同形成上线门槛。一次完整复盘应重建时间线首个异常信号、告警触发、人工确认、缓解动作、服务恢复和根因修复。区分触发因素、根因与放大因素例如错误配置是触发缺少模式校验是根因告警延迟和无回滚脚本则扩大影响。行动项必须有负责人、截止时间和可验证完成条件不能只写“加强监控”或“提高警惕”。观测数据本身也要演练灾难日志后端不可用时应用是否被阻塞指标采集过载是否影响集群追踪采样是否仍保留错误请求。采集代理设置资源限制与缓冲上限故障时优先保护业务。控制面审计日志与应用日志分开保存并设置不同权限值班人员能看到诊断所需信息却不默认拥有读取所有密钥和客户数据的权限。排障的第一步是把状态归类为下一条最有信息量的检查而不是随机执行命令。下面程序把常见 Pod 状态映射到证据来源规则是显式数据可直接扩展为团队 runbook 的单元测试。fromdataclassesimportdataclassdataclass(frozenTrue)classSymptom:phase:strreason:strrestarts:intdefnext_check(item:Symptom)-str:ifitem.phasePending:returninspect scheduling events and PVC bindingifitem.reasonImagePullBackOff:returninspect image name and registry credentialsifitem.reasonCrashLoopBackOffanditem.restarts0:returninspect previous logs and last termination stateifitem.phaseRunninganditem.reasonNotReady:returninspect readiness probe and EndpointSlicereturninspect conditions, events, logs, and metrics in ordercases[Symptom(Pending,Unschedulable,0),Symptom(Running,CrashLoopBackOff,4),Symptom(Running,NotReady,0),]forcaseincases:print(f{case.reason}:{next_check(case)})运行输出Unschedulable: inspect scheduling events and PVC binding CrashLoopBackOff: inspect previous logs and last termination state NotReady: inspect readiness probe and EndpointSlice指标标签必须控制基数。第二个程序计算候选标签组合数并拒绝把 request ID、用户 ID 这类近乎唯一的字段放入时间序列可迁移的判断标准是“维度用于聚合标识符用于日志或追踪”。dimensions{service:6,namespace:4,status_class:5,method:6,}forbidden{request_id:2_000_000,user_id:80_000,}series_budget5_000defcombinations(items:dict[str,int])-int:total1forcardinalityinitems.values():total*cardinalityreturntotal safe_seriescombinations(dimensions)with_request_idsafe_series*forbidden[request_id]print(fsafe_series{safe_series})print(fwithin_budget{safe_seriesseries_budget})print(fwith_request_id{with_request_id})print(request_id_targetlogs_or_traces)运行输出safe_series720 within_budgetTrue with_request_id1440000000 request_id_targetlogs_or_traces参考来源Troubleshooting ApplicationsLogging ArchitectureDebug Running PodsPrometheus Documentation 觉得有用就点个赞 收藏方便回头查阅有疑问直接在评论区留言我看到都会回。 本文属于《Kubernetes 上手实战》系列持续更新关注不迷路。 文章里的代码都能直接跑。想要可直接 clone 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。