kubeadm 故障排除

與任何程式一樣,在安裝或執行 kubeadm 時可能會遇到錯誤。本頁面列出了一些常見的故障場景,並提供了可以幫助你理解和解決問題的步驟。

如果你的問題未列在下面,請按照以下步驟操作

  • 如果你認為你的問題是 kubeadm 的一個 bug

  • 如果你不確定 kubeadm 的工作原理,可以在 Slack#kubeadm 頻道提問,或者在 StackOverflow 上提出問題。請包含相關標籤,如 #kubernetes#kubeadm,以便大家幫助你。

由於缺少 RBAC,無法將 v1.18 節點加入 v1.17 叢集

在 v1.18 中,kubeadm 增加了防止將同名節點加入叢集的功能。這需要為 bootstrap-token 使用者新增 RBAC 才能獲取節點物件。

然而,這導致了 v1.18 的 kubeadm join 無法加入由 kubeadm v1.17 建立的叢集的問題。

要解決此問題,你有兩種選擇

在控制平面節點上使用 kubeadm v1.18 執行 kubeadm init phase bootstrap-token。請注意,這也會啟用其餘的 bootstrap-token 許可權。

或者

使用 kubectl apply -f ... 手動應用以下 RBAC

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubeadm:get-nodes
rules:
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubeadm:get-nodes
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubeadm:get-nodes
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: Group
    name: system:bootstrappers:kubeadm:default-node-token

安裝期間未找到 ebtables 或類似的可執行檔案

如果在執行 kubeadm init 時看到以下警告

[preflight] WARNING: ebtables not found in system path
[preflight] WARNING: ethtool not found in system path

那麼你的節點上可能缺少 ebtablesethtool 或類似的可執行檔案。你可以使用以下命令安裝它們

  • 對於 Ubuntu/Debian 使用者,執行 apt install ebtables ethtool
  • 對於 CentOS/Fedora 使用者,執行 yum install ebtables ethtool

安裝期間 kubeadm 阻塞等待控制平面

如果你注意到 kubeadm init 在打印出以下行後掛起

[apiclient] Created API client, waiting for the control plane to become ready

這可能是由多種問題引起的。最常見的原因是

  • 網路連線問題。在繼續之前,請檢查你的機器是否具有完整的網路連線。
  • 容器執行時的 cgroup 驅動與 kubelet 的 cgroup 驅動不同。要了解如何正確配置它,請參閱 配置 cgroup 驅動
  • 控制平面容器處於崩潰迴圈或掛起狀態。你可以透過執行 docker ps 並透過執行 docker logs 檢查每個容器來檢查這一點。對於其他容器執行時,請參閱 使用 crictl 除錯 Kubernetes 節點

kubeadm 在移除受管容器時阻塞

如果容器執行時停止且未移除任何 Kubernetes 管理的容器,則可能發生以下情況

sudo kubeadm reset
[preflight] Running pre-flight checks
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
[reset] Removing kubernetes-managed containers
(block)

一個可能的解決方案是重新啟動容器執行時,然後重新執行 kubeadm reset。你還可以使用 crictl 除錯容器執行時的狀態。請參閱 使用 crictl 除錯 Kubernetes 節點

Pod 處於 RunContainerErrorCrashLoopBackOffError 狀態

kubeadm init 之後,不應有任何 Pod 處於這些狀態。

  • 如果在 kubeadm init 之後 立即 有 Pod 處於這些狀態之一,請在 kubeadm 倉庫中提出問題。coredns(或 kube-dns)應處於 Pending 狀態,直到你部署網路附加元件。
  • 如果你在部署網路附加元件後看到 Pod 處於 RunContainerErrorCrashLoopBackOffError 狀態,並且 coredns(或 kube-dns)沒有發生任何變化,那麼你安裝的 Pod 網路附加元件很可能存在某種問題。你可能需要授予它更多 RBAC 許可權或使用更新的版本。請在 Pod 網路提供商的問題跟蹤器中提出問題,並在那裡對問題進行分類。

