排解 GKE 中的負載平衡問題

Google Kubernetes Engine (GKE) 的負載平衡問題可能會導致服務中斷,例如發生 HTTP 502 錯誤,或無法存取應用程式。

請參閱本文,瞭解如何排解外部 Ingress 的 502 錯誤,以及如何使用負載平衡器記錄和診斷工具 (例如 check-gke-ingress) 找出問題。

對於在 GKE 中設定及維護負載平衡服務的平台管理員、操作員和應用程式開發人員來說,這項資訊非常重要。如要進一步瞭解我們在 Google Cloud 內容中提及的常見角色和範例工作,請參閱「常見的 GKE 使用者角色和工作」。

找不到 BackendConfig

如果 Service 註解中指定了 Service 通訊埠的 BackendConfig,但找不到實際的 BackendConfig 資源,就會發生這項錯誤。

如要評估 Kubernetes 事件,請執行下列指令:

kubectl get event

以下輸出範例表示系統找不到 BackendConfig:

KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: error getting BackendConfig for port 80 on service "default/my-service":
no BackendConfig for service port exists

如要解決這個問題,請確認您未在錯誤的命名空間中建立 BackendConfig 資源,或是在服務註解中拼錯參照名稱。

找不到 Ingress 安全性政策

建立 Ingress 物件後,如果安全性政策未正確與 LoadBalancer 服務建立關聯,請評估 Kubernetes 事件,確認是否有設定錯誤。如果 BackendConfig 指定的安全性政策不存在,系統會定期發出警告事件。

如要評估 Kubernetes 事件,請執行下列指令:

kubectl get event

以下輸出範例表示找不到安全性政策:

KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: The given security policy "my-policy" does not exist.

如要解決這個問題,請在 BackendConfig 中指定正確的安全政策名稱。

在 GKE 中資源調度工作負載時,使用 NEG 解決 500 系列錯誤

問題:

使用 GKE 佈建的 NEG 進行負載平衡時,工作負載縮減期間,服務可能會發生 502 或 503 錯誤。如果 Pod 在現有連線關閉前終止,就會發生 502 錯誤;如果流量導向已刪除的 Pod,則會發生 503 錯誤。

如果您使用採用 NEG 的 GKE 代管負載平衡產品 (包括 Gateway、Ingress 和獨立 NEG),叢集可能會受到這個問題影響。如果經常調整工作負載規模,叢集受影響的風險會更高。

診斷結果:

如果未先排空端點並從 NEG 移除,就直接移除 Kubernetes 中的 Pod,會導致 500 系列錯誤。為避免 Pod 終止期間發生問題,您必須考量作業順序。下圖顯示 BackendService Drain Timeout 未設定和 BackendService Drain Timeout 設為 BackendConfig 的情境。

情境 1:未設定 BackendService Drain Timeout。

下圖顯示 BackendService Drain Timeout 未設定的情況。

未設定 BackendService 停止運作逾時。

情境 2:已設定 BackendService Drain Timeout。

下圖顯示設定 BackendService Drain Timeout 的情境。

已設定 BackendService 排空逾時。

500 系列錯誤的確切發生時間取決於下列因素:

  • NEG API 卸離延遲時間:NEG API 卸離延遲時間是指目前在 Google Cloud中完成卸離作業所需的時間。這會受到 Kubernetes 以外的多種因素影響,包括負載平衡器類型和特定可用區。

  • 排空延遲時間:排空延遲時間是指負載平衡器開始將流量從系統特定部分導出的時間。啟動排空程序後,負載平衡器會停止將新要求傳送至端點,但觸發排空程序仍有延遲 (排空延遲),如果 Pod 不再存在,可能會導致暫時發生 503 錯誤。

  • 健康狀態檢查設定:更嚴格的健康狀態檢查門檻可縮短 503 錯誤的持續時間,因為即使分離作業尚未完成,這類門檻也能向負載平衡器發出訊號,停止將要求傳送至端點。

  • 終止寬限期:終止寬限期會決定 Pod 結束時的最多時間。不過,Pod 可以在終止寬限期結束前退出。如果 Pod 耗時超過這段時間,系統會在時間結束時強制 Pod 結束。這是 Pod 的設定,需要在工作負載定義中設定。

可能的解決方法:

