Kubernetes生产运维02:只会get pods还不够,kubectl排障工具箱怎么组合使用
Kubernetes生产运维02:只会get pods还不够,kubectl排障工具箱怎么组合使用

写在前面
很多Kubernetes排障清单都是这样开始的:
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>
命令本身没有错,但如果不知道每条命令要回答什么问题,排障很容易变成机械巡检:输出越来越多,故障范围却没有缩小。
kubectl get适合看范围和差异,describe适合看单个对象的聚合状态,logs回答容器进程说了什么,events记录控制面和组件观察到了什么,top提供近期资源用量,debug则用于现有容器缺少工具或需要进入Node现场的情况。
真正需要掌握的不是命令数量,而是下面这条链路:
当前问题
→ 选择工具
→ 提取关键字段
→ 解释字段语义
→ 形成下一项假设
→ 决定继续只读取证还是进入主动调试
本文命令按照Kubernetes官方文档和命令参考进行语法核对。由于当前没有连接可验证的实验集群,文中的终端输出均为说明性示例,不是生产原始记录,也不声称已经在统一版本环境中完整执行。不同Kubernetes和kubectl版本、容器运行时、Metrics Server、操作系统及RBAC策略可能影响实际结果,使用前应通过kubectl help和目标集群验证。

一、排障前先确认自己连到了哪里
多集群环境里,最严重的错误有时不是命令写错,而是在错误的Context或Namespace执行了正确命令。
1.1 确认Context、集群和Namespace
kubectl config current-context
kubectl config get-contexts
kubectl cluster-info
kubectl config view --minify
关注四项信息:
- 当前Context名称
- API Server地址
- 当前用户或认证身份
- 默认Namespace
kubectl config view --minify可能展示集群地址、用户引用和证书配置。输出归档或发到群里之前必须脱敏,不要把Token、客户端证书或完整kubeconfig当作故障附件传播。
生产操作建议显式指定Namespace:
NS=<namespace>
kubectl get pod -n "$NS"
不要因为Shell提示符里写了生产集群名称,就假设当前Context一定正确。
1.2 确认客户端与服务端版本
kubectl version
如果某个参数不存在、输出字段不同或kubectl events无法使用,先比较客户端与服务端版本,再查看本机帮助:
kubectl events --help
kubectl debug --help
kubectl logs --help
文章中的参数不能替代目标环境的命令帮助。尤其是较新的子命令、调试Profile和特性门控,必须以实际版本为准。
1.3 先确认是否有读取权限
kubectl auth can-i get pods -n "$NS"
kubectl auth can-i get pods/log -n "$NS"
kubectl auth can-i list events -n "$NS"
kubectl auth can-i create pods/ephemeralcontainers -n "$NS"
前三项属于常见只读排障权限,最后一项涉及临时调试容器,风险和授权级别更高。
如果命令返回Forbidden,它只能说明当前身份没有执行该API动作的权限,不代表目标资源不存在。不要为了排障临时绑定cluster-admin,应按最小权限补充所需动作并保留审计记录。
二、先建立工具与问题的对应关系
| 工具 | 主要回答的问题 | 典型观察点 | 是否改变现场 |
|---|---|---|---|
get | 哪些对象异常,异常集中在哪里 | 状态、Ready、重启、Node、IP、版本 | 否 |
describe | 单个对象为什么处于当前状态 | Conditions、容器状态、探针、挂载、Events | 否 |
logs | 容器进程在指定时间说了什么 | 时间、错误类型、上下文、当前或上次实例 | 否 |
events | Kubernetes组件观察到了什么 | Reason、对象、时间、次数、消息 | 否 |
top | 当前近期资源用量是否异常 | CPU、内存、Pod和Node对比 | 否 |
custom-columns或JSONPath | 怎样批量提取和对比字段 | Node、镜像、重启、退出原因、资源配置 | 否 |
debug | 现有容器缺工具或需要进入Node时怎样取证 | DNS、网络、进程、文件、主机环境 | 可能会 |
选择顺序不是固定的。例如:
Pod Pending
→ get确认范围
→ events或describe查看调度原因
→ 暂时不需要logs,因为容器可能尚未启动
CrashLoopBackOff
→ get确认重启和分布
→ describe看Last State与Exit Code
→ logs --previous读取上一次实例日志
多个服务集中在同一Node异常
→ get按Node分组
→ describe node与top node
→ 必要时经过授权使用debug node
三、get不是看一眼状态,而是确认范围和差异
3.1 用-o wide建立第一张分布表
kubectl get pods -n "$NS" -o wide
常见字段的语义如下:
| 字段 | 可以证明什么 | 不能直接证明什么 |
|---|---|---|
| READY | 就绪容器数与总容器数 | 业务接口一定正常 |
| STATUS | kubectl给出的便捷状态摘要 | 完整Pod状态机和根因 |
| RESTARTS | 当前Pod UID下容器累计重启信息 | 重启一定由应用Bug造成 |
| AGE | 当前对象存在时间 | 当前容器连续运行时间 |
| IP | 当前Pod IP | Service链路一定正常 |
| NODE | Pod所在节点 | 节点一定健康或异常 |
第一轮应该寻找差异:
- 异常是否只有一个Pod
- 异常Pod是否集中在同一Node
- 重启是否只发生在新版本Pod
- Ready异常是否与Pod年龄对应
- 同一工作负载是否存在新旧ReplicaSet
3.2 Label Selector用于限定业务范围
kubectl get pods -n "$NS" -l app=<app-name> -o wide
kubectl get pods -n "$NS" -l 'app=<app-name>,tier=backend' -o wide
kubectl get pods -n "$NS" -l 'version in (v1,v2)' -o wide
使用前先确认实际Label:
kubectl get pods -n "$NS" --show-labels
不要默认每个团队都使用app=<name>。如果Selector写错后返回空列表,结论应该是当前筛选没有匹配项,而不是业务没有Pod。
3.3 Field Selector用于筛选API字段
kubectl get pods -A \
--field-selector spec.nodeName=<node-name> \
-o wide
kubectl get pods -n "$NS" \
--field-selector status.phase=Pending
Label Selector面向标签,Field Selector面向API支持的字段,两者不能互相替代。不同资源支持的Field Selector字段有限,不能假设任意JSON字段都可用于服务端筛选。
3.4 Watch适合观察变化,不适合代替历史记录
kubectl get pods -n "$NS" -w -o wide
-w可以观察后续变化,但终端看到的是从当前状态开始的事件流,不是完整历史。需要保留证据时,可以加入输出时间或同时依赖监控、日志与Events平台。
按Ctrl+C退出不会改变集群资源,只会终止本地观察命令。
四、describe用来解释状态,但不要拿它做自动解析
kubectl describe pod <pod-name> -n "$NS"
describe将对象配置、状态和相关Events聚合成人类可读输出。它非常适合现场判断,但其文本布局不应作为长期脚本的稳定接口。自动化提取应使用-o json、JSONPath、Go Template或客户端库。
4.1 Pod需要重点看六个区域
调度与身份
Namespace
Node
Start Time
Labels
Controlled By
它们用于确认Pod属于哪个控制器、运行在哪台Node,以及是否与异常分布一致。
Init Containers与Containers
State
Last State
Reason
Exit Code
Started
Finished
Ready
Restart Count
State是当前容器实例状态,Last State是上一个已终止实例的信息。容器已经重启后,当前可能显示Running,但Last State仍可能保留OOMKilled、Error和退出码。
Resources
Requests
Limits
它们用于理解调度承诺和容器限制,但describe只展示配置,不展示当前使用量。当前CPU和内存需要结合top或监控系统。
Environment与Mounts
这里可以发现ConfigMap、Secret引用、Downward API和Volume挂载。注意describe可能暴露环境信息和资源名称,分享前仍需脱敏。
Conditions
PodScheduled
Initialized
ContainersReady
Ready
Conditions比单一Phase更细。Ready=False要继续查看Reason、Message、探针以及EndpointSlice状态。
Events
Pod底部Events经常包含调度失败、镜像拉取、挂载、探针和BackOff线索。但显示<none>只代表当前查询没有返回相关Events,不能证明历史上从未发生事件。
4.2 不同结果决定下一步
Pod没有调度到Node
→ 先看FailedScheduling、资源、污点、亲和性、PVC和配额
Last State存在终止记录
→ 看Reason、Exit Code和Finished时间,再查logs --previous
Ready为False但容器仍Running
→ 看Readiness探针、端口、应用依赖和EndpointSlice
Mount失败
→ 转向PVC、Secret、ConfigMap、CSI与Node挂载事件
Events没有明显异常
→ 不能停止排查,继续看应用日志、指标和业务链路

