K8s实践练习10_故障排查

官网链接:调试Pod

官网链接:调试运行中的Pod

官网链接:调试Service

发现服务异常,第一步是直接访问Pod IP。如果异常,说明问题出在Pod自身;如果正常,问题可能出在Service或DNS。

Pod 排查流水线:

  1. 看状态:kubectl get pods -n <namespace_name> 。STATUS:Pending / Running / CrashLoopBackOff / ImagePullBackOff / Error 等。 READY:1/1 表示容器都就绪;0/1 常见为容器刚启动还没就绪,或就绪探针失败。
  2. 看事件:kubectl describe pod <pod_name> -n <namespace_name>
  3. 看日志:kubectl logs <pod_name>
  4. 必要时进容器:kubectl exec -it <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>。或者使用临时容器。

-c ${CONTAINER_NAME} 是可选择的。如果 Pod 中仅包含一个容器,就可以忽略它。

不加-n <namespace_name>会查看default命名空间的pod

Service 排查流水线:

  1. 查看Pod是否Running、Ready:kubectl get pods -n <namespace_name>
  2. 查看Service是否存在:kubectl get sc -n <namespace_name>
  3. Endpoints是否有IP:
  4. Pod内DNS能否解析Service
  5. 通过ClusterIP能否访问Service
  6. 检测kube-proxy或网络策略

Pod状态异常

调试 Pod 的第一步是查看 Pod 信息。用如下命令查看 Pod 的当前状态和最近的事件

1
kubectl describe pod <Pod-Name>

Pod停滞在 Pending 状态

如果一个 Pod 停滞在 Pending 状态,表示 Pod 没有被调度到节点上。常见原因有:

  • 集群资源不足(CPU/内存)。可能集群上所有的CPU或内存已耗尽。此时需要删除 Pod、调整资源请求或者为集群添加节点。
  • 节点选择器 / 亲和性 / 污点容忍没匹配到节点。此时需要调整 Pod 的 nodeSelector / affinity / tolerations
  • 使用了 hostPort,但可用节点数不足。如果绑定Pod到hostPort,那么集群中节点的个数就是所能创建的 Pod 的数量上限。解决方法是改用 Service 对外暴露,或保证节点数 ≥ Pod 数。

查看详情和事件,输出最后一段Events里通常有FailedScheduling之类的事件。例如:

1
2
3
4
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  10s   default-scheduler  0/3 nodes are available: 3 Insufficient cpu.

Pod停滞在 Waiting 状态

如果 Pod 停滞在 Waiting 状态,则表示 Pod 已经被调度到某工作节点,但是无法在该节点上运行。Waiting 状态的最常见原因是拉取镜像失败。要检查的有三个方面:

  • 确保镜像名字拼写正确。
  • 确保镜像已被推送到镜像仓库。
  • 尝试手动是否能拉取镜像。sudo crictl pull <image_name>

详情和事件里输出的Events里通常有Failed to pull image,非常容易排查。

解决方法就是使用正确的镜像名字,如果k8s官方仓库拉不来下,

  1. 配置阿里云镜像源(国内只有阿里云宣布支持新版的k8s仓库)
  2. 配置私有仓库
  3. 在一个能拉取镜像的机器上手动拉取(可能docker仓库有,可以查看一下),然后保存save为tar包, 导出tar包上传到k8s集群上再导入镜像sudo ctr -n k8s.io images import <image_tar_name>。 如果有需要,可以修改导入到k8s集群上的镜像的标签tag。

Pod一直CrashLoopBackOff / Error

如果Pod一直CrashLoopBackOff/Error,表明容器反复启动后退出。此时 Pod 已经被调度,重点看日志:

1
2
3
kubectl logs <pod-name> -n <namespace>
# 如果容器刚崩溃,可以看前一个容器日志:
kubectl logs <pod-name> --previous -n <namespace>

结合 describe 里的退出码,在 Containers 下的 Last State 可以看到退出码和原因, 例如Exit Code 137 通常表示被 SIGKILL(OOM 或人为杀掉),Exit Code 1、Exit Code 0 等需要看应用日志具体含义。