coredns 卡在 Pending 狀態

這是 **預期** 的,也是設計的一部分。kubeadm 不依賴於特定的網路提供商,因此管理員應 安裝選擇的 Pod 網路附加元件。你必須安裝 Pod 網路,CoreDNS 才能完全部署。因此,在網路設定之前,它會處於 Pending 狀態。

HostPort 服務不起作用

HostPortHostIP 功能是否可用取決於你的 Pod 網路提供商。請聯絡 Pod 網路附加元件的作者,以瞭解 HostPortHostIP 功能是否可用。

Calico、Canal 和 Flannel CNI 提供商已驗證支援 HostPort。

有關更多資訊,請參閱 CNI portmap 文件

如果你的網路提供商不支援 portmap CNI 外掛,你可能需要使用 服務的 NodePort 功能 或使用 HostNetwork=true

Pod 無法透過其 Service IP 訪問

  • 許多網路附加元件尚未啟用 髮夾模式 (hairpin mode),該模式允許 Pod 透過其 Service IP 訪問自身。這是與 CNI 相關的問題。請聯絡網路附加元件提供商,以獲取其對髮夾模式支援的最新狀態。

  • 如果你正在使用 VirtualBox(直接或透過 Vagrant),你需要確保 hostname -i 返回一個可路由的 IP 地址。預設情況下,第一個介面連線到不可路由的僅主機網路。一種解決方法是修改 /etc/hosts,請參閱此 Vagrantfile 獲取示例。

TLS 證書錯誤

以下錯誤表示可能的證書不匹配。

# kubectl get pods
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
  • 驗證 $HOME/.kube/config 檔案是否包含有效證書,如有必要,重新生成證書。kubeconfig 檔案中的證書經過 base64 編碼。可以使用 base64 --decode 命令解碼證書,並使用 openssl x509 -text -noout 檢視證書資訊。

  • 使用以下命令取消設定 KUBECONFIG 環境變數

    unset KUBECONFIG
    

    或者將其設定為預設的 KUBECONFIG 位置

    export KUBECONFIG=/etc/kubernetes/admin.conf
    
  • 另一種解決方法是覆蓋 "admin" 使用者的現有 kubeconfig

    mv $HOME/.kube $HOME/.kube.bak
    mkdir $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    

Kubelet 客戶端證書輪換失敗

預設情況下,kubeadm 使用 /etc/kubernetes/kubelet.conf 中指定的 /var/lib/kubelet/pki/kubelet-client-current.pem 符號連結配置 kubelet,以實現客戶端證書的自動輪換。如果此輪換過程失敗,你可能會在 kube-apiserver 日誌中看到諸如 x509: certificate has expired or is not yet valid 之類的錯誤。要解決此問題,你必須遵循以下步驟

  1. 備份並刪除故障節點上的 /etc/kubernetes/kubelet.conf/var/lib/kubelet/pki/kubelet-client*

  2. 在叢集中一個包含 /etc/kubernetes/pki/ca.key 的工作控制平面節點上,執行 kubeadm kubeconfig user --org system:nodes --client-name system:node:$NODE > kubelet.conf$NODE 必須設定為叢集中現有故障節點的名稱。手動修改生成的 kubelet.conf 以調整叢集名稱和伺服器端點,或者傳遞 kubeconfig user --config (請參閱 為其他使用者生成 kubeconfig 檔案)。如果你的叢集沒有 ca.key,你必須在外部簽署 kubelet.conf 中的嵌入式證書。

  3. 將此生成的 kubelet.conf 複製到故障節點上的 /etc/kubernetes/kubelet.conf

  4. 在故障節點上重啟 kubelet (systemctl restart kubelet),並等待 /var/lib/kubelet/pki/kubelet-client-current.pem 被重新建立。

  5. 手動編輯 kubelet.conf,透過替換 client-certificate-dataclient-key-data 來指向輪換後的 kubelet 客戶端證書

    client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
    client-key: /var/lib/kubelet/pki/kubelet-client-current.pem
    
  6. 重新啟動 kubelet。

  7. 確保節點變為 Ready

