排解容器執行階段問題


本文提供疑難排解步驟,協助您解決 Google Kubernetes Engine (GKE) 節點上容器執行階段的常見問題。

在採用 containerd 的 Windows 節點集上,使用簡單磁碟機代號掛接路徑會失敗

這個問題已在 containerd 1.6.6 以上版本中解決。

如果 GKE 叢集執行的 Windows Server 節點集區使用 1.6.6 之前的 containerd 執行階段,啟動容器時可能會發生下列錯誤:

failed to create containerd task : CreateComputeSystem : The parameter is incorrect : unknown

詳情請參閱 GitHub 問題 #6589

解決方案

如要解決這個問題,請將節點集區升級至最新 GKE 版本,該版本使用 containerd 執行階段 1.6.6 以上版本。

在 Windows 節點集區中,如果容器映像檔的預先逸出 CMDENTRYPOINT 指令列不是陣列,就會導致 containerd 失敗

這個問題已在 containerd 1.6 以上版本中解決。

如果 GKE 叢集執行的 Windows Server 節點集區使用 containerd 執行階段 1.5.X,啟動容器時可能會發生錯誤,例如:

failed to start containerd task : hcs::System::CreateProcess : The system cannot find the file specified.: unknown

詳情請參閱 GitHub 問題 #5067GitHub 問題 #6300

解決方案

如要解決這個問題,請將節點集區升級至最新 GKE 版本,該版本使用 containerd 執行階段 1.6.6 以上版本。

在 Windows 節點集區中,如果容器映像檔磁碟區的路徑不存在或類似 Linux (正斜線),則會導致 containerd 失敗

這個問題已在 containerd 1.6 以上版本中解決。

如果 GKE 叢集執行的 Windows Server 節點集區使用 containerd 執行階段 1.5.X,啟動容器時可能會發生錯誤,例如:

failed to generate spec: failed to stat "<volume_path>": CreateFile : The system cannot find the path specified.

詳情請參閱 GitHub 問題 #5671

解決方案

如要解決這個問題,請將節點集區升級至最新 GKE 版本,使用 containerd 執行階段 1.6.x 版以上。

/etc/mtab:找不到你所指定的檔案或目錄

Docker 容器執行階段預設會在容器內填入這個符號連結,但 containerd 執行階段不會。

詳情請參閱 GitHub 問題 #2419

解決方案

如要解決這個問題,請在建構映像檔時手動建立符號連結 /etc/mtab

ln -sf /proc/mounts /etc/mtab

映像檔提取錯誤:不是目錄

受影響的 GKE 版本:所有版本

使用 kaniko 建構映像檔時,可能無法使用 containerd 提取映像檔,並顯示「not a directory」錯誤訊息。如果映像檔是以特殊方式建構,就會發生這項錯誤:當先前的指令移除目錄,而下一個指令在該目錄中重新建立相同檔案時。

以下 Dockerfile 範例 (含 npm) 說明瞭這個問題。

RUN npm cache clean --force
RUN npm install

詳情請參閱 GitHub 問題 #4659

解決方案

如要解決這個問題,請使用 docker build 建構映像檔,這個映像檔不受此問題影響。

如果無法使用 docker build,請將指令合併為一個。 下列 Dockerfile 範例會合併 RUN npm cache clean --forceRUN npm install

RUN npm cache clean --force && npm install

缺少部分檔案系統指標,且指標格式不同

受影響的 GKE 版本:所有版本

Kubelet /metrics/cadvisor 端點會提供 Prometheus 指標,如「Kubernetes 系統元件的指標」一文所述。如果安裝的指標收集器依賴該端點,可能會發生下列問題:

  • Docker 節點上的指標格式為 k8s_<container-name>_<pod-name>_<namespace>_<pod-uid>_<restart-count>,但 containerd 節點上的格式為 <container-id>
  • containerd 節點缺少部分檔案系統指標,如下所示:

    container_fs_inodes_free
    container_fs_inodes_total
    container_fs_io_current
    container_fs_io_time_seconds_total
    container_fs_io_time_weighted_seconds_total
    container_fs_limit_bytes
    container_fs_read_seconds_total
    container_fs_reads_merged_total
    container_fs_sector_reads_total
    container_fs_sector_writes_total
    container_fs_usage_bytes
    container_fs_write_seconds_total
    container_fs_writes_merged_total
    