如要避免發生 5XX 錯誤,請套用下列設定。逾時值僅供參考,您可能需要根據特定應用程式進行調整。下一節將引導您完成自訂程序。

下圖顯示如何使用 preStop hook 讓 Pod 保持運作:

已設定 BackendService 排除逾時。

如要避免發生 500 系列錯誤,請按照下列步驟操作:

  1. 將服務的 BackendService Drain Timeout 設為 1 分鐘。

  2. 擴充 Pod 上的 terminationGracePeriod。

    將 Pod 的 terminationGracePeriodSeconds 設為 3.5 分鐘。搭配建議設定使用時,這項功能可讓 Pod 在端點從 NEG 移除後,有 30 到 45 秒的時間正常關機。如需更多時間進行正常關機,可以延長寬限期,或按照「自訂逾時」一節中的指示操作。

    下列 Pod 資訊清單指定 210 秒 (3.5 分鐘) 的連線排除逾時:

    spec:
      terminationGracePeriodSeconds: 210
      containers:
      - name: my-app
        ...
      ...
    
  3. 將 preStop 鉤子套用至所有容器。

    套用 preStop hook,確保 Pod 在負載平衡器中排空 Pod 端點,並從 NEG 移除端點時,仍可存活 120 秒。

    spec:
      containers:
      - name: my-app
        ...
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 120s"]
      ...
    

自訂逾時

為確保 Pod 持續運作並避免發生 500 系列錯誤,Pod 必須保持運作,直到端點從 NEG 移除為止。具體來說,為避免 502 和 503 錯誤,建議您同時實作逾時和 preStop 鉤。

如要在關機程序期間延長 Pod 的存活時間,請在 Pod 中新增 preStop hook。在系統發出 Pod 結束訊號前執行 preStop hook,因此 preStop hook 可用於讓 Pod 保持存活,直到對應的端點從 NEG 中移除為止。

如要延長 Pod 在關機程序期間保持啟用的時間,請在 Pod 設定中插入 preStop hook,如下所示:

spec:
  containers:
  - name: my-app
    ...
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep <latency time>"]

您可以設定逾時和相關設定,在工作負載縮減期間,管理 Pod 的正常關機作業。您可以根據特定用途調整逾時時間。建議您先設定較長的逾時時間,再視需要縮短。您可以透過下列方式,設定逾時相關參數和 preStop 勾點,自訂逾時時間:

後端服務排空逾時

Backend Service Drain Timeout 參數預設為未設定,因此不會產生任何作用。如果您設定 Backend Service Drain Timeout 參數並啟用,負載平衡器會停止將新要求路由至端點,並等待逾時,然後終止現有連線。

您可以設定 Backend Service Drain Timeout 參數,方法是搭配使用 BackendConfig 和 Ingress、GCPBackendPolicy 和閘道,或是在 BackendService 手動設定獨立 NEG。逾時時間應比處理要求所需的時間長 1.5 到 2 倍。這樣一來,如果要求在開始排除前傳入,就會在逾時前完成。將 Backend Service Drain Timeout 參數設為大於 0 的值,有助於減輕 503 錯誤,因為系統不會將新要求傳送至預定移除的端點。如要讓這項逾時設定生效,您必須搭配 preStop 勾點使用,確保 Pod 在排空期間保持運作。如果沒有這個組合,現有未完成的要求會收到 502 錯誤。

preStop 鉤子時間

preStop 鉤子必須延遲 Pod 關機,讓排除延遲和後端服務排除逾時完成,確保在 Pod 關機前,連線排除和從 NEG 移除端點作業能正確執行。

為達到最佳效果,請確保 preStop 鉤子執行時間大於或等於 Backend Service Drain Timeout 和耗盡延遲時間的總和。

請使用下列公式,計算理想的 preStop 鉤子執行時間:

preStop hook execution time >= BACKEND_SERVICE_DRAIN_TIMEOUT + DRAIN_LATENCY

更改下列內容:

  • BACKEND_SERVICE_DRAIN_TIMEOUT:您為 Backend Service Drain Timeout 設定的時間。
  • DRAIN_LATENCY:預估的排空延遲時間。建議您將預估時間設為一分鐘。

如果 500 錯誤持續發生,請估算總發生時間長度,並將該時間長度乘以兩倍,然後加到預估的排除延遲時間。這樣一來,Pod 就能在從服務中移除前,有足夠的時間正常耗盡。如果這個值對特定用途而言太長,可以調整。

