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

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

写在前面

很多Kubernetes排障清单都是这样开始的:

bash 复制代码
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>

命令本身没有错,但如果不知道每条命令要回答什么问题,排障很容易变成机械巡检:输出越来越多,故障范围却没有缩小。

kubectl get适合看范围和差异,describe适合看单个对象的聚合状态,logs回答容器进程说了什么,events记录控制面和组件观察到了什么,top提供近期资源用量,debug则用于现有容器缺少工具或需要进入Node现场的情况。

真正需要掌握的不是命令数量,而是下面这条链路:

text 复制代码
当前问题
→ 选择工具
→ 提取关键字段
→ 解释字段语义
→ 形成下一项假设
→ 决定继续只读取证还是进入主动调试

本文命令按照Kubernetes官方文档和命令参考进行语法核对。由于当前没有连接可验证的实验集群,文中的终端输出均为说明性示例,不是生产原始记录,也不声称已经在统一版本环境中完整执行。不同Kubernetes和kubectl版本、容器运行时、Metrics Server、操作系统及RBAC策略可能影响实际结果,使用前应通过kubectl help和目标集群验证。


一、排障前先确认自己连到了哪里

多集群环境里,最严重的错误有时不是命令写错,而是在错误的Context或Namespace执行了正确命令。

1.1 确认Context、集群和Namespace

bash 复制代码
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:

bash 复制代码
NS=<namespace>
kubectl get pod -n "$NS"

不要因为Shell提示符里写了生产集群名称,就假设当前Context一定正确。

1.2 确认客户端与服务端版本

bash 复制代码
kubectl version

如果某个参数不存在、输出字段不同或kubectl events无法使用,先比较客户端与服务端版本,再查看本机帮助:

bash 复制代码
kubectl events --help
kubectl debug --help
kubectl logs --help

文章中的参数不能替代目标环境的命令帮助。尤其是较新的子命令、调试Profile和特性门控,必须以实际版本为准。

1.3 先确认是否有读取权限

bash 复制代码
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、网络、进程、文件、主机环境 可能会

选择顺序不是固定的。例如:

text 复制代码
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建立第一张分布表

bash 复制代码
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用于限定业务范围

bash 复制代码
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:

bash 复制代码
kubectl get pods -n "$NS" --show-labels

不要默认每个团队都使用app=<name>。如果Selector写错后返回空列表,结论应该是当前筛选没有匹配项,而不是业务没有Pod。

3.3 Field Selector用于筛选API字段

bash 复制代码
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适合观察变化,不适合代替历史记录

bash 复制代码
kubectl get pods -n "$NS" -w -o wide

-w可以观察后续变化,但终端看到的是从当前状态开始的事件流,不是完整历史。需要保留证据时,可以加入输出时间或同时依赖监控、日志与Events平台。

Ctrl+C退出不会改变集群资源,只会终止本地观察命令。


四、describe用来解释状态,但不要拿它做自动解析

bash 复制代码
kubectl describe pod <pod-name> -n "$NS"

describe将对象配置、状态和相关Events聚合成人类可读输出。它非常适合现场判断,但其文本布局不应作为长期脚本的稳定接口。自动化提取应使用-o json、JSONPath、Go Template或客户端库。

4.1 Pod需要重点看六个区域

调度与身份
text 复制代码
Namespace
Node
Start Time
Labels
Controlled By

它们用于确认Pod属于哪个控制器、运行在哪台Node,以及是否与异常分布一致。

Init Containers与Containers
text 复制代码
State
Last State
Reason
Exit Code
Started
Finished
Ready
Restart Count

State是当前容器实例状态,Last State是上一个已终止实例的信息。容器已经重启后,当前可能显示Running,但Last State仍可能保留OOMKilledError和退出码。

Resources
text 复制代码
Requests
Limits

它们用于理解调度承诺和容器限制,但describe只展示配置,不展示当前使用量。当前CPU和内存需要结合top或监控系统。