五、logs必须先确认容器、实例和时间窗口
5.1 多容器Pod先列出容器名
kubectl get pod <pod-name> -n "$NS" \
-o jsonpath='{.spec.containers[*].name}{"\n"}'
然后显式指定容器:
kubectl logs <pod-name> -n "$NS" -c <container-name>
如果不指定-c,单容器Pod通常可以直接读取;多容器Pod可能要求选择容器,或采用注解中指定的默认容器。生产脚本最好显式指定,避免读错Sidecar。
5.2 当前日志与上一次实例日志不是一回事
# 当前容器实例
kubectl logs <pod-name> -n "$NS" \
-c <container-name> --timestamps
# 上一个已终止的容器实例
kubectl logs <pod-name> -n "$NS" \
-c <container-name> --previous --timestamps
典型场景:容器OOM后已被重新拉起,当前日志只有启动信息,真正的异常上下文可能位于--previous。
--previous读取失败可能有多种原因:容器没有上一次实例、旧日志已不可用、容器名错误或权限不足。不能把命令失败直接解释为应用没有崩溃过。
5.3 限定时间和行数,避免把整个日志文件拉回来
kubectl logs <pod-name> -n "$NS" \
-c <container-name> \
--since=30m \
--tail=500 \
--timestamps
也可以使用绝对时间,具体格式以当前版本帮助为准:
kubectl logs <pod-name> -n "$NS" \
-c <container-name> \
--since-time=<RFC3339-time> \
--timestamps
时间必须与告警、发布和Events对齐。若应用日志本身没有时区或时间戳,应先确认容器和日志平台使用的时间基准。
5.4 按Label读取多个Pod日志要控制并发和可识别性
kubectl logs -n "$NS" \
-l app=<app-name> \
--all-containers=true \
--prefix \
--since=10m \
--tail=200 \
--max-log-requests=5
这适合快速比较多个副本,但存在三个限制:
- 多个Pod日志交错,必须保留Pod和容器前缀
- 大范围拉取可能给API Server、kubelet和本地终端增加压力
kubectl logs不是集中式日志检索系统,历史、跨Pod关联和长期保存应依赖日志平台
不要在生产高峰直接对全Namespace执行无限制的--all-containers日志抓取。
六、events回答Kubernetes组件观察到了什么
应用日志来自容器进程,Events通常来自调度器、kubelet、控制器、存储或其他组件。两者描述的是不同观察面。
6.1 优先使用kubectl events
kubectl events -n "$NS" --types=Warning
kubectl events -n "$NS" --for pod/<pod-name>
kubectl events -A --types=Warning
实际版本支持的--for和--types参数应通过下面的命令确认:
kubectl events --help
如果目标版本没有kubectl events,可以退回资源查询:
kubectl get events -n "$NS" \
--field-selector involvedObject.name=<pod-name> \
--sort-by='.metadata.creationTimestamp'
6.2 Events重点看四项
| 字段 | 排障意义 |
|---|---|
| OBJECT或涉及对象 | 哪个资源被观察到异常 |
| REASON | 机器可识别的原因摘要 |
| AGE或时间 | 是否与故障窗口一致 |
| NOTE或MESSAGE | 组件给出的详细上下文 |
常见Reason包括FailedScheduling、FailedMount、Unhealthy、BackOff和FailedCreate。同一个Reason可能被聚合并带有重复次数,因此不能把列表中的一行简单理解为只发生过一次。
6.3 Events的证据边界
- Events有保留周期,不是永久审计记录
- Events可能被聚合、限流或重复
- 没有Events不等于没有故障
- Event时间字段在API版本和客户端展示中可能不同
- Message适合人读,不建议依赖完整文本做稳定自动化判断
需要长期关联时,应将Events接入集中平台,并与发布记录、指标和日志使用统一时间基准。
七、top只能回答近期资源使用,不能回答历史和根因
7.1 先确认资源指标链路可用
kubectl top nodes
kubectl top pods -n "$NS"
如果返回类似Metrics API not available,优先检查Metrics Server或资源指标管道,而不是据此判断所有Pod没有CPU和内存使用。
kubectl top展示的是资源指标管道提供的近期CPU和内存用量,数据设计目标偏向自动扩缩容等场景,不等同于Linux top的逐进程实时视图,也不替代Prometheus历史趋势。
7.2 看总量还不够,要看容器和对照组
kubectl top pod <pod-name> -n "$NS" --containers
kubectl top pods -n "$NS" --sort-by=memory
kubectl top nodes --sort-by=cpu
判断时至少比较:
- 异常Pod与同工作负载正常Pod
- 当前用量与Requests、Limits
- 异常Node与同节点池其他Node
- 当前值与故障前历史基线
7.3 容器刚重启后,当前内存低不能推翻OOM证据
假设describe显示上一个容器实例Reason: OOMKilled,而kubectl top显示当前实例内存很低,两者并不矛盾。前者描述上一次终止,后者描述新实例近期使用。
正确的下一步是:
检查Last State和退出时间
→ 对齐历史监控中的内存曲线
→ 检查容器Limit、工作集和应用堆配置
→ 判断是突发峰值、配置不匹配还是持续泄漏
不要用重启后的低用量否定重启前的资源问题。
八、批量对比优先用custom-columns,复杂提取再用JSONPath
8.1 custom-columns适合值班现场快速看表
kubectl get pods -n "$NS" \
-o custom-columns='POD:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount,NODE:.spec.nodeName,IP:.status.podIP'
查看镜像和Node分布:
kubectl get pods -n "$NS" \
-l app=<app-name> \
-o custom-columns='POD:.metadata.name,IMAGE:.spec.containers[*].image,NODE:.spec.nodeName,START:.status.startTime'
优点是可读性好,缺点是复杂嵌套、条件处理和多容器对应关系有限。
8.2 JSONPath适合提取嵌套字段
列出Pod、Node和Phase:
kubectl get pods -n "$NS" -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\t"}{.status.phase}{"\n"}{end}'
查看所有容器的重启次数:
kubectl get pod <pod-name> -n "$NS" \
-o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.restartCount}{"\n"}{end}'
查看上一次终止原因和退出码:
kubectl get pod <pod-name> -n "$NS" \
-o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.lastState.terminated.reason}{"\t"}{.lastState.terminated.exitCode}{"\n"}{end}'
查看Requests和Limits:
kubectl get pod <pod-name> -n "$NS" \
-o jsonpath='{range .spec.containers[*]}{.name}{"\trequest.cpu="}{.resources.requests.cpu}{"\trequest.memory="}{.resources.requests.memory}{"\tlimit.cpu="}{.resources.limits.cpu}{"\tlimit.memory="}{.resources.limits.memory}{"\n"}{end}'
8.3 JSONPath的三个常见陷阱
- 字段不存在时可能输出空值,空值不等于
false或0。 - 多容器数组展开后,要保证容器名与对应状态仍能配对。
- Shell引号在Linux、macOS和Windows上的行为不同,跨平台脚本需要单独验证。
如果要进行复杂条件、关联多个资源或长期自动化,优先考虑-o json配合jq,或者使用Kubernetes客户端库,而不是把所有逻辑塞进一条JSONPath。