1
2
3
4
5
kubectl describe pod <pod-name> -n <namespace>

Last State:     Terminated
  Reason:       Error
  Exit Code:    137

日志里常见问题:配置错误(连不上数据库、参数写错)。启动脚本错误。资源限制导致被 OOM Killed

Pod优雅终止(Graceful Termination)

Pod优雅终止能确保高可用,避免请求丢失。当执行kubectl delete Pod或节点维护触发驱逐时:

  1. kube-server将Pod状态设置为Terminating,同时通知EndpointSlice控制器。
  2. EndpointSlice检测到Pod处于Terminating状态,立刻将该Pod的IP从所有关联的Service的EndpointSlice对象中删除。避免请求打到已终止的Pod。
  3. kubelet向Pod的主进程(PID 1)发送SIGTERM信号。应用接受到信号后停止接受新请求,完成正在处理的请求,释放资源(数据库连接、文件句柄等)。
  4. 等待优雅期(Termination Grace PeriodSeconds),默认等待30s,如果应用在超时前退出,则终止成功。
  5. 如果应用未在优雅期退出,kubelet发送SIGKILL强制要求杀死容器。

Pod停滞在terminating 状态

如果Pod停滞在Terminating状态,表示已发出删除Pod的请求,但控制平面无法删除该 Pod 对象。

如果Pod拥有Finalizer并且集群中安装了准入Webhook,可能会导致控制平面无法移除 Finalizer,从而导致Pod出现此问题。

要确认这种情况,请检查你的集群中是否有ValidatingWebhookConfiguration或MutatingWebhookConfiguration处理pods资源的UPDATE操作。

如果 Webhook 是由第三方提供的:

  • 确保你使用的是最新版。
  • 禁用处理 UPDATE 操作的 Webhook。
  • 向相关供应商报告问题。

如果你是 Webhook 的作者:

  • 对于变更性质的 Webhook,请确保在处理 UPDATE 操作时不要更改不可变字段。 例如,一般不允许更改 containers。
  • 对于验证性质的 Webhook,请确保你的验证策略仅被应用于新的更改之上。换句话说, 你应该允许存在违规的现有 Pod 通过验证。这样可以确保在安装验证性质的 Webhook 之前创建的 Pod 可以继续运行

Pod Running 但不符合预期

容器在跑,但行为不正常。这种一般是业务逻辑问题。

1
2
3
4
5
6
7
8
# 进入容器
kubectl exec -it <pod-name> -n <namespace> -- sh

# 在容器内看进程、网络、文件
ps aux
netstat -tlnp
cat /etc/resolv.conf
curl http://localhost:<port>/healthz

进阶:临时容器(Ephemeral Container)

当容器镜像没有 shell,或者容器刚启动就崩,kubectl exec 用不了,可以用 kubectl debug 挂一个临时容器进去

1
2
3
# 示例:给一个 pause 容器(无 shell)加临时 busybox 容器
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo

这会在同一个 Pod 里跑一个 busybox 容器,并共享进程命名空间,可以 ps 看到原容器的进程,做网络调试等。

排查小结

现象 关键命令 重点关注
一直 Pending kubectl describe pod Events 里的调度失败原因
一直 Waiting / ImagePullBackOff kubectl describe pod + 在节点手动拉镜像 镜像名 / 私有仓库凭证
CrashLoopBackOff / Error kubectl logs / kubectl logs –previous 应用日志、退出码
Running 但行为异常 kubectl exec / kubectl debug 容器内网络/进程/配置

Service状态异常

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostnames
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hostnames
  template:
    metadata:
      labels:
        app: hostnames
    spec:
      containers:
      - name: hostnames
        image: registry.k8s.io/serve_hostname
        ports:
        - containerPort: 9376
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - port: 80
    targetPort: 9376
1
2
vim hostnames.yaml
kubectl apply -f hostnames.yaml
  1. 先确认Pod本身没问题