Environment与Mounts

这里可以发现ConfigMap、Secret引用、Downward API和Volume挂载。注意describe可能暴露环境信息和资源名称,分享前仍需脱敏。

Conditions
text 复制代码
PodScheduled
Initialized
ContainersReady
Ready

Conditions比单一Phase更细。Ready=False要继续查看Reason、Message、探针以及EndpointSlice状态。

Events

Pod底部Events经常包含调度失败、镜像拉取、挂载、探针和BackOff线索。但显示<none>只代表当前查询没有返回相关Events,不能证明历史上从未发生事件。

4.2 不同结果决定下一步

text 复制代码
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先列出容器名

bash 复制代码
kubectl get pod <pod-name> -n "$NS" \
  -o jsonpath='{.spec.containers[*].name}{"\n"}'

然后显式指定容器:

bash 复制代码
kubectl logs <pod-name> -n "$NS" -c <container-name>

如果不指定-c,单容器Pod通常可以直接读取;多容器Pod可能要求选择容器,或采用注解中指定的默认容器。生产脚本最好显式指定,避免读错Sidecar。

5.2 当前日志与上一次实例日志不是一回事

bash 复制代码
# 当前容器实例
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 限定时间和行数,避免把整个日志文件拉回来

bash 复制代码
kubectl logs <pod-name> -n "$NS" \
  -c <container-name> \
  --since=30m \
  --tail=500 \
  --timestamps

也可以使用绝对时间,具体格式以当前版本帮助为准:

bash 复制代码
kubectl logs <pod-name> -n "$NS" \
  -c <container-name> \
  --since-time=<RFC3339-time> \
  --timestamps

时间必须与告警、发布和Events对齐。若应用日志本身没有时区或时间戳,应先确认容器和日志平台使用的时间基准。

5.4 按Label读取多个Pod日志要控制并发和可识别性

bash 复制代码
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

bash 复制代码
kubectl events -n "$NS" --types=Warning
kubectl events -n "$NS" --for pod/<pod-name>
kubectl events -A --types=Warning

实际版本支持的--for--types参数应通过下面的命令确认:

bash 复制代码
kubectl events --help

如果目标版本没有kubectl events,可以退回资源查询:

bash 复制代码
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 先确认资源指标链路可用

bash 复制代码
kubectl top nodes
kubectl top pods -n "$NS"

如果返回类似Metrics API not available,优先检查Metrics Server或资源指标管道,而不是据此判断所有Pod没有CPU和内存使用。

kubectl top展示的是资源指标管道提供的近期CPU和内存用量,数据设计目标偏向自动扩缩容等场景,不等同于Linux top的逐进程实时视图,也不替代Prometheus历史趋势。

7.2 看总量还不够,要看容器和对照组

bash 复制代码
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显示当前实例内存很低,两者并不矛盾。前者描述上一次终止,后者描述新实例近期使用。

正确的下一步是:

text 复制代码
检查Last State和退出时间
→ 对齐历史监控中的内存曲线
→ 检查容器Limit、工作集和应用堆配置
→ 判断是突发峰值、配置不匹配还是持续泄漏

不要用重启后的低用量否定重启前的资源问题。


八、批量对比优先用custom-columns,复杂提取再用JSONPath

8.1 custom-columns适合值班现场快速看表

bash 复制代码
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分布:

bash 复制代码
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:

bash 复制代码
kubectl get pods -n "$NS" -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\t"}{.status.phase}{"\n"}{end}'

查看所有容器的重启次数:

bash 复制代码
kubectl get pod <pod-name> -n "$NS" \
  -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.restartCount}{"\n"}{end}'

查看上一次终止原因和退出码:

bash 复制代码
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:

bash 复制代码
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添加临时调试容器

bash 复制代码
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副本进行调试

bash 复制代码
kubectl debug pod/<pod-name> -n "$NS" -it \
  --copy-to=<pod-name>-debug \
  --container=<container-name> \
  -- sh

