修復一個kubernetes叢集

charlieroro發表於2024-10-22

前幾天有個朋友把他們的kubernetes叢集搞掛了,讓我幫忙恢復一下,由於很多現場都沒有了,這裡提供一下解決思路。

環境問題

該環境有一個master節點,即控制面pod(etcd、scheduler等)也都只有一個pod

問題起因是他們的服務訪問出了點問題,為修復該問題,他:

  • 備份了etcd資料(資料是3天前的)
  • 重啟了docker
  • 將etcd資料恢復(資料是3天前的)

然後訪問服務依然不通。

問題診斷

deployment reversion不匹配

首先看到pod並非running狀態,直接刪除pod,讓其重建,檢視pod建立過程,發現pod並沒有分配到node。

問題分析

首先懷疑可能kube-scheduler出現了問題:

  1. 刪除kube-scheduler 發現無法重建該pod
  2. 最後透過將/etc/kubernetes/manifests/kube-scheduler.yaml檔案移出再移入的方式建立出scheduler pod

此時仍然無法排程pod,因此懷疑是在scheduler之前出現了問題,檢視api-server的日誌,發現有很多reversion版本不匹配的錯誤,應該是叢集中的資源版本和etcd中的資源版本不匹配導致的:

  1. 使用etctctl檢查etcd的狀態,發現etcd一切正常

    etcdctl endpoint health
    etcdctl endpoint status --write-out=table
    
  2. 使用kubectl rollout history deployment/<deployment_name>檢視etcd中儲存的的deployment的版本,然後執行kubectl rollout undo daemonset/<deployment_name> --to-revision=<version>回滾到與etcd匹配的版本。

    回滾之前可以透過kubectl rollout history daemonset/<deployment_name> --revision=<version>對比etcd和環境中的配置區別

  3. 回滾之後發現pod可以正常建立出來

Iptables丟失問題

pod起來之後,服務訪問仍然不通。使用kubectl describe命令檢視服務的service,發現沒有找到service對應的endpoints,一開始還以為是service的yaml的問題,debug了大半天發現絕大部分services都沒有endpoints。。。

問題分析

service找不到endpoints,體現在系統中就是可能沒有建立出iptables規則:

  1. 使用iptables-save命令檢視,發現果然沒有kubernetes的iptables規則
  2. 該環境使用的是ipvs模式,使用ipvsadm -l -n也發現service的cluster IP沒有對應的pod IP
  3. 檢視kube-proxy日誌,並未發現任何異常

此時想到的方式有:

  1. 重新建立pod和對應的service,重新整理iptables:嘗試失敗,重建之後並未生成iptables
  2. 重建節點:所有節點都存在問題,無法透過kubectl drain遷移pod
  3. 手動新增iptables:太過複雜,即便成功,也會汙染節點的iptables規則。
  4. 重新建立kube-proxy pod:重啟kube-proxy pod之後也並未建立iptables規則

最後懷疑kube-proxy也可能出現問題,需要重新初始化kube-proxy,恰好kubeadm有如下命令可以重新初始化kube-proxy:

kubeadm init phase addon kube-proxy --kubeconfig ~/.kube/config --apiserver-advertise-address <api-server-ip>

在重新初始化kube-proxy之後發現iptables規則建立成功,刪除並建立pod和service之後可以正確建立出對應的iptables規則,此時service也有了endpoints。

CNI連線錯誤

在上一步重啟pod之後,發現有一個webhook對應的pod沒有重啟成功,用kubectl describe 該pod發現如下錯誤:

networkPlugin cni failed to set up pod "webhook-1" network: Get "https://[10.233.0.1]:443/api/v1/namespaces/volcano-system": dial tcp 10.233.0.1:443: i/o timeout

該叢集使用的是calico CNI,檢視該CNI對應的daemonset,發現只有5個pod是ready的。

刪除"webhook-1" pod所在的節點的"calico-node" pod,發現該"calico-node" pod啟動失敗。

問題分析

在上述錯誤中,"10.233.0.1"為kubernetes apiserver的service cluster IP,由於"clico-node" pod使用的是hostnetwork,因此可以直接在node上測試聯通性,使用telnet 10.233.0.1 443測試,發現果然不通。

calico的/etc/cni/net.d/10-calico.conflist配置檔案中定義了連線apiserver所需的kubeconfig檔案:

{
  "name": "cni0",
  "cniVersion":"0.3.1",
  "plugins":[
    {
      ... 
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    ...
  ]
}

/etc/cni/net.d/calico-kubeconfig中就定義了連線apiserver所需的地址和埠,因此只需將該地址埠換成apiserver pod的地址和埠應該就可以解決該問題:

# cat /etc/cni/net.d/calico-kubeconfig
# Kubeconfig file for Calico CNI plugin.
apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    server: https://[10.233.0.1]:443
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0...
users:
- name: calico
  user:
    token: eyJhbGciOiJSUzI1NiIsImtpZC...
contexts:
- name: calico-context
  context:
    cluster: local
    user: calico

Calico提供瞭如下兩個環境變數用於修改生成的kubeconfig中的apiserver的地址和埠,將如下環境變數加入calico的daemonset,重新建立calico-node pod即可:

- name: KUBERNETES_SERVICE_HOST
  value: <api-server-pod-ip>
- name: KUBERNETES_SERVICE_PORT
  value: <api-server-pod-port>

至此,問題基本解決。由於錯誤的操作,該叢集出現了大量問題,後續可以透過驅逐節點pod的方式,重新初始化整個節點,逐步重置叢集節點配置。

相關文章