解決方案

如要解決這個問題,可以使用 cAdvisor 做為獨立的 DaemonSet。

  1. 找到名稱模式為 vX.Y.Z-containerd-cri 的最新 cAdvisor 版本 (例如 v0.42.0-containerd-cri)。
  2. 請按照「cAdvisor Kubernetes Daemonset」中的步驟建立 Daemonset。
  3. 將已安裝的指標收集器指向 cAdvisor /metrics 端點,該端點會提供完整的 Prometheus 容器指標

替代方案

  1. 將監控解決方案遷移至 Cloud Monitoring,取得完整的容器指標。
  2. Kubelet 摘要 API (端點為 /stats/summary) 收集指標。

在 GKE Windows 上重新啟動容器執行階段後,以附加為基礎的作業無法正常運作

受影響的 GKE 版本:1.21 至 1.21.5-gke.1802、1.22 至 1.22.3-gke.700

如果強制重新啟動容器執行階段,使用 containerd 執行階段 (版本 1.5.4 和 1.5.7-gke.0) 的 Windows Server 節點集區執行的 GKE 叢集可能會發生問題,因為附加至現有執行中容器的作業無法再次繫結 IO。這個問題不會導致 API 呼叫失敗,但系統不會傳送或接收資料。這包括透過叢集 API 伺服器附加和記錄 CLI 與 API 的資料。

解決方案

如要解決這個問題,請升級至修補後的容器執行階段版本 (1.5.7-gke.1),並使用較新的 GKE 版本。

Pod 顯示 failed to allocate for range 0: no IP addresses available in range set 錯誤訊息

受影響的 GKE 版本:1.24.6-gke.1500 以下版本、1.23.14-gke.1800 以下版本,以及 1.22.16-gke.2000 以下版本

如果 GKE 叢集執行的節點集區使用 containerd,可能會發生 IP 位址洩漏問題,並耗盡節點上的所有 Pod IP 位址。排入受影響節點的 Pod 會顯示類似以下的錯誤訊息:

failed to allocate for range 0: no IP addresses available in range set: 10.48.131.1-10.48.131.62

如要進一步瞭解這個問題,請參閱 containerd GitHub 問題 #5438GitHub 問題 #5768

GKE Dataplane V2 有已知問題,可能會觸發這個問題。不過,其他原因也可能導致這個問題,包括 runc 停滯

解決方案

如要解決這個問題,請按照「標準 GKE 叢集的解決方法」一節的說明,為 GKE Dataplane V2 採取解決方法。

探測作業超過逾時時間時,執行探測作業的行為差異

受影響的 GKE 版本:所有版本

containerd 映像檔的執行探測行為與 dockershim 映像檔的行為不同。如果為 Pod 定義的 exec 探測超出宣告的 KubernetestimeoutSeconds dockershim 映像檔門檻,系統會將其視為探測失敗。在 containerd 映像檔中,系統會忽略聲明 timeoutSeconds 臨界值後傳回的探查結果。

解決方案

在 GKE 中,功能閘 ExecProbeTimeout 會設為 false,且無法變更。如要解決這個問題,請提高所有受影響執行探查的timeoutSeconds門檻,或在探查邏輯中實作逾時功能。

排解私人登錄檔問題

本節提供 containerd 中私有登錄設定的疑難排解資訊。

映像檔提取失敗,並顯示「x509: certificate signed by unknown authority」錯誤

如果 GKE 找不到特定私有登錄檔網域的憑證,就會發生這個問題。您可以使用下列查詢,在 Cloud Logging 中檢查這項錯誤:

  1. 前往 Google Cloud 控制台的「Logs Explorer」頁面:

    前往「Logs Explorer」頁面

  2. 執行以下查詢:

    ("Internal error pulling certificate" OR
    "Failed to get credentials from metadata server" OR
    "Failed to install certificate")
    

如要解決這個問題,請嘗試下列方法:

  1. 在 GKE Standard 中,開啟下列路徑中的設定檔:

    /etc/containerd/hosts.d/DOMAIN/config.toml
    

    DOMAIN 替換為登錄檔的 FQDN。

  2. 確認設定檔包含正確的 FQDN。

  3. 確認設定檔中 secretURI 欄位的憑證路徑是否正確。

  4. 確認憑證是否位於 Secret Manager 中。