Pod副本适合需要调整命令、镜像或部分配置的场景。它不会完全复刻原Pod运行时现场,尤其是IP、临时状态、外部连接和时间条件可能已经变化。

调试完成后,先保存证据,再删除明确创建的副本:

bash 复制代码
kubectl delete pod <pod-name>-debug -n "$NS"

删除前确认名称,避免误删正式Pod。

9.3 调试Node

bash 复制代码
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。第一轮查询如下:

bash 复制代码
NS=shop
kubectl get pods -n "$NS" -l app=checkout-api -o wide

说明性输出:

text 复制代码
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区分当前与上次实例

bash 复制代码
POD=checkout-api-6f8d7c9b5f-d3e4f
kubectl describe pod "$POD" -n "$NS"

说明性片段:

text 复制代码
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

此时可以形成更准确的结论:

text 复制代码
已确认:上一个容器实例以OOMKilled结束,当前新实例已运行但尚未Ready
尚未确认:是内存泄漏、瞬时峰值、堆配置不合理,还是Limit设置错误

退出码137常见于进程收到SIGKILL后的结果,但应结合Reason: OOMKilled、Node状态和监控一起判断,不能只凭退出码命名根因。

10.3 用--previous读取真正相关的日志窗口

bash 复制代码
kubectl logs "$POD" -n "$NS" \
  -c checkout-api \
  --previous \
  --timestamps \
  --tail=300

如果日志中存在内存分配失败或运行时异常,它可以作为应用侧证据。如果日志在进程被终止前没有来得及刷新,空日志也不能否定OOMKilled状态。

当前实例日志仍需查看,但用途不同:

bash 复制代码
kubectl logs "$POD" -n "$NS" \
  -c checkout-api \
  --since=10m \
  --timestamps \
  --tail=300

它用于确认新实例为何仍未Ready,例如启动慢、依赖失败或Readiness探针未通过。

10.4 用Events确认重启和探针时间

bash 复制代码
kubectl events -n "$NS" --for pod/"$POD"

如果版本不支持该参数,则使用:

bash 复制代码
kubectl get events -n "$NS" \
  --field-selector involvedObject.name="$POD" \
  --sort-by='.metadata.creationTimestamp'

OOMKilled结束时间、BackOff或Unhealthy事件、应用日志和用户502时间放在同一条时间线上。事件只说明组件观察到的现象,不自动给出内存增长原因。

10.5 用top确认当前状态,但回到历史监控验证重启前趋势

bash 复制代码
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篇深入展开。


十一、可直接使用的只读现场采集脚本

下面的脚本只调用查询类命令,不执行删除、重启、扩缩容和调试容器创建。它仍可能收集镜像、环境引用、内部地址和日志中的敏感信息,因此输出目录必须按事故资料管理。

bash 复制代码
#!/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 复制代码
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
相关推荐
陈聪.1 小时前
Kubernetes 中 Pod 的全面知识
云原生·容器·kubernetes
SakitamaX1 小时前
k8s中的控制器的使用
云原生·容器·kubernetes
祖力551 小时前
Linux应用软件编程:目录IO与framebuffer
linux·运维·算法·framebuffer·目录io
IT 小阿姨(数据库)2 小时前
K8s v1.24.17 完整搭建文档(CentOS7 + containerd1.6.33 + Calico)
java·容器·kubernetes
Dachui_11222 小时前
企业内网大模型公网访问方案:Ollama + OpenWebUI + ZeroNews 实战部署记录
运维·服务器·安全·远程工作·内网穿透
2401_890603402 小时前
Linux 进程控制
linux·运维·服务器
雨辰AI3 小时前
openGauss 生产运维避坑指南|适配信创项目改造核心难点
java·运维·后端
小波波啊3 小时前
nginx 转发超时故障排查实录
运维·nginx
2401_865382503 小时前
信息化建设项目申报建设费时运维费能否一起报送?
运维