九、debug不是只读命令,使用前先确认边界
当业务镜像极度精简,没有Shell、curl、dig或ss时,不应该为了排障临时修改正式镜像。kubectl debug提供了几种调试方式,但它们都会在集群中创建或修改调试对象。
9.1 给运行中的Pod添加临时调试容器
kubectl debug -it pod/<pod-name> -n "$NS" \
--image=<approved-debug-image> \
--target=<container-name>
适用场景:
- 正式容器没有Shell或网络工具
- 需要从同一个Pod网络环境测试DNS、端口或路由
- 需要观察目标容器相关进程,但是否可见取决于运行时和进程命名空间配置
风险与边界:
- 会更新Pod的ephemeral containers子资源
- 临时容器通常不能像普通容器一样删除或重启
- 调试镜像可能带入额外工具和供应链风险
- RBAC、Pod安全策略和准入控制可能阻止操作
--target效果取决于容器运行时支持
9.2 创建Pod副本进行调试
kubectl debug pod/<pod-name> -n "$NS" -it \
--copy-to=<pod-name>-debug \
--container=<container-name> \
-- sh
Pod副本适合需要调整命令、镜像或部分配置的场景。它不会完全复刻原Pod运行时现场,尤其是IP、临时状态、外部连接和时间条件可能已经变化。
调试完成后,先保存证据,再删除明确创建的副本:
kubectl delete pod <pod-name>-debug -n "$NS"
删除前确认名称,避免误删正式Pod。
9.3 调试Node
kubectl debug node/<node-name> -it \
--image=<approved-debug-image>
Node调试通常会在目标Node创建调试Pod,并将主机根文件系统挂载到特定路径,常见为/host。实际权限、Profile、挂载和进程可见性以当前版本帮助、集群策略与生成的Pod规格为准。
Node调试前必须确认:
- 已获得生产Node调试授权
- 使用经过扫描和批准的固定版本镜像
- 了解调试容器获得的主机访问范围
- 命令会被审计,输出不会泄露凭据和业务数据
- 完成后清理调试Pod
不要在没有审批的情况下使用特权Profile,也不要把宿主机目录、容器运行时Socket或ServiceAccount凭据复制到外部。