未顯示憑證

如果 GKE 無法從 Secret Manager 提取憑證,在節點上設定 containerd,就會發生這個問題。

如要解決這個問題,請嘗試下列方法:

  1. 確認受影響的節點執行 Container-Optimized OS。 不支援 Ubuntu 和 Windows 節點。
  2. 在設定檔中,確認 secretURI 欄位中的密鑰路徑正確無誤。
  3. 確認叢集的 IAM 服務帳戶具備存取密鑰的正確權限
  4. 確認叢集具有 cloud-platform 存取權範圍。如需操作說明,請參閱「檢查存取範圍」。

未為本機網路 (10.0.0.0/8) 設定不安全的登錄選項

受影響的 GKE 版本:所有版本

在 containerd 映像檔中,系統不會為本機網路 10.0.0.0/8 設定不安全的登錄檔選項。如果您使用不安全的私人登錄檔,可能會看到類似下列的錯誤:

pulling image: rpc error: code = Unknown desc = failed to pull and unpack image "IMAGE_NAME": failed to do request: Head "IMAGE_NAME": http: server gave HTTP response to HTTPS client

如要解決這個問題,請嘗試下列方法:

  • 使用 Artifact Registry
  • 如果您的用途支援這個選項,請在私人登錄檔上設定 TLS。您可以使用 containerd 設定檔,指示 GKE 使用儲存在 Secret Manager 中的憑證存取私有登錄檔。如需操作說明,請參閱「使用私人 CA 憑證存取私人登錄檔」。

設定具備特殊權限的 DaemonSet,以修改 containerd 設定

如果是 Standard 叢集,請嘗試下列步驟。Autopilot 不提供這項解決方法,因為具備權限的容器有安全風險。如果您的環境會連上網際網路,請先考量風險承受度,再部署這項解決方案。在所有情況下,我們都強烈建議您為私人登錄檔設定 TLS,並改用 Secret Manager 選項。

  1. 請查看下列資訊清單:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: insecure-registries
      namespace: default
      labels:
        k8s-app: insecure-registries
    spec:
      selector:
        matchLabels:
          name: insecure-registries
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            name: insecure-registries
        spec:
          nodeSelector:
            cloud.google.com/gke-container-runtime: "containerd"
          hostPID: true
          containers:
            - name: startup-script
              image: registry.k8s.io/startup-script:v2
              imagePullPolicy: Always
              securityContext:
                privileged: true
              env:
              - name: ADDRESS
                value: "REGISTRY_ADDRESS"
              - name: STARTUP_SCRIPT
                value: |
                  set -o errexit
                  set -o pipefail
                  set -o nounset
    
                  if [[ -z "$ADDRESS" || "$ADDRESS" == "REGISTRY_ADDRESS" ]]; then
                    echo "Error: Environment variable ADDRESS is not set in containers.spec.env"
                    exit 1
                  fi
    
                  echo "Allowlisting insecure registries..."
                  containerd_config="/etc/containerd/config.toml"
                  hostpath=$(sed -nr 's;  config_path = "([-/a-z0-9_.]+)";\1;p' "$containerd_config")
                  if [[ -z "$hostpath" ]]; then
                    echo "Node uses CRI config model V1 (deprecated), adding mirror under $containerd_config..."
                    grep -qxF '[plugins."io.containerd.grpc.v1.cri".registry.mirrors."'$ADDRESS'"]' "$containerd_config" || \
                      echo -e '[plugins."io.containerd.grpc.v1.cri".registry.mirrors."'$ADDRESS'"]\n  endpoint = ["http://'$ADDRESS'"]' >> "$containerd_config"
                  else
                    host_config_dir="$hostpath/$ADDRESS"
                    host_config_file="$host_config_dir/hosts.toml"
                    echo "Node uses CRI config model V2, adding mirror under $host_config_file..."
                    if [[ ! -e "$host_config_file" ]]; then
                      mkdir -p "$host_config_dir"
                      echo -e "server = \"https://$ADDRESS\"\n" > "$host_config_file"
                    fi
                    echo -e "[host.\"http://$ADDRESS\"]\n  capabilities = [\"pull\", \"resolve\"]\n" >> "$host_config_file"
                  fi
                  echo "Reloading systemd management configuration"
                  systemctl daemon-reload
                  echo "Restarting containerd..."
                  systemctl restart containerd

    .spec.containers.env 欄位中,將 ADDRESS 變數的 REGISTRY_ADDRESS 值替換為本機 HTTP 登錄的位址,格式為 DOMAIN_NAME:PORT。例如,假設使用者要求系統 將文字從英文翻譯成法文

    containers:
    - name: startup-script
      ...
      env:
      - name: ADDRESS
        value: "example.com:5000"
    
  2. 部署 DaemonSet:

    kubectl apply -f insecure-registry-ds.yaml
    

