kubeadm 疑難排解

如同任何程式一樣,您在安裝或執行 kubeadm 時可能會遇到錯誤。本頁列出了一些常見的失敗情境,並提供步驟來協助您理解和修正問題。

如果您的問題未在下方列出,請依照下列步驟操作

  • 如果您認為您的問題是 kubeadm 的錯誤

  • 如果您不確定 kubeadm 的運作方式,您可以在 Slack#kubeadm 頻道中提問,或在 StackOverflow 上開啟一個問題。請包含相關標籤,例如 #kubernetes#kubeadm,以便大家可以協助您。

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

在 v1.18 中,kubeadm 新增了防止將節點加入叢集的機制,如果叢集中已存在同名的節點。這需要為 bootstrap-token 使用者新增 RBAC,使其能夠 GET 節點物件。

然而,這會導致一個問題,即來自 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 驅動程式
  • 控制平面容器正在 CrashLoopBackOff 或卡住。您可以執行 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 repo 中開啟一個 issue。在您部署網路附加元件之前,coredns (或 kube-dns) 應該處於 Pending 狀態。
  • 如果您在部署網路附加元件後看到 Pod 處於 RunContainerErrorCrashLoopBackOffError 狀態,且 coredns (或 kube-dns) 沒有任何反應,則很可能是您安裝的 Pod 網路附加元件在某方面已損壞。您可能需要授予它更多 RBAC 權限或使用較新版本。請在 Pod 網路供應商的 issue 追蹤器中提交 issue,並在那裡對 issue 進行分類。

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 存取

  • 許多網路附加元件尚未啟用髮夾模式,該模式允許 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 以指向輪換後的 kubelet 用戶端憑證,方法是將 client-certificate-dataclient-key-data 替換為

    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 通常會為所有 VM 分配兩個介面。第一個介面(所有主機都被分配 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 為 eth0 分配了一個公用 IP,也分配了一個私有 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
    

    解決方法是告知 kubelet 要使用哪個 IP,方法是使用 --node-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 Pod 持續重新啟動

如果您遇到以下錯誤

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 位址,並對管理負載平衡器的 Proxy 功能產生連鎖反應。

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

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"
          }
        ]
      }
    }
  }
}'

此問題的追蹤 issue 在這裡

/usr 在節點上以唯讀方式掛載

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

為了解決此問題,您可以使用 kubeadm 組態檔來設定 flex-volume 目錄。

在主要控制平面節點(使用 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 的 Kubernetes 叢集(在執行外部 etcd 的情況下)時,會顯示此錯誤訊息。這不是嚴重的錯誤,發生原因是舊版 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 叢集中,可以透過將 --kubelet-insecure-tls 傳遞給 metrics-server 來不安全地使用它。不建議在生產叢集中使用此方法。

如果您想在 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
...

此失敗的原因是受影響的版本在 PodSpec 中產生具有非預期預設值的 etcd manifest 檔案。這將導致 manifest 比較產生差異,且 kubeadm 會預期 Pod 雜湊值發生變更,但 kubelet 永遠不會更新雜湊值。

如果您在叢集中遇到此問題,有兩種方法可以解決

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

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

    如果後續的 v1.28 修補程式版本引入了新的 etcd 版本,則不建議這樣做。

  • 在升級之前,修補 etcd 靜態 Pod 的 manifest 檔案,以移除有問題的預設屬性

    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
    

更多資訊可以在此錯誤的追蹤 issue 中找到。

上次修改時間:2024 年 7 月 5 日下午 4:06 PST:kubeadm: use v1beta4 in all docs examples (efc1133fa4)