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容器进程在指定时间说了什么时间、错误类型、上下文、当前或上次实例
eventsKubernetes组件观察到了什么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就绪容器数与总容器数业务接口一定正常
STATUSkubectl给出的便捷状态摘要完整Pod状态机和根因
RESTARTS当前Pod UID下容器累计重启信息重启一定由应用Bug造成
AGE当前对象存在时间当前容器连续运行时间
IP当前Pod IPService链路一定正常
NODEPod所在节点节点一定健康或异常

第一轮应该寻找差异:

  • 异常是否只有一个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仍可能保留OOMKilledError和退出码。

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包括FailedSchedulingFailedMountUnhealthyBackOffFailedCreate。同一个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的三个常见陷阱

  1. 字段不存在时可能输出空值,空值不等于false0
  2. 多容器数组展开后,要保证容器名与对应状态仍能配对。
  3. Shell引号在Linux、macOS和Windows上的行为不同,跨平台脚本需要单独验证。

如果要进行复杂条件、关联多个资源或长期自动化,优先考虑-o json配合jq,或者使用Kubernetes客户端库,而不是把所有逻辑塞进一条JSONPath。

在这里插入图片描述


九、debug不是只读命令,使用前先确认边界

当业务镜像极度精简,没有Shell、curldigss时,不应该为了排障临时修改正式镜像。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与历史工作集
当前实例内存较低只能描述重启后的当前状态查询重启前监控
当前实例仍未ReadyOOM恢复后还有启动或依赖问题查当前日志与探针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和日志

输出可能包含内部地址、镜像仓库、配置引用、用户数据和凭据片段,应先脱敏。


十三、把工具使用沉淀为团队能力

个人会用命令还不够。生产团队应进一步建设:

  1. 只读排障Role,覆盖Pod、日志、Events、Service、EndpointSlice、Deployment和Node必要查询。
  2. 经审批的调试镜像,固定版本、完成漏洞扫描,不临时使用未知镜像。
  3. 现场采集脚本和输出目录规范,默认限制权限并要求脱敏。
  4. Context与生产环境的明显提示,降低跨集群误操作。
  5. Events、审计日志、应用日志、指标和发布记录的统一时间基准。
  6. kubectl debugexec和变更类命令的审计与授权流程。
  7. 针对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. describeget -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输出能否作为完整事故证据

它是重要证据来源,但不是全部。完整事故还需要业务指标、集中日志、历史监控、发布记录、云平台事件和操作审计。


小结

  1. 使用kubectl之前先确认Context、Namespace、版本和权限。
  2. get用于确认范围,describe用于解释对象状态,二者不能互相替代。
  3. 容器重启后要区分当前日志与--previous日志。
  4. Events、应用日志和指标来自不同观察面,必须按时间对齐。
  5. top只能提供近期资源视图,历史和根因分析需要监控系统。
  6. 批量对比优先使用custom-columns或JSONPath,不解析describe文本。
  7. kubectl debug属于主动调试,必须控制RBAC、镜像、权限和审计。
  8. 最有价值的命令不是输出最多的命令,而是最能区分当前假设的命令。

下一篇预告

下一篇进入Pod Pending系统排查。我们会沿着调度器的决策过程,逐项分析资源不足、污点与容忍、节点亲和性、拓扑约束、PVC绑定、ResourceQuota和调度器插件,并整理一份可直接执行的Pending决策树。

参考资料

  1. Kubernetes官方文档:kubectl命令介绍
  2. Kubernetes官方文档:kubectl get
  3. Kubernetes官方文档:kubectl describe
  4. Kubernetes官方文档:kubectl logs
  5. Kubernetes官方文档:kubectl events
  6. Kubernetes官方文档:kubectl top
  7. Kubernetes官方文档:kubectl debug
  8. Kubernetes官方文档:JSONPath Support
  9. Kubernetes官方文档:Debug Running Pods
  10. Kubernetes官方文档:Resource Metrics Pipeline

更多推荐