DaemonSet 會將不安全的登錄檔新增至每個節點的 containerd 設定。

containerd 會忽略特殊權限 Pod 的所有裝置對應

受影響的 GKE 版本:所有版本

如果是具備權限的 Kubernetes Pod,容器執行階段會忽略傳遞給它的任何裝置對應,並改為將主機上的每個裝置提供給 /dev 下的容器。volumeDevices.devicePath

節點承受 I/O 壓力時,containerd 會洩漏 shim 程序

受影響的 GKE 版本:1.25.0 至 1.25.15-gke.1040000、1.26.0 至 1.26.10-gke.1030000、1.27.0 至 1.27.6-gke.1513000,以及 1.28.0 至 1.28.3-gke.1061000

如果 GKE 節點的 I/O 負載過重,當 Pod 遭到刪除時,containerd 可能無法刪除 containerd-shim-runc-v2 程序,導致程序洩漏。如果節點發生洩漏,您會發現節點上的 containerd-shim-runc-v2 程序數量多於該節點上的 Pod 數量。您也可能會發現記憶體和 CPU 使用量增加,以及 PID 額外增加。詳情請參閱 GitHub 問題「Fix leaked shim caused by high IO pressure」。

如要解決這個問題,請將節點升級至下列版本或更新版本:

  • 1.25.15-gke.1040000
  • 1.26.10-gke.1030000
  • 1.27.6-gke.1513000
  • 1.28.3-gke.1061000

在執行 containerd 的 Pod 上啟用 IPv6 位址系列

受影響的 GKE 版本:1.18、1.19、1.20.0 至 1.20.9

針對使用 containerd 執行的 Pod 啟用 IPv6 映像檔系列。dockershim映像檔會停用所有 Pod 的 IPv6,但 containerd 映像檔不會。舉例來說,localhost 會先解析為 IPv6 位址 ::1。這通常不會造成問題,但在某些情況下可能會導致非預期的行為。

解決方案

如要解決這個問題,請明確使用 IPv4 位址 (例如 127.0.0.1),或將在 Pod 中執行的應用程式設定為可同時處理這兩種位址系列。

節點自動佈建功能只會佈建含有 Docker 的 Container-Optimized OS 節點集區

受影響的 GKE 版本:1.18、1.19、1.20.0 至 1.20.6-gke.1800

節點自動佈建功能可讓您使用任何支援的映像檔類型,自動調整節點集區大小,但只能使用 Container-Optimized OS with Docker 映像檔類型建立節點集區。

解決方案

如要解決這個問題,請將 GKE 叢集升級至 1.20.6-gke.1800 以上版本。在這些 GKE 版本中,您可以為叢集設定預設映像檔類型

172.17/16 IP 位址範圍衝突

受影響的 GKE 版本:1.18.0 至 1.18.14

節點 VM 上已啟用 containerd 的 docker0 介面會佔用 172.17/16 IP 位址範圍。傳送至該範圍或源自該範圍的流量可能無法正確路由 (例如,Pod 可能無法連線至 IP 位址位於 172.17/16 範圍內的 VPN 連線主機)。

未收集 GPU 指標

受影響的 GKE 版本:1.18.0 至 1.18.18

在 1.18.18 之前的 GKE 版本上,如果使用 containerd 做為執行階段,系統不會收集 GPU 使用率指標

解決方案

如要解決這個問題,請將叢集升級至 GKE 1.18.18 以上版本。

config.mediaType 設為 application/octet-stream 的映像檔無法在 containerd 上使用

受影響的 GKE 版本:所有版本