在 Vagrant 中使用 flannel 作為 Pod 網路時的預設 NIC

以下錯誤可能表明 Pod 網路中存在問題

Error from server (NotFound): the server could not find the requested resource
  • 如果你在 Vagrant 中使用 flannel 作為 Pod 網路,則必須為 flannel 指定預設介面名稱。

    Vagrant 通常為所有虛擬機器分配兩個介面。第一個介面,所有主機都分配了 IP 地址 10.0.2.15,用於經過 NAT 的外部流量。

    這可能會導致 flannel 出現問題,flannel 預設使用主機上的第一個介面。這導致所有主機都認為它們具有相同的公共 IP 地址。為防止這種情況,請將 --iface eth1 標誌傳遞給 flannel,以便選擇第二個介面。

容器使用的非公共 IP

在某些情況下,在功能正常的叢集中,kubectl logskubectl run 命令可能會返回以下錯誤

Error from server: Get https://10.19.0.41:10250/containerLogs/default/mysql-ddc65b868-glc5m/mysql: dial tcp 10.19.0.41:10250: getsockopt: no route to host
  • 這可能是由於 Kubernetes 使用的 IP 無法與同一子網上的其他 IP 通訊,這可能是由機器提供商的策略造成的。

  • DigitalOcean 將一個公共 IP 分配給 eth0,同時將一個私有 IP 分配給內部使用,作為其浮動 IP 功能的錨點,但 kubelet 會選擇後者作為節點的 InternalIP,而不是公共 IP。

    使用 ip addr show 而不是 ifconfig 來檢查此情況,因為 ifconfig 不會顯示有問題的別名 IP 地址。或者,DigitalOcean 特定的 API 端點允許從 droplet 查詢錨點 IP

    curl http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address
    

    解決方法是使用 --node-ip 告訴 kubelet 使用哪個 IP。使用 DigitalOcean 時,它可以是公共 IP(分配給 eth0)或私有 IP(分配給 eth1),如果你想使用可選的私有網路。kubeadm NodeRegistrationOptions 結構kubeletExtraArgs 部分可以用於此目的。

    然後重啟 kubelet

    systemctl daemon-reload
    systemctl restart kubelet
    

coredns Pod 處於 CrashLoopBackOffError 狀態

如果你的節點執行的是帶有舊版本 Docker 的 SELinux,你可能會遇到 coredns Pod 未啟動的情況。要解決此問題,你可以嘗試以下選項之一

kubectl -n kube-system get deployment coredns -o yaml | \
  sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | \
  kubectl apply -f -

CoreDNS 出現 CrashLoopBackOff 的另一個原因是當部署在 Kubernetes 中的 CoreDNS Pod 檢測到迴圈時。有多種解決方法 可以避免 Kubernetes 在 CoreDNS 每次檢測到迴圈並退出時都嘗試重新啟動 CoreDNS Pod。

etcd Pods 持續重啟

如果你遇到以下錯誤

rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "process_linux.go:110: decoding init error from pipe caused \"read parent: connection reset by peer\""

如果你在 CentOS 7 上執行 Docker 1.13.1.84,則會出現此問題。此版本的 Docker 可能會阻止 kubelet 執行到 etcd 容器中。

要解決此問題,請選擇以下選項之一

  • 回滾到早期版本的 Docker,例如 1.13.1-75

    yum downgrade docker-1.13.1-75.git8633870.el7.centos.x86_64 docker-client-1.13.1-75.git8633870.el7.centos.x86_64 docker-common-1.13.1-75.git8633870.el7.centos.x86_64
    
  • 安裝一個更近期的推薦版本,例如 18.06

    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum install docker-ce-18.06.1.ce-3.el7.x86_64
    

