k8s集群排错指南
排查集群状态异常问题通常从 Node 和 Kubernetes 服务 的状态出发,常见的有
-
kube-apiserver 无法启动会导致
-
集群不可访问
-
已有的 Pod 和服务正常运行(依赖于 Kubernetes API 的除外)
-
-
etcd 集群异常会导致
-
kube-apiserver 无法正常读写集群状态,进而导致 Kubernetes API 访问出错
-
kubelet 无法周期性更新状态
-
-
kube-controller-manager/kube-scheduler 异常会导致
-
复制控制器、节点控制器、云服务控制器等无法工作,从而导致 Deployment、Service 等无法工作,也无法注册新的 Node 到集群中来
-
新创建的 Pod 无法调度(总是 Pending 状态)
-
-
Node 本身宕机或者 Kubelet 无法启动会导致
-
Node 上面的 Pod 无法正常运行
-
已在运行的 Pod 无法正常终止
-
-
网络分区会导致 Kubelet 等与控制平面通信异常以及 Pod 之间通信异常
查看 Node 状态
一般来说,可以首先查看 Node 的状态,确认 Node 本身是不是 Ready 状态
1 2 | kubectl get nodes kubectl describe node <node-name> |
如果是 NotReady 状态,则可以执行 kubectl describe node <node-name>
命令来查看当前 Node 的事件。这些事件通常都会有助于排查 Node 发生的问题。
SSH 登录 Node
在排查 Kubernetes 问题时,通常需要 SSH 登录到具体的 Node 上面查看 kubelet、docker、iptables 等的状态和日志。在使用云平台时,可以给相应的 VM 绑定一个公网 IP;而在物理机部署时,可以通过路由器上的端口映射来访问。但更简单的方法是使用 SSH Pod (不要忘记替换成你自己的 nodeName):
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 | # cat ssh.yaml apiVersion: v1 kind: Service metadata: name: ssh spec: selector: app: ssh type: LoadBalancer ports: - protocol: TCP port: 22 targetPort: 22 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: ssh labels: app: ssh spec: replicas: 1 selector: matchLabels: app: ssh template: metadata: labels: app: ssh spec: containers: - name: alpine image: alpine ports: - containerPort: 22 stdin: true tty: true hostNetwork: true nodeName: <node-name> |
1 2 3 4 | $ kubectl create -f ssh.yaml $ kubectl get svc ssh NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ssh LoadBalancer 10.0.99.149 52.52.52.52 22:32008/TCP 5m |
接着,就可以通过 ssh 服务的外网 IP 来登录 Node,如 ssh user@52.52.52.52
。
在使用完后, 不要忘记删除 SSH 服务 kubectl delete -f ssh.yaml
。
查看日志
一般来说,Kubernetes 的主要组件有两种部署方法
-
直接使用 systemd 等启动控制节点的各个服务
-
使用 Static Pod 来管理和启动控制节点的各个服务
使用 systemd 等管理控制节点服务时,查看日志必须要首先 SSH 登录到机器上,然后查看具体的日志文件。如
1 2 3 4 5 | journalctl -l -u kube-apiserver journalctl -l -u kube-controller-manager journalctl -l -u kube-scheduler journalctl -l -u kubelet journalctl -l -u kube-proxy |
或者直接查看日志文件
-
/var/log/kube-apiserver.log
-
/var/log/kube-scheduler.log
-
/var/log/kube-controller-manager.log
-
/var/log/kubelet.log
-
/var/log/kube-proxy.log
而对于使用 Static Pod 部署集群控制平面服务的场景,可以参考下面这些查看日志的方法。
kube-apiserver 日志
1 2 | PODNAME=$(kubectl -n kube-system get pod -l component=kube-apiserver -o jsonpath= '{.items[0].metadata.name}' ) kubectl -n kube-system logs $PODNAME --tail 100 |
kube-controller-manager 日志
1 2 | PODNAME=$(kubectl -n kube-system get pod -l component=kube-controller-manager -o jsonpath= '{.items[0].metadata.name}' ) kubectl -n kube-system logs $PODNAME --tail 100 |
kube-scheduler 日志
1 2 | PODNAME=$(kubectl -n kube-system get pod -l component=kube-scheduler -o jsonpath= '{.items[0].metadata.name}' ) kubectl -n kube-system logs $PODNAME --tail 100 |
kube-dns 日志
1 2 | PODNAME=$(kubectl -n kube-system get pod -l k8s-app=kube-dns -o jsonpath= '{.items[0].metadata.name}' ) kubectl -n kube-system logs $PODNAME -c kubedns |
Kubelet 日志
查看 Kubelet 日志需要首先 SSH 登录到 Node 上。
1 | journalctl -l -u kubelet |
Kube-proxy 日志
Kube-proxy 通常以 DaemonSet 的方式部署
1 2 3 4 5 6 | $ kubectl -n kube-system get pod -l component=kube-proxy NAME READY STATUS RESTARTS AGE kube-proxy-42zpn 1/1 Running 0 1d kube-proxy-7gd4p 1/1 Running 0 3d kube-proxy-87dbs 1/1 Running 0 4d $ kubectl -n kube-system logs kube-proxy-42zpn |
Kube-dns/Dashboard CrashLoopBackOff
由于 Dashboard 依赖于 kube-dns,所以这个问题一般是由于 kube-dns 无法正常启动导致的。查看 kube-dns 的日志
1 2 3 | $ kubectl logs -- namespace =kube-system $(kubectl get pods -- namespace =kube-system -l k8s-app=kube-dns -o name) -c kubedns $ kubectl logs -- namespace =kube-system $(kubectl get pods -- namespace =kube-system -l k8s-app=kube-dns -o name) -c dnsmasq $ kubectl logs -- namespace =kube-system $(kubectl get pods -- namespace =kube-system -l k8s-app=kube-dns -o name) -c sidecar |
可以发现如下的错误日志
1 2 3 | Waiting for services and endpoints to be initialized from apiserver... skydns: failure to forward request "read udp 10.240.0.18:47848->168.63.129.16:53: i/o timeout" Timeout waiting for initialization |
这说明 kube-dns pod 无法转发 DNS 请求到上游 DNS 服务器。解决方法为
-
如果使用的 Docker 版本大于 1.12,则在每个 Node 上面运行
iptables -P FORWARD ACCEPT
开启 Docker 容器的 IP 转发 -
等待一段时间,如果还未恢复,则检查 Node 网络是否正确配置,比如是否可以正常请求上游DNS服务器、是否开启了 IP 转发(包括 Node 内部和公有云上虚拟网卡等)、是否有安全组禁止了 DNS 请求等
如果错误日志中不是转发 DNS 请求超时,而是访问 kube-apiserver 超时,比如
1 2 3 4 5 6 | E0122 06:56:04.774977 1 reflector.go:199] k8s.io/dns/vendor/k8s.io/client-go/tools/cache/reflector.go:94: Failed to list *v1.Endpoints: Get https: //10.0.0.1:443/api/v1/endpoints?resourceVersion=0: dial tcp 10.0.0.1:443: i/o timeout I0122 06:56:04.775358 1 dns.go:174] Waiting for services and endpoints to be initialized from apiserver... E0122 06:56:04.775574 1 reflector.go:199] k8s.io/dns/vendor/k8s.io/client-go/tools/cache/reflector.go:94: Failed to list *v1.Service: Get https: //10.0.0.1:443/api/v1/services?resourceVersion=0: dial tcp 10.0.0.1:443: i/o timeout I0122 06:56:05.275295 1 dns.go:174] Waiting for services and endpoints to be initialized from apiserver... I0122 06:56:05.775182 1 dns.go:174] Waiting for services and endpoints to be initialized from apiserver... I0122 06:56:06.275288 1 dns.go:174] Waiting for services and endpoints to be initialized from apiserver... |
这说明 Pod 网络(一般是多主机之间)访问异常,包括 Pod->Node、Node->Pod 以及 Node-Node 等之间的往来通信异常。可能的原因比较多,具体的排错方法可以参考
Node NotReady
Node 处于 NotReady 状态,大部分是由于 PLEG(Pod Lifecycle Event Generator)问题导致的。社区 issue #45419 目前还处于未解决状态。
NotReady 的原因比较多,在排查时最重要的就是执行 kubectl describe node <node name>
并查看 Kubelet 日志中的错误信息。常见的问题及修复方法为:
-
Kubelet 未启动或者异常挂起:重新启动 Kubelet。
-
CNI 网络插件未部署:部署 CNI 插件。
-
Docker 僵死(API 不响应):重启 Docker。
-
磁盘空间不足:清理磁盘空间,比如镜像、临时文件等。