排查集群狀態異常問題通常從 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 狀態
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):
# 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>
$ 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 登錄到機器上,然后查看具體的日志文件。如
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 日志
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 日志
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 日志
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 日志
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 上。
journalctl -l -u kubelet
Kube-proxy 日志
Kube-proxy 通常以 DaemonSet 的方式部署
$ 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 的日志
$ 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
可以發現如下的錯誤日志
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 超時,比如
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。
-
磁盤空間不足:清理磁盤空間,比如鏡像、臨時文件等。