十、组合案例:一个Running Pod为什么仍在反复重启
下面是一个基于Kubernetes真实机制构造的C级模拟场景,用于演示工具组合。资源名、时间和输出均为说明性示例,不代表真实生产事故。
10.1 故障现象
checkout-api有三个副本,用户反馈部分请求出现502。第一轮查询如下:
NS=shop
kubectl get pods -n "$NS" -l app=checkout-api -o wide
说明性输出:
NAME READY STATUS RESTARTS AGE IP NODE
checkout-api-6f8d7c9b5f-a1b2c 1/1 Running 0 2h 10.2.1.21 worker-a
checkout-api-6f8d7c9b5f-d3e4f 0/1 Running 4 2h 10.2.3.18 worker-c
checkout-api-6f8d7c9b5f-g5h6i 1/1 Running 0 2h 10.2.2.16 worker-b
这里能确认:
- 不是全部副本异常
- 异常Pod当前Phase显示Running,但Ready为
0/1 - 异常Pod已经重启4次
- 下一步应该进入异常Pod状态,而不是先查Service全部链路
10.2 用describe区分当前与上次实例
POD=checkout-api-6f8d7c9b5f-d3e4f
kubectl describe pod "$POD" -n "$NS"
说明性片段:
State: Running
Started: Mon, 24 Aug 2026 14:31:10 +0800
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Finished: Mon, 24 Aug 2026 14:31:08 +0800
Ready: False
Restart Count: 4
Limits:
memory: 512Mi
此时可以形成更准确的结论:
已确认:上一个容器实例以OOMKilled结束,当前新实例已运行但尚未Ready
尚未确认:是内存泄漏、瞬时峰值、堆配置不合理,还是Limit设置错误
退出码137常见于进程收到SIGKILL后的结果,但应结合Reason: OOMKilled、Node状态和监控一起判断,不能只凭退出码命名根因。
10.3 用--previous读取真正相关的日志窗口
kubectl logs "$POD" -n "$NS" \
-c checkout-api \
--previous \
--timestamps \
--tail=300
如果日志中存在内存分配失败或运行时异常,它可以作为应用侧证据。如果日志在进程被终止前没有来得及刷新,空日志也不能否定OOMKilled状态。
当前实例日志仍需查看,但用途不同:
kubectl logs "$POD" -n "$NS" \
-c checkout-api \
--since=10m \
--timestamps \
--tail=300
它用于确认新实例为何仍未Ready,例如启动慢、依赖失败或Readiness探针未通过。
10.4 用Events确认重启和探针时间
kubectl events -n "$NS" --for pod/"$POD"
如果版本不支持该参数,则使用:
kubectl get events -n "$NS" \
--field-selector involvedObject.name="$POD" \
--sort-by='.metadata.creationTimestamp'
把OOMKilled结束时间、BackOff或Unhealthy事件、应用日志和用户502时间放在同一条时间线上。事件只说明组件观察到的现象,不自动给出内存增长原因。
10.5 用top确认当前状态,但回到历史监控验证重启前趋势
kubectl top pod "$POD" -n "$NS" --containers
假设当前只显示较低内存,这符合新实例刚启动的情况。接下来应在Prometheus或监控平台查看重启前的容器工作集、Limit、Node内存压力和同副本对照。
此时工具链给出的不是一句内存泄漏,而是一份证据清单:
| 证据 | 当前结论 | 下一步 |
|---|---|---|
| 一个Pod 0/1且重启4次 | 故障不是全部副本同时发生 | 比较副本版本、流量和Node |
| Last State为OOMKilled | 上一实例发生容器内存终止 | 检查Limit与历史工作集 |
| 当前实例内存较低 | 只能描述重启后的当前状态 | 查询重启前监控 |
| 当前实例仍未Ready | OOM恢复后还有启动或依赖问题 | 查当前日志与探针Events |
| 其他副本正常 | 可作为对照组 | 比较负载、配置和Node |
如果业务影响严重且故障与刚才的发布高度相关,可以按已有Runbook评估回滚;如果健康副本能够承接,则优先保存证据并做单变量验证。具体OOM根因与内存治理会在第05篇深入展开。
十一、可直接使用的只读现场采集脚本
下面的脚本只调用查询类命令,不执行删除、重启、扩缩容和调试容器创建。它仍可能收集镜像、环境引用、内部地址和日志中的敏感信息,因此输出目录必须按事故资料管理。
#!/usr/bin/env bash
set -u
set -o pipefail
NS=${1:?用法: $0 <namespace> <pod-name> [container-name]}
POD=${2:?用法: $0 <namespace> <pod-name> [container-name]}
CONTAINER=${3:-}
STAMP=$(date +%Y%m%d-%H%M%S)
OUT="incident-${NS}-${POD}-${STAMP}"
mkdir -p "$OUT"
chmod 700 "$OUT"
run_capture() {
local name=$1
shift
printf '采集 %s\n' "$name"
"$@" >"$OUT/$name" 2>&1 || \
printf '采集失败: %s\n' "$name" >>"$OUT/errors.txt"
}
run_capture context.txt kubectl config current-context
run_capture version.txt kubectl version
run_capture pod.yaml kubectl get pod "$POD" -n "$NS" -o yaml
run_capture pod-wide.txt kubectl get pod "$POD" -n "$NS" -o wide
run_capture pod-describe.txt kubectl describe pod "$POD" -n "$NS"
run_capture namespace-pods.txt kubectl get pods -n "$NS" -o wide
run_capture services.txt kubectl get service -n "$NS" -o wide
run_capture endpointslices.txt kubectl get endpointslice -n "$NS" -o wide
run_capture events.txt kubectl get events -n "$NS" \
--field-selector "involvedObject.name=$POD" \
--sort-by=.metadata.creationTimestamp
if [[ -n "$CONTAINER" ]]; then
run_capture current.log kubectl logs "$POD" -n "$NS" \
-c "$CONTAINER" --timestamps --since=30m --tail=1000
run_capture previous.log kubectl logs "$POD" -n "$NS" \
-c "$CONTAINER" --previous --timestamps --tail=1000
else
run_capture current.log kubectl logs "$POD" -n "$NS" \
--all-containers=true --timestamps --since=30m --tail=1000
run_capture previous.log kubectl logs "$POD" -n "$NS" \
--all-containers=true --previous --timestamps --tail=1000
fi
if kubectl top pod "$POD" -n "$NS" >/dev/null 2>&1; then
run_capture pod-top.txt kubectl top pod "$POD" -n "$NS" --containers
else
printf 'Metrics API不可用或当前身份无权限\n' \
>>"$OUT/errors.txt"
fi
printf '采集完成: %s\n' "$OUT"
printf '归档或分享前检查日志、地址、镜像和配置引用中的敏感信息\n'
使用方式:
bash collect-pod-evidence.sh <namespace> <pod-name> [container-name]
脚本边界:
- 没有读取Secret对象,但应用日志和Pod YAML仍可能包含敏感信息
previous.log失败不一定是错误,可能没有上一次容器实例- 多容器Pod未指定容器时,日志会混合采集
- Events和Metrics受版本、保留周期、组件与RBAC影响
- 脚本用于保存首轮现场,不能替代根据结果选择下一步
十二、常见误区
误区1:把kubectl get pods当成健康检查
它只能提供对象摘要。Running不是业务健康结论。
误区2:只看describe最底部的Events
还应看State、Last State、Exit Code、Conditions、Resources、Mounts和控制器归属。
误区3:容器重启后只查当前日志
真正的崩溃上下文可能在--previous,也可能只存在于集中日志平台。
误区4:用当前top数据解释过去的故障
重启后的当前用量不能代表重启前峰值,必须查历史监控。
误区5:复制一条JSONPath后不检查空字段
字段不存在、数组为空和实际值为空是不同情况,脚本必须处理。
误区6:把kubectl debug当成完全只读
它可能更新Pod临时容器子资源或创建新的Pod,Node调试还可能获得主机访问能力。
误区7:看到Forbidden就认为资源不存在
Forbidden是授权结论,不是资源存在性结论。
误区8:在事故群里直接粘贴完整YAML和日志
输出可能包含内部地址、镜像仓库、配置引用、用户数据和凭据片段,应先脱敏。
十三、把工具使用沉淀为团队能力
个人会用命令还不够。生产团队应进一步建设:
- 只读排障Role,覆盖Pod、日志、Events、Service、EndpointSlice、Deployment和Node必要查询。
- 经审批的调试镜像,固定版本、完成漏洞扫描,不临时使用未知镜像。
- 现场采集脚本和输出目录规范,默认限制权限并要求脱敏。
- Context与生产环境的明显提示,降低跨集群误操作。
- Events、审计日志、应用日志、指标和发布记录的统一时间基准。
kubectl debug、exec和变更类命令的审计与授权流程。- 针对Pending、CrashLoopBackOff、OOM、Service和Node故障的专项Runbook。
K8sChat后续也可以复用这套工具边界:默认只开放get、状态、Events和日志等查询能力;临时容器、重启、扩缩容和节点操作必须经过人工确认。当前K8sChat仍是产品设计,不能把这套映射描述成已经运行的Agent能力。
十四、面试怎么说
60秒版本
我不会把kubectl命令当成固定套餐,而是先判断当前要回答什么问题。
get用于确认故障范围和Pod分布,describe查看Conditions、容器当前与上次状态以及Events,容器重启时用logs --previous补上一次实例日志,events查看调度器、kubelet和控制器的观察,top只用于近期资源对比,历史趋势回到Prometheus。批量对比字段时用custom-columns或JSONPath。业务镜像没有工具时才评估kubectl debug,并先检查RBAC、调试镜像、审计和主机访问风险。每条命令都要对应当前假设,并根据结果选择下一步。
3分钟场景版本
假设三个副本中有一个Pod显示Running但Ready为0/1并持续重启。我先用
get -o wide确认异常是否集中在单Pod、特定版本或Node,再用describe查看Last State、Reason、Exit Code、Resources和探针Events。如果上一个实例是OOMKilled,就用logs --previous查看终止前日志,同时对齐事件时间。kubectl top只能看到新实例近期使用,不能用它否定重启前OOM,因此还要去Prometheus查看历史工作集和Limit。其他正常副本作为对照,继续比较配置、流量和Node。这样得到的是证据链,而不是看到退出码137就直接说内存泄漏。只有现有容器缺少必要工具时,我才会经过授权使用临时调试容器。
十五、延伸问答
1. describe和get -o yaml有什么区别
describe聚合展示人类排障常看的配置、状态和相关Events;YAML更接近API对象,适合完整保存和结构化处理。两者用途不同。
2. 为什么Pod重启后要看--previous
kubectl logs默认读取当前容器实例。上一次实例已经终止时,其崩溃前日志需要通过--previous尝试读取。
3. Events和日志冲突时相信谁
先确认二者的对象、观察者和时间。应用日志描述进程视角,Events描述Kubernetes组件视角,它们可能同时正确,只是观察层不同。
4. kubectl top显示内存低,能否排除OOM
不能。它可能展示容器重启后的新实例。应查看Last State、历史监控、Limit和Node压力。
5. 什么情况下使用JSONPath
需要从多个对象批量提取嵌套字段时使用。简单表格优先custom-columns,复杂逻辑考虑JSON与jq或客户端库。
6. 临时调试容器会改变业务Pod吗
它会更新Pod的临时容器子资源并共享部分Pod环境,属于主动调试动作。是否共享进程、可获得哪些权限取决于配置、运行时和安全策略。
7. 为什么不建议事故期间无限制拉取全量日志
这会制造大量无关信息,也可能增加API Server、kubelet、网络和本地终端压力。应先限定对象、容器、时间和行数。
8. kubectl输出能否作为完整事故证据
它是重要证据来源,但不是全部。完整事故还需要业务指标、集中日志、历史监控、发布记录、云平台事件和操作审计。
小结
- 使用kubectl之前先确认Context、Namespace、版本和权限。
get用于确认范围,describe用于解释对象状态,二者不能互相替代。- 容器重启后要区分当前日志与
--previous日志。 - Events、应用日志和指标来自不同观察面,必须按时间对齐。
top只能提供近期资源视图,历史和根因分析需要监控系统。- 批量对比优先使用
custom-columns或JSONPath,不解析describe文本。 kubectl debug属于主动调试,必须控制RBAC、镜像、权限和审计。- 最有价值的命令不是输出最多的命令,而是最能区分当前假设的命令。
下一篇预告
下一篇进入Pod Pending系统排查。我们会沿着调度器的决策过程,逐项分析资源不足、污点与容忍、节点亲和性、拓扑约束、PVC绑定、ResourceQuota和调度器插件,并整理一份可直接执行的Pending决策树。
参考资料
更多推荐
所有评论(0)