無法將逗號分隔的值列表傳遞給 --component-extra-args 標誌中的引數

kubeadm init 標誌,例如 --component-extra-args 允許你將自定義引數傳遞給控制平面元件,例如 kube-apiserver。但是,此機制受到用於解析值的底層型別 (mapStringString) 的限制。

如果你決定傳遞一個支援多個逗號分隔值的引數,例如 --apiserver-extra-args "enable-admission-plugins=LimitRanger,NamespaceExists",此標誌將失敗並顯示 flag: malformed pair, expect string=string。發生這種情況是因為 --apiserver-extra-args 的引數列表需要 key=value 對,在這種情況下,NamespacesExists 被視為缺少值的鍵。

或者,你可以嘗試像這樣分隔 key=value 對:--apiserver-extra-args "enable-admission-plugins=LimitRanger,enable-admission-plugins=NamespaceExists",但這將導致鍵 enable-admission-plugins 僅具有 NamespaceExists 的值。

一個已知的解決方法是使用 kubeadm 配置檔案

kube-proxy 在節點由 cloud-controller-manager 初始化之前排程

在雲提供商場景中,kube-proxy 可能會在 cloud-controller-manager 初始化節點地址之前在新工作節點上排程。這會導致 kube-proxy 無法正確獲取節點的 IP 地址,並對管理負載均衡器的代理功能產生連鎖反應。

在 kube-proxy Pods 中可以看到以下錯誤

server.go:610] Failed to retrieve node IP: host IP unknown; known addresses: []
proxier.go:340] invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP

一個已知的解決方案是修補 kube-proxy DaemonSet,使其可以在控制平面節點上排程,無論其條件如何,並在其他節點的初始防護條件緩解之前將其保持在其他節點之外

kubectl -n kube-system patch ds kube-proxy -p='{
  "spec": {
    "template": {
      "spec": {
        "tolerations": [
          {
            "key": "CriticalAddonsOnly",
            "operator": "Exists"
          },
          {
            "effect": "NoSchedule",
            "key": "node-role.kubernetes.io/control-plane"
          }
        ]
      }
    }
  }
}'

此問題的跟蹤問題位於 此處

/usr 在節點上掛載為只讀

在 Fedora CoreOS 或 Flatcar Container Linux 等 Linux 發行版上,目錄 /usr 作為只讀檔案系統掛載。對於 FlexVolume 支援,Kubernetes 元件(例如 kubelet 和 kube-controller-manager)使用 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ 的預設路徑,但 FlexVolume 目錄 必須可寫 才能使此功能正常工作。

要解決此問題,你可以使用 kubeadm 配置檔案 配置 FlexVolume 目錄。

在主控制平面節點(使用 kubeadm init 建立)上,使用 --config 傳遞以下檔案

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
nodeRegistration:
  kubeletExtraArgs:
  - name: "volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
controllerManager:
  extraArgs:
  - name: "flex-volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

在加入節點上

apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration
nodeRegistration:
  kubeletExtraArgs:
  - name: "volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

或者,你可以修改 /etc/fstab 使 /usr 掛載可寫,但請注意,這會修改 Linux 發行版的設計原則。

kubeadm upgrade plan 打印出 context deadline exceeded 錯誤訊息

當使用 kubeadm 升級帶有外部 etcd 的 Kubernetes 叢集時,會顯示此錯誤訊息。這不是一個關鍵錯誤,它發生是因為舊版本的 kubeadm 會對外部 etcd 叢集執行版本檢查。你可以繼續執行 kubeadm upgrade apply ...

此問題已在 1.19 版本中修復。

kubeadm reset 解除安裝 /var/lib/kubelet

如果 /var/lib/kubelet 已掛載,執行 kubeadm reset 將有效解除安裝它。

為了解決這個問題,在執行 kubeadm reset 操作後,重新掛載 /var/lib/kubelet 目錄。

這是 kubeadm 1.15 中引入的一個迴歸。此問題已在 1.20 中修復。