或者,您也可以查看 Pod 的刪除時間戳記,以及 Cloud 稽核記錄中端點從 NEG 移除的時間戳記,估算時間。

終止寬限期參數

您必須設定 terminationGracePeriod 參數,確保 preStop 勾點有足夠時間完成,且 Pod 能順利關閉。

如未明確設定,terminationGracePeriod 預設為 30 秒。您可以使用下列公式計算最佳 terminationGracePeriod:

terminationGracePeriod >= preStop hook time + Pod shutdown time

如要在 Pod 的設定中定義 terminationGracePeriod,請按照下列步驟操作:

  spec:
    terminationGracePeriodSeconds: <terminationGracePeriod>
    containers:
    - name: my-app
      ...
    ...

建立內部 Ingress 資源時找不到 NEG

在 GKE 中建立內部 Ingress 時,可能會發生下列錯誤:

Error syncing: error running backend syncing routine: googleapi: Error 404: The resource 'projects/PROJECT_ID/zones/ZONE/networkEndpointGroups/NEG' was not found, notFound

發生這項錯誤的原因是,內部應用程式負載平衡器的 Ingress 需要網路端點群組 (NEG) 做為後端。

在 Shared VPC 環境或啟用網路政策的叢集中,請將 cloud.google.com/neg: '{"ingress": true}' 註解新增至服務資訊清單。

504 Gateway Timeout:上游要求逾時

從 GKE 的內部 Ingress 存取 Service 時,可能會發生下列錯誤:

HTTP/1.1 504 Gateway Timeout
content-length: 24
content-type: text/plain

upstream request timeout

發生這項錯誤的原因是,傳送至內部應用程式負載平衡器的流量,是由 Proxy 專用子網路範圍內的 Envoy Proxy 轉送。

如要允許來自僅限 Proxy 子網路範圍的流量,請在服務的 targetPort 上建立防火牆規則。

錯誤 400:「resource.target」欄位的值無效

從 GKE 的內部 Ingress 存取 Service 時,可能會發生下列錯誤:

Error syncing:LB_NAME does not exist: googleapi: Error 400: Invalid value for field 'resource.target': 'https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/regions/REGION_NAME/targetHttpProxies/LB_NAME. A reserved and active subnetwork is required in the same region and VPC as the forwarding rule.

如要解決這個問題,請建立僅限 Proxy 的子網路。

同步處理錯誤:執行負載平衡器同步處理常式時發生錯誤:負載平衡器不存在

GKE 控制平面升級或修改 Ingress 物件時,可能會發生下列其中一項錯誤:

"Error during sync: error running load balancer syncing routine: loadbalancer
INGRESS_NAME does not exist: invalid ingress frontend configuration, please
check your usage of the 'kubernetes.io/ingress.allow-http' annotation."

或:

Error during sync: error running load balancer syncing routine: loadbalancer LOAD_BALANCER_NAME does not exist:
googleapi: Error 400: Invalid value for field 'resource.IPAddress':'INGRESS_VIP'. Specified IP address is in-use and would result in a conflict., invalid

如要解決這些問題,請嘗試下列步驟:

  • 在 Ingress 資訊清單的 tls 區段中新增 hosts 欄位,然後刪除 Ingress。等待五分鐘,讓 GKE 刪除未使用的 Ingress 資源。然後重新建立 Ingress。詳情請參閱「Ingress 物件的 hosts 欄位」。
  • 還原對 Ingress 所做的變更。然後使用註解或 Kubernetes Secret 新增憑證。

外部 Ingress 產生 HTTP 502 錯誤

請按照下列指引,排解外部 Ingress 資源的 HTTP 502 錯誤:

  1. 針對與 Ingress 參照的各項 GKE 服務相關聯的每個後端服務,啟用記錄。
  2. 使用狀態詳細資料,找出 HTTP 502 回應的原因。如果狀態詳細資料指出 HTTP 502 回應來自後端,則需要對放送 Pod 進行疑難排解,而非負載平衡器。

非代管執行個體群組

