上一篇把应用打成可追踪的 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-lab namespace,避免污染业务。
apiVersion: v1
kind: Namespace
metadata:
name: debug–lab
—
apiVersion: v1
kind: Pod
metadata:
name: crash–demo
namespace: debug–lab
labels:
app: crash–demo
spec:
containers:
– name: app
image: busybox:1.36.1
command: ["sh", "-c"]
args:
– |
echo '{"level":"info","message":"starting"}'
sleep 2
echo '{"level":"error","message":"intentional failure"}' >&2
exit 17
—
apiVersion: apps/v1
kind: Deployment
metadata:
name: web–observe
namespace: debug–lab
spec:
replicas: 2
selector:
matchLabels:
app: web–observe
template:
metadata:
labels:
app: web–observe
spec:
containers:
– name: web
image: nginx:1.27.4–alpine
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
memory: 64Mi
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
三、实现:用脚本保存一次排障快照
下面脚本不会修改故障对象,只应用实验清单并把诊断输出写到时间戳目录。previous logs 在容器至少重启一次后才存在,因此脚本先等待;如果尚无 previous 实例,显式记录原因而非失败退出。
#!/usr/bin/env bash
set -euo pipefail
kubectl apply -f debug.yaml
snapshot_dir="./debug-snapshot-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "${snapshot_dir}"
sleep 12
kubectl cluster-info > "${snapshot_dir}/cluster-info.txt"
kubectl get pods -n debug-lab -o wide > "${snapshot_dir}/pods.txt"
kubectl get deployment -n debug-lab -o yaml > "${snapshot_dir}/deployments.yaml"
kubectl describe pod crash-demo -n debug-lab > "${snapshot_dir}/crash-describe.txt"
kubectl get events -n debug-lab \\
–sort-by=.metadata.creationTimestamp > "${snapshot_dir}/events.txt"
kubectl logs crash-demo -n debug-lab \\
–timestamps > "${snapshot_dir}/crash-current.log" 2>&1 || true
kubectl logs crash-demo -n debug-lab \\
–previous –timestamps > "${snapshot_dir}/crash-previous.log" 2>&1 || true
kubectl top pods -n debug-lab > "${snapshot_dir}/top.txt" 2>&1 || true
kubectl get pod crash-demo -n debug-lab \\
-o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}{"\\n"}' \\
> "${snapshot_dir}/exit-code.txt"
grep -q '^17$' "${snapshot_dir}/exit-code.txt"
grep -q 'intentional failure' "${snapshot_dir}/crash-previous.log"
echo "diagnostic snapshot saved to ${snapshot_dir}"
预期 exit code 为 17,previous 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 的单元测试。
from dataclasses import dataclass
@dataclass(frozen=True)
class Symptom:
phase: str
reason: str
restarts: int
def next_check(item: Symptom) –> str:
if item.phase == "Pending":
return "inspect scheduling events and PVC binding"
if item.reason == "ImagePullBackOff":
return "inspect image name and registry credentials"
if item.reason == "CrashLoopBackOff" and item.restarts > 0:
return "inspect previous logs and last termination state"
if item.phase == "Running" and item.reason == "NotReady":
return "inspect readiness probe and EndpointSlice"
return "inspect conditions, events, logs, and metrics in order"
cases = [
Symptom("Pending", "Unschedulable", 0),
Symptom("Running", "CrashLoopBackOff", 4),
Symptom("Running", "NotReady", 0),
]
for case in cases:
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_budget = 5_000
def combinations(items: dict[str, int]) –> int:
total = 1
for cardinality in items.values():
total *= cardinality
return total
safe_series = combinations(dimensions)
with_request_id = safe_series * forbidden["request_id"]
print(f"safe_series={safe_series}")
print(f"within_budget={safe_series <= series_budget}")
print(f"with_request_id={with_request_id}")
print("request_id_target=logs_or_traces")
运行输出:
safe_series=720
within_budget=True
with_request_id=1440000000
request_id_target=logs_or_traces
参考来源
- Troubleshooting Applications
- Logging Architecture
- Debug Running Pods
- Prometheus Documentation
👍 觉得有用就点个 赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。
🚀 本文属于 《Kubernetes 上手实战》 系列,持续更新,关注不迷路。
📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到 cj2664@qq.com,我免费发你。 如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