1
2
3
4
5
rust@k8s1:~/k8s_try$ kubectl get pods -l app=hostnames
NAME                        READY   STATUS    RESTARTS   AGE
hostnames-8dbf4d78c-bfd84   1/1     Running   0          10m
hostnames-8dbf4d78c-t6v7c   1/1     Running   0          10m
hostnames-8dbf4d78c-tmqzx   1/1     Running   0          10m
  1. 检查Service是否存在
1
2
3
rust@k8s1:~/k8s_try$ kubectl get svc hostnames
NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
hostnames   ClusterIP   10.97.22.136   <none>        80/TCP    11m
  1. 检查EndpointSlices,正常应该看到3个Pod IP(副本是3)。 如果 ENDPOINTS ,说明 Service 没选中 Pod,通常是标签选择器不匹配,Deployment / Pod 的标签和 Service 的 selector 不一致。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
kubectl get endpointslices -l kubernetes.io/service-name=hostnames

rust@k8s1:~/k8s_try$ kubectl get endpointslices -l kubernetes.io/service-name=hostnames
NAME              ADDRESSTYPE   PORTS   ENDPOINTS                                    AGE
hostnames-s27cx   IPv4          9376    172.16.109.117,172.16.219.22,172.16.219.23   12m

# 查看 Service 的选择器
kubectl get svc hostnames -o jsonpath='{.spec.selector}'
# 查看 Pod 的标签
kubectl get pods -l app=hostnames -o wide
  1. 从 Pod内部测试DNS解析
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 创建临时容器busybox,进入容器里
kubectl run -it --rm --restart=Never busybox --image=busybox sh

# 1. 看 /etc/resolv.conf
cat /etc/resolv.conf

# 2. 解析 Service 名称
nslookup hostnames

# 3. 使用全限定名
nslookup hostnames.default.svc.cluster.local


rust@k8s1:~/k8s_try$ kubectl run -it --rm --restart=Never busybox --image=busybox sh
If you don't see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
/ # nslookup hostnames
Server:		10.96.0.10
Address:	10.96.0.10:53

** server can't find hostnames.cluster.local: NXDOMAIN

** server can't find hostnames.svc.cluster.local: NXDOMAIN

Name:	hostnames.default.svc.cluster.local
Address: 10.97.22.136


** server can't find hostnames.cluster.local: NXDOMAIN

** server can't find hostnames.svc.cluster.local: NXDOMAIN

/ # nslookup hostnames.default.svc.cluster.local
Server:		10.96.0.10
Address:	10.96.0.10:53


Name:	hostnames.default.svc.cluster.local
Address: 10.97.22.136

/ # 

如果短名 hostnames 不行,但 hostnames.default.svc.cluster.local 可以,通常是 search 域或 ndots 配置问题。 如果完全解析不了,确认 CoreDNS / kube-dns 是否正常。

1
2
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system <coredns-pod>
  1. 通过ClusterIP直接访问Service
1
2
3
4
5
6
7
# 获取 Service ClusterIP
kubectl get svc hostnames -n <namespace>

# 在 busybox 里访问
wget -qO- <ClusterIP>:80
# 或
curl http://<ClusterIP>:80

如果 ClusterIP 能访问,说明 Service 代理正常。 如果 ClusterIP 不通,再检查 kube-proxy 日志和 iptables/ipvs 规则。 6. kube-proxy或者网络策略问题 如果Pod正常、Service有Endpoints、DNS解析正常但是ClusterIP仍然不通;那就要怀疑kube-proxy或网络策略。

1
2
3
4
5
# 查看 kube-proxy 日志
kubectl logs -n kube-system <kube-proxy-pod>

# 在节点上看 iptables 规则
sudo iptables -t nat -S | grep hostnames

如果使用了 NetworkPolicy,还要检查是否允许 Pod 间访问或从某命名空间访问

网站总访客数:Loading
网站总访问量:Loading
使用 Hugo 构建
主题 StackJimmy 设计