如果外部 Ingress 使用非代管執行個體群組後端,您可能會遇到外部 Ingress 資源的 HTTP 502 錯誤。如果符合下列所有條件,就會發生這個問題:

  • 叢集內所有節點集區的節點總數過多。
  • Ingress 參照的一或多個服務的服務 Pod 僅位於少數節點上。
  • Ingress 參照的 Service 會使用 externalTrafficPolicy: Local。

如要判斷外部 Ingress 是否使用非代管執行個體群組後端,請執行下列步驟:

  1. 前往 Google Cloud 控制台的「Ingress」(輸入) 頁面。

    前往 Ingress

  2. 按一下外部 Ingress 的名稱。

  3. 按一下負載平衡器名稱。系統會顯示「Load balancing details」(負載平衡詳細資料) 頁面。

  4. 查看「Backend services」(後端服務) 區段中的表格,判斷外部 Ingress 是否使用 NEG 或執行個體群組。

如要解決這個問題,請採用下列任一解決方法:

  • 使用 VPC 原生叢集。
  • 針對外部 Ingress 參照的每個服務使用 externalTrafficPolicy: Cluster。這個解決方案會導致您遺失封包來源中的原始用戶端 IP 位址。
  • 使用 node.kubernetes.io/exclude-from-external-load-balancers=true 註解。將註解新增至節點或節點集區,這些節點或節點集區不會為叢集中任何外部 Ingress 或 LoadBalancer Service 參照的任何 Service 執行任何服務 Pod。

L4 負載平衡器記錄設定

如果您已為外部直通式網路負載平衡器或內部直通式網路負載平衡器啟用記錄功能,本節將提供疑難排解資訊。

監控記錄設定狀態

GKE L4LB 控制器會透過 Service 的 status.conditions 類型,提供記錄檔的協調狀態回饋。您可以執行下列指令來檢查這項狀態:

kubectl get svc SERVICE_NAME -o yaml

更改下列內容:

  • SERVICE_NAME:叢集名稱。

在輸出內容中,尋找 LoggingConfigManaged 條件類型。下表說明可能造成這種情況的原因:

條件狀態 原因 說明
是 已協調 控制器會主動強制執行 L4LBConfig CRD 中定義的記錄設定。
否 非代管 L4LBConfig CRD 缺少 logging 區段,或註解已移除。控制器已停止管理,並將後端服務保留在最後已知狀態。
否 找不到 找不到 Service 註解中參照的 L4LBConfig 資源。
否 無效 L4LBConfig 資源未通過 optionalFields 參數的交叉驗證。
否 錯誤 後端服務對帳時發生錯誤。

瞭解滑行行為

如果從 Service 資訊清單中移除 networking.gke.io/l4lb-config 註解,或刪除參照的 L4LBConfig 資源,設定就會進入 Coast 狀態。

在這種狀態下,GKE 控制器會停止管理記錄設定,但不會將 Google Cloud 後端服務重設為預設設定。後端服務會維持最後已知的良好狀態。系統通常會發出警告事件,通知您 Kubernetes 不再控管設定。

使用負載平衡器記錄檔排解問題

您可以透過內部直通式網路負載平衡器記錄和外部直通式網路負載平衡器記錄,排解負載平衡器相關問題,並將負載平衡器的流量與 GKE 資源建立關聯。

系統會匯總每個連線的記錄,並以近乎即時的方式匯出。系統會為 LoadBalancer 服務資料路徑中涉及的每個 GKE 節點產生記錄,包括輸入和輸出流量。記錄項目包含 GKE 資源的其他欄位,例如:

  • 叢集名稱
  • 叢集位置
  • 服務名稱
  • 服務命名空間
  • Pod 名稱
  • Pod 命名空間

定價

使用記錄不會產生額外費用。視記錄擷取方式而定,系統會依 Cloud Logging、BigQuery 或 Pub/Sub 的標準價格計費。啟用記錄不會影響負載平衡器的效能。

使用診斷工具排解問題

check-gke-ingress 診斷工具會檢查 Ingress 資源,找出常見的設定錯誤。你可以透過下列方式使用「check-gke-ingress」工具:

  • 在叢集上執行 gcpdiag 指令列工具。輸入結果會顯示在檢查規則 gke/ERR/2023_004 部分。
  • 請按照 check-gke-ingress 中的操作說明,單獨使用 check-gke-ingress 工具,或將其做為 kubectl 外掛程式使用。

後續步驟