無法在 kubeadm 叢集中安全地使用 metrics-server

在 kubeadm 叢集中,可以透過向 metrics-server 傳遞 --kubelet-insecure-tls 來不安全地使用它。不建議在生產叢集中這樣做。

如果你想在 metrics-server 和 kubelet 之間使用 TLS,就會出現問題,因為 kubeadm 為 kubelet 部署了自簽名的服務證書。這可能會導致 metrics-server 端出現以下錯誤

x509: certificate signed by unknown authority
x509: certificate is valid for IP-foo not IP-bar

請參閱 啟用已簽名 kubelet 服務證書 以瞭解如何在 kubeadm 叢集中配置 kubelet 以獲得正確簽名的服務證書。

另請參閱 如何安全執行 metrics-server

由於 etcd 雜湊未更改而升級失敗

僅適用於使用 kubeadm 二進位制檔案 v1.28.3 或更高版本升級控制平面節點,其中該節點當前由 kubeadm 版本 v1.28.0、v1.28.1 或 v1.28.2 管理。

你可能會遇到以下錯誤訊息

[upgrade/etcd] Failed to upgrade etcd: couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced: static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
[upgrade/etcd] Waiting for previous etcd to become available
I0907 10:10:09.109104    3704 etcd.go:588] [etcd] attempting to see if all cluster endpoints ([https://172.17.0.6:2379/ https://172.17.0.4:2379/ https://172.17.0.3:2379/]) are available 1/10
[upgrade/etcd] Etcd was rolled back and is now available
static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.rollbackOldManifests
	cmd/kubeadm/app/phases/upgrade/staticpods.go:525
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.upgradeComponent
	cmd/kubeadm/app/phases/upgrade/staticpods.go:254
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.performEtcdStaticPodUpgrade
	cmd/kubeadm/app/phases/upgrade/staticpods.go:338
...

此故障的原因是受影響的版本生成的 etcd 清單檔案在 PodSpec 中包含不需要的預設值。這將導致與清單比較的差異,kubeadm 將期望 Pod 雜湊發生更改,但 kubelet 永遠不會更新雜湊。

如果你的叢集中出現此問題,有兩種解決方法

  • 可以使用以下命令跳過受影響版本和 v1.28.3(或更高版本)之間的 etcd 升級

    kubeadm upgrade {apply|node} [version] --etcd-upgrade=false
    

    如果更新的 v1.28 補丁版本引入了新的 etcd 版本,則不建議這樣做。

  • 在升級之前,修補 etcd 靜態 Pod 的清單,以刪除有問題的預設屬性

    diff --git a/etc/kubernetes/manifests/etcd_defaults.yaml b/etc/kubernetes/manifests/etcd_origin.yaml
    index d807ccbe0aa..46b35f00e15 100644
    --- a/etc/kubernetes/manifests/etcd_defaults.yaml
    +++ b/etc/kubernetes/manifests/etcd_origin.yaml
    @@ -43,7 +43,6 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
        name: etcd
        resources:
    @@ -59,26 +58,18 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
    -    terminationMessagePath: /dev/termination-log
    -    terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /var/lib/etcd
          name: etcd-data
        - mountPath: /etc/kubernetes/pki/etcd
          name: etcd-certs
    -  dnsPolicy: ClusterFirst
    -  enableServiceLinks: true
      hostNetwork: true
      priority: 2000001000
      priorityClassName: system-node-critical
    -  restartPolicy: Always
    -  schedulerName: default-scheduler
      securityContext:
        seccompProfile:
          type: RuntimeDefault
    -  terminationGracePeriodSeconds: 30
      volumes:
      - hostPath:
          path: /etc/kubernetes/pki/etcd
    

有關此 bug 的更多資訊,請參見 跟蹤問題

上次修改時間:2024 年 7 月 5 日 下午 4:06 PST:kubeadm: 在所有文件示例中使用 v1beta4 (efc1133fa4)