如果映像檔的 config.mediaType 設為 "application/octet-stream",就無法在 containerd 上使用。詳情請參閱 GitHub 問題 #4756。這些映像檔不符合開放容器倡議規格,因此視為不正確。這些映像檔可與 Docker 搭配使用,提供回溯相容性,但 containerd 不支援這些映像檔。

症狀和診斷

節點記錄中的錯誤範例:

Error syncing pod <pod-uid> ("<pod-name>_<namespace>(<pod-uid>)"), skipping: failed to "StartContainer" for "<container-name>" with CreateContainerError: "failed to create containerd container: error unpacking image: failed to extract layer sha256:<some id>: failed to get reader from content store: content digest sha256:<some id>: not found"

映像檔資訊清單通常位於映像檔的代管登錄檔中。 取得資訊清單後,請檢查 config.mediaType,判斷您是否遇到這個問題:

"mediaType": "application/octet-stream",

解決方案

由於 containerd 社群決定不支援這類映像檔,因此所有版本的 containerd 都會受到影響,且無法修正。您必須使用 Docker 1.11 以上版本重建容器映像檔,並確保 config.mediaType 欄位未設為 "application/octet-stream"

CNI 未初始化

受影響的 GKE 版本:所有版本

如果看到類似下方的錯誤訊息,表示容器網路介面 (CNI) 設定尚未就緒:

Error: "network is not ready: container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized".

發生這項錯誤的主要原因有兩個:

  • CNI 尚未完成安裝
  • Webhook 設定有誤

確認 CNI 已完成安裝

在節點啟動期間,GKE 安裝 CNI 設定時,記錄檔中可能會顯示這項錯誤。如果看到這項錯誤,但 GKE 正確建立所有節點,您可以放心忽略這項錯誤。

發生這種情況的原因是 CNI 會為 Pod 提供網路連線,因此 Pod 需要 CNI 才能運作。不過,Kubernetes 會使用 taint 標記未準備就緒的節點,而系統 Pod 可以容許這些 taint。這表示系統 Pod 可能會在網路準備就緒前,於新節點上啟動,導致發生錯誤。

如要解決這個問題,請等待 GKE 安裝 CNI 設定完成。CNI 完成網路設定後,系統 Pod 會順利啟動,不需任何介入。

修正設定錯誤的 Webhook

如果 CNI 未初始化錯誤持續發生,且您發現 GKE 在升級、調整大小或其他動作期間無法建立節點,可能是 Webhook 設定錯誤。

如果您有自訂 Webhook,可攔截 DaemonSet 控制器指令來建立 Pod,但該 Webhook 設定錯誤,您可能會在 Google Cloud 控制台中看到錯誤,顯示為節點錯誤狀態。這項設定錯誤會導致 GKE 無法建立 netdcalico-node Pod。如果 netdcalico-node Pod 啟動成功,但錯誤仍持續發生,請與客戶服務團隊聯絡

如要修正設定錯誤的 Webhook,請完成下列步驟:

  1. 找出設定錯誤的 Webhook

    如果您使用的叢集已啟用 Dataplane V1 網路政策強制執行功能,也可以檢查 calico-typha Pod 的狀態,瞭解是哪些 Webhook 導致這個錯誤:

    kubectl describe pod -n kube-system -l k8s-app=calico-typha
    

    如果 Pod 發生錯誤,輸出結果會與下列內容相似:

    Events:
    Type     Reason        Age                     From                   Message
    ----     ------        ----                    ----                   -------
    Warning  FailedCreate  9m15s (x303 over 3d7h)  replicaset-controller  Error creating: admission webhook WEBHOOK_NAME denied the request [...]
    

    在這個輸出內容中,WEBHOOK_NAME 是失敗的 Webhook 名稱。輸出內容可能包含其他類型的錯誤資訊。

  2. 如要保留設定錯誤的 Webhook,請排解問題。如果不需要這些權限,請執行下列指令來刪除:

    kubectl delete mutatingwebhookconfigurations WEBHOOK_NAME
    kubectl delete validatingwebhookconfigurations WEBHOOK_NAME
    

    WEBHOOK_NAME 替換為要移除的設定錯誤 Webhook 名稱。

  3. 設定 Webhook,忽略系統 Pod

後續步驟