出事的时候没人会去翻文档。这篇按实际排查顺序排:先看集群是不是活着,再看 Pod 死在哪一步,最后才是网络和调度。命令全部能直接复制。
一、结论先给:90% 的问题走这三步
kubectl get pod -A # 1)谁不正常
kubectl describe pod <pod> -n <ns> # 2)为什么(看 Events,答案基本都在最后 5 行)
kubectl logs <pod> -n <ns> --tail=100 -f # 3)容器里发生了什么
describe 的 Events 段是 kubectl 里信息密度最高的地方,比 logs 更早该看。日志只告诉你程序想说什么,Events 告诉你 K8s 对它做了什么。
二、开干之前:先把环境配舒服
# 补全(bash)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
# zsh
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
# 别名,能少敲一半
cat >> ~/.bashrc <<'EOF'
alias k='kubectl'
alias kg='kubectl get'
alias kd='kubectl describe'
alias kl='kubectl logs'
alias kx='kubectl exec -it'
alias kn='kubectl config set-context --current --namespace'
complete -F __start_kubectl k
EOF
source ~/.bashrc
kn <ns> 切换默认命名空间是这几个里最省时间的——不用每条命令都带 -n。
多集群别手改 kubeconfig,用上下文切:
kubectl config get-contexts
kubectl config use-context prod
kubectl config view --minify | grep namespace # 当前上下文
三、第一层:集群活着吗
kubectl cluster-info
kubectl get node -o wide
kubectl get cs # 新版已废弃,1.19+ 用下面这条
kubectl get --raw='/readyz?verbose' | head -20
kubectl top node # 需要 metrics-server
top 报错 Metrics API not available 不是集群坏了,是没装 metrics-server,装完就有:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 内网环境常见坑:kubelet 证书自签,需要给 metrics-server 加 --kubelet-insecure-tls
节点 NotReady 的常见原因排前三:kubelet 挂了、磁盘满了、容器运行时挂了。
systemctl status kubelet && journalctl -u kubelet --since "10 min ago" | tail -40
df -h /var/lib/docker /var/lib/containerd 2>/dev/null
systemctl status containerd
磁盘满是最容易被忽略的一个——K8s 默认在磁盘 < 15% 时开始驱逐 Pod,表现为 Pod 莫名被 Evicted。
四、第二层:Pod 死在哪一步
kubectl get pod 的 STATUS 只有那几种,认全了排查就快了:
| 状态 | 含义 | 第一动作 |
|---|---|---|
Pending |
调度不进去 | describe 看 Events,多半是资源不够或污点没容忍 |
ImagePullBackOff |
镜像拉不下来 | 看仓库地址、tag、imagePullSecrets |
CrashLoopBackOff |
起来就退 | logs --previous 看上一次的报错 |
Running 但 0/1 |
存活但探针不过 | 看 readiness/liveness 配置与端口 |
Evicted |
被驱逐 | 节点磁盘或内存压力 |
Terminating 卡住 |
删不掉 | 有 finalizer 或节点失联 |
Init:0/1 |
初始化容器没跑完 | 看 initContainer 日志 -c <init-name> |
对应命令:
# Pending:为什么调度不进去
kubectl describe pod <pod> -n <ns> | sed -n '/Events/,$p'
# 典型输出:0/3 nodes are available: 3 Insufficient cpu
# CrashLoop:看上一次崩溃的日志(关键在 --previous)
kubectl logs <pod> -n <ns> --previous
# 多容器 Pod 必须指定容器
kubectl logs <pod> -n <ns> -c <container>
# Init 容器卡住
kubectl logs <pod> -n <ns> -c <init-container-name>
# 资源实际用量
kubectl top pod -A --sort-by=memory | head -20
--previous 是被低估最多的一个参数。容器崩溃重启后,不带它看到的是新进程的日志,真正的死因在上一次。
五、第三层:进容器里看
kubectl exec -it <pod> -n <ns> -- /bin/sh # 镜像没有 bash 时用 sh
kubectl exec -it <pod> -n <ns> -c <container> -- /bin/bash
# 没有 curl / 没有 shell 的极简镜像:临时起一个调试容器共享网络栈
kubectl debug -it <pod> -n <ns> --image=busybox:1.36 --target=<container>
kubectl debug(1.18+ beta、1.23+ GA)是 distroless 镜像时代的救命功能——目标容器里啥都没有,就在旁边挂一个有工具的容器,共享进程和网络命名空间。
本地调端口:
kubectl port-forward svc/my-svc 8080:80 -n <ns>
kubectl port-forward pod/<pod> 15432:5432 # 连数据库最常用
拷贝文件(没装 kubectl 的机器之间传东西):
kubectl cp <pod>:/app/config.yaml ./config.yaml -n <ns>
kubectl cp ./local.conf <pod>:/etc/nginx/nginx.conf -n <ns>
六、第四层:服务通不通
kubectl get svc,ep -n <ns> # ep 是 endpoints,空的就是 selector 没匹配上
kubectl get ingress -n <ns>
kubectl describe ingress <name> -n <ns>
Endpoints 为空是 Service 不通的第一大原因:Service 的 selector 和 Pod 的 label 对不上,Service 正常存在但后面没挂任何东西。
# 直接比对
kubectl get svc my-svc -n <ns> -o jsonpath='{.spec.selector}'
kubectl get pod -n <ns> --show-labels | grep my-svc
DNS 解析:
kubectl run -it --rm dns-test --image=busybox:1.36 --restart=Never -- nslookup my-svc.<ns>.svc.cluster.local
Service 的四层连不上,但 Pod IP 直连能通 —— 基本就是 kube-proxy 或 NetworkPolicy 的锅。
七、改东西:apply / edit / scale / rollout
kubectl apply -f deploy.yaml
kubectl edit deploy/<name> -n <ns> # 在线改,保存即生效
kubectl scale deploy/<name> --replicas=5 -n <ns>
kubectl set image deploy/<name> app=nginx:1.27 -n <ns>
# 发布状态三件套
kubectl rollout status deploy/<name> -n <ns>
kubectl rollout history deploy/<name> -n <ns>
kubectl rollout undo deploy/<name> -n <ns> # 回滚上一版
kubectl rollout undo deploy/<name> --to-revision=3 -n <ns>
# 强制重启(配置没变但想重拉)
kubectl rollout restart deploy/<name> -n <ns>
kubectl edit 适合救急,但别把它当常态——改动不在 Git 里,下次 apply 会被覆盖回去。
八、查资源占用与清理
kubectl top pod -A --sort-by=cpu | head -20
kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p' # 看这台机器还剩多少
# 清理(慎用,按命名空间批量删前先 --dry-run)
kubectl delete pod --field-selector=status.phase=Failed -A
kubectl delete pod --field-selector=status.phase=Succeeded -A
kubectl delete evicted-pod 2>/dev/null || kubectl get pod -A | grep Evicted | awk '{print $1" "$2}' | xargs -n2 sh -c 'kubectl delete pod $1 -n $0'
Requests / Limits 配错是"集群看着很闲但新 Pod 调度不进去"的根源——看 Allocated resources 那段,requests 那列才是调度依据,不是实际用量。
九、一张表收尾
| 我想… | 命令 |
|---|---|
| 看谁挂了 | kubectl get pod -A | grep -v Running |
| 为什么挂 | kubectl describe pod <pod> -n <ns> |
| 崩溃前说了啥 | kubectl logs <pod> --previous |
| 进容器 | kubectl exec -it <pod> -- sh |
| 本地连集群服务 | kubectl port-forward svc/x 8080:80 |
| 发版失败回退 | kubectl rollout undo deploy/x |
| 重启应用 | kubectl rollout restart deploy/x |
| 服务不通 | kubectl get ep -n <ns> 看 Endpoints 空不空 |
别再敲 -n |
kn <ns> |
命令记不全没关系,K8s 命令速查 按场景列好了,直接搜就行。排障时如果要从日志里找规律(比如某个错误码一天出现多少次、集中在哪几个小时),可以把日志贴进 日志分析工具 直接出结论。
相关工具:K8s 命令速查 · Linux 命令速查 · 日志分析工具
延伸阅读:Ubuntu 服务器调优:上线前必须改的 12 个系统参数 · Nginx 跑满并发:Nginx + 系统内核调优