透過 Ingress 使用容器原生負載平衡功能


本頁說明如何在 Google Kubernetes Engine (GKE) 中使用容器原生負載平衡。容器原生負載平衡功能可讓負載平衡器直接指定 Kubernetes Pod,並將流量平均傳送給 Pod。

如要進一步瞭解容器原生負載平衡的優點、需求和限制,請參閱「容器原生負載平衡」一文。

事前準備

開始之前,請確認你已完成下列工作:

  • 啟用 Google Kubernetes Engine API。
  • 啟用 Google Kubernetes Engine API
  • 如要使用 Google Cloud CLI 執行這項工作,請安裝初始化 gcloud CLI。如果您先前已安裝 gcloud CLI,請執行 gcloud components update,取得最新版本。

使用容器原生負載平衡

下列各節會逐步引導您在 GKE 上設定容器原生負載平衡。

建立 VPC 原生叢集

如要使用容器原生負載平衡,GKE 叢集必須啟用別名 IP

舉例來說,下列指令會建立名為 neg-demo-cluster 的 GKE 叢集,並自動佈建子網路:

  • 在 Autopilot 模式中,別名 IP 位址預設為啟用:

    gcloud container clusters create-auto neg-demo-cluster \
        --location=COMPUTE_LOCATION
    

    COMPUTE_LOCATION 替換為叢集的 Compute Engine 位置

  • 如果是標準模式,請在建立叢集時啟用別名 IP 位址:

    gcloud container clusters create neg-demo-cluster \
        --enable-ip-alias \
        --create-subnetwork="" \
        --network=default \
        --location=us-central1-a
    

可建立部署作業

下列的 Deployment 範例 neg-demo-app 會執行容器化 HTTP 伺服器的單一執行個體。建議使用會使用 Pod 完備性反饋的工作負載。

使用 Pod 完備性反饋

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
        ports:
        - containerPort: 9376
          protocol: TCP
  

使用硬式編碼延遲

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
        ports:
        - containerPort: 9376
          protocol: TCP
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

在此部署中,各容器都會執行一個 HTTP 伺服器。HTTP 伺服器會傳回應用程式伺服器的主機名稱 (亦即伺服器執行所在的 Pod 名稱) 做為回應。

將這份資訊清單儲存為 neg-demo-app.yaml,然後建立 Deployment:

kubectl apply -f neg-demo-app.yaml

為容器原生負載平衡器建立 Service

建立 Deployment 後,必須將部署的多個 Pod 組成一個 Service

下列的 Service 範例 neg-demo-svc 是以前一節建立的 Deployment 範例為目標:

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
spec: # Service's specification
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - name: http
    port: 80 # Service's port
    protocol: TCP
    targetPort: 9376

要等到您為 Service 建立 Ingress 後,才會建立負載平衡器。

將此資訊清單儲存為 neg-demo-svc.yaml,然後建立 Service:

kubectl apply -f neg-demo-svc.yaml

為 Service 建立 Ingress

下列的 Ingress 範例 neg-demo-ing 會以您建立的 Service 為目標:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  defaultBackend:
    service:
      name: neg-demo-svc # Name of the Service targeted by the Ingress
      port:
        number: 80 # Should match the port used by the Service

將此資訊清單儲存為 neg-demo-ing.yaml,然後建立 Ingress:

kubectl apply -f neg-demo-ing.yaml

建立 Ingress 後,專案中會建立應用程式負載平衡器,且叢集執行所在的每個區域中都會建立網路端點群組(NEG)。NEG 裡的端點和 Service 端點會保持同步。

驗證 Ingress

在部署工作負載、將工作負載的多個 Pod 組成一個 Service,並為 Service 建立 Ingress 之後,應該要確認 Ingress 已成功佈建容器原生負載平衡器。

擷取 Ingress 的狀態:

kubectl describe ingress neg-demo-ing

輸出內容包含 ADDCREATE 事件:

Events:
Type     Reason   Age                From                     Message
----     ------   ----               ----                     -------
Normal   ADD      16m                loadbalancer-controller  default/neg-demo-ing
Normal   Service  4s                 loadbalancer-controller  default backend set to neg-demo-svc:32524
Normal   CREATE   2s                 loadbalancer-controller  ip: 192.0.2.0

測試負載平衡器

下列各節說明如何測試容器原生負載平衡器的功能。

造訪 Ingress IP 位址

請等候數分鐘,以等待應用程式負載平衡器完成設定。

您可以造訪 Ingress 的 IP 位址,確認容器原生負載平衡器功能運作正常。

如要取得 Ingress IP 位址,請執行下列指令:

kubectl get ingress neg-demo-ing

在指令列輸出中,Ingress 的 IP 位址會顯示於 ADDRESS 欄。使用網路瀏覽器造訪 IP 位址。

檢查後端服務健康狀態

您也可以取得負載平衡器的後端服務健康狀態。

  1. 取得專案中執行的後端服務清單:

    gcloud compute backend-services list
    

    記錄含有 Service 名稱的後端服務名稱,例如 neg-demo-svc

  2. 取得後端服務的健康狀態:

    gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
    

    BACKEND_SERVICE_NAME 改為後端服務名稱。

測試 Ingress

要如預期般測試負載平衡器功能,還有另外一種方法:調度 Deployment 範例的資源,將測試要求傳送到 Ingress,然後驗證回應的備用資源數量正確無誤。

  1. 將一個執行個體的 neg-demo-app Deployment 資源擴充到兩個執行個體:

    kubectl scale deployment neg-demo-app --replicas 2
    

    這個指令可能需要幾分鐘才能完成。

  2. 確認推出作業已完成:

    kubectl get deployment neg-demo-app
    

    輸出內容應包含兩個可用的備用資源:

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    neg-demo-app   2         2         2            2           26m
    
  3. 取得 Ingress IP 位址:

    kubectl describe ingress neg-demo-ing
    

    如果此指令傳回 404 錯誤,請稍待幾分鐘,等負載平衡器啟動後再試一次。

  4. 計算負載平衡器傳出的不重複回應之數量:

    for i in `seq 1 100`; do \
      curl --connect-timeout 1 -s IP_ADDRESS && echo; \
    done  | sort | uniq -c
    

    IP_ADDRESS 替換為 Ingress IP 位址。

    輸出結果會與下列內容相似:

    44 neg-demo-app-7f7dfd7bc6-dcn95
    56 neg-demo-app-7f7dfd7bc6-jrmzf
    

    在這個輸出內容中,不重複回應的數量與備用資源數量相同,表示所有後端 Pod 都在處理流量。

清除所用資源

完成本頁面的工作之後,為避免您的帳戶中產生不必要的費用,請按照下列步驟移除資源:

刪除叢集

gcloud

gcloud container clusters delete neg-demo-cluster

控制台

  1. 前往 Google Cloud 控制台的「Google Kubernetes Engine」頁面。

    前往「Google Kubernetes Engine」

  2. 選取「neg-demo-cluster」,然後按一下 「Delete」(刪除)

  3. 當系統提示時,按一下「Delete」(刪除)

疑難排解

請使用以下技巧確認您的網路設定。下列各節說明如何解決關於容器原生負載平衡的特定問題。

  • 如要進一步瞭解如何列出網路端點群組,請參閱負載平衡器說明文件

  • 您可以在服務的 neg-status 註解中,找到與該服務相對應的 NEG 名稱和區域。取得 Service 規格:

    kubectl get svc SVC_NAME -o yaml
    

    metadata:annotations:cloud.google.com/neg-status 註解會列出服務對應的 NEG 名稱和 NEG 區域。

  • 您可以透過下列指令,檢查與 NEG 相對應的後端服務狀態:

    gcloud compute backend-services --project PROJECT_NAME \
        get-health BACKEND_SERVICE_NAME --global
    

    後端服務的名稱與其 NEG 相同。

  • 如何列印服務事件記錄:

    kubectl describe svc SERVICE_NAME
    

    服務的名稱字串包含對應 GKE 服務的名稱和命名空間。

無法使用別名 IP 建立叢集

問題

嘗試使用別名 IP 建立叢集時,可能會碰到下列錯誤:

ResponseError: code=400, message=IP aliases cannot be used with a legacy network.
可能原因

嘗試透過使用舊版網路的別名 IP 來建立叢集時,可能會發生上列錯誤。

解決方法

請勿在啟用舊版網路的情況下使用別名 IP 建立叢集。別名 IP 使用詳情請參閱建立虛擬私有雲原生叢集

流量未達端點

問題
502/503 錯誤或連線遭拒。
可能原因

新端點只要會回應健康狀態檢查,則在附加到負載平衡器後通常就可存取。如果流量達不到端點,就可能會發生 502 錯誤或連線遭拒。

如果容器無法處理 SIGTERM,也可能會造成 502 錯誤和連線遭拒。如果容器未明確處理 SIGTERM,則會立即終止並停止處理要求。負載平衡器會持續將傳入的流量傳送至已終止的容器,導致發生錯誤。

容器原生負載平衡器只有一個後端端點。在滾動更新期間,系統會先取消舊端點的程式設計,再為新端點設計程式。

佈建容器原生負載平衡器後,後端 Pod 會首次部署到新區域。當可用區中至少有一個端點時,系統會在該可用區中設定負載平衡器基礎架構。在區域中新增端點時,系統會對負載平衡器基礎架構進行程式設計,導致服務中斷。

解決方法

將置容器設為處理 SIGTERM,並在整個終止寬限期內 (預設為 30 秒) 繼續回應要求。將 Pod 設為在收到 SIGTERM 時,開始讓健康狀態檢查失敗。如此會向負載平衡器發出信號,要求在端點正在取消程式設計時,停止向 Pod 傳送流量。

如果應用程式在收到 SIGTERM 時未正常關閉,且停止回應要求,則可使用 preStop hook 處理 SIGTERM,並在端點取消程式設計期間持續提供流量。

lifecycle:
  preStop:
    exec:
      # if SIGTERM triggers a quick exit; keep serving traffic instead
      command: ["sleep","60"]

請參閱 Pod 終止說明文件

如果負載平衡器後端只有一個執行個體,請設定推出策略,以免在新的執行個體完全完成程式設計前,就拆除唯一的執行個體。如要為 Deployment 工作負載管理的應用程式 Pod 達成此目的,請將 maxUnavailable 參數設為 0,藉此設定推出策略

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

如要對流量未抵達端點的問題進行疑難排解,請確認防火牆規則允許傳入 TCP 流量到 130.211.0.0/2235.191.0.0/16 範圍內的端點。詳情請參閱 Cloud Load Balancing 說明文件中的新增健康狀態檢查

查看專案裡的後端服務。相關後端服務的名稱字串,包含了對應的 GKE Service 名稱和命名空間:

gcloud compute backend-services list

向後端服務擷取後端健康狀態:

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

如果所有後端健康狀態不良,就表示防火牆、Ingress 或 Service 可能設定有誤。

如果有些後端健康狀態短時間不良,就表示網路程式設計延遲時間可能是原因所在。

如果有些後端沒有出現在後端服務清單,就表示程式設計延遲時間可能是原因所在。要確認這點,請執行下列指令,當中的 NEG_NAME 是後端服務名稱。 (NEG 和後端服務共用同一個名稱):

gcloud compute network-endpoint-groups list-network-endpoints NEG_NAME

確認所有預期的端點是否都已在 NEG 中。

如果容器原生負載平衡器選取的後端數量較少 (例如 1 個 Pod),請考慮增加副本數量,並將後端 Pod 分散到 GKE 叢集涵蓋的所有區域。這可確保底層負載平衡器基礎架構已完全程式化。否則,請考慮將後端 Pod 限制在單一區域。

如果您為端點設定網路政策,請務必允許從僅限 Proxy 的子網路輸入。

停止發布

問題
更新的 Deployment 延遲推出,且最新備用資源的數量與與所需備用資源的數量不符。
可能原因

Deployment 的健康狀態檢查失敗。容器映像檔可能不正確,或是健康狀態檢查可能設定錯誤。Pod 的滾動式替換作業會等候新啟動的 Pod 傳送其 Pod 完備性門檻。只有當 Pod 在回應負載平衡器健康狀態檢查時,才會發生這種情況。如果 Pod 沒有回應,或是健康狀態檢查設定有誤,就無法達到完備性門檻,導致無法繼續發布。

如果您使用的是 kubectl 1.13 或以上版本,則可以使用以下指令檢查 Pod 完備性門檻的狀態:

kubectl get pod POD_NAME -o wide

請勾選 READINESS GATES 欄位。

kubectl 1.12 以下版本沒有這個資料欄。標記為處於 READY 狀態的 Pod,可能會有完備性門檻失敗的問題。如要驗證這一點,請使用以下指令:

kubectl get pod POD_NAME -o yaml

系統會在輸出結果中列出完備性門檻和其狀態。

解決方法

確認 Deployment 的 Pod 規格中的容器映像檔運作正常,且能回應健康狀態檢查。請確認健康狀態檢查設定正確無誤。

模式降級錯誤

問題

從 GKE 1.29.2-gke.1643000 版開始,更新 NEG 時,您可能會在服務的記錄檔探索工具中看到下列警告:

Entering degraded mode for NEG <service-namespace>/<service-name>-<neg-name>... due to sync err: endpoint has missing nodeName field
可能原因

這些警告表示 GKE 在根據 EndpointSlice 物件更新 NEG 時,偵測到端點設定錯誤,因此觸發了更深入的計算程序,稱為降級模式。GKE 會盡力更新 NEG,方法是修正設定錯誤,或從 NEG 更新中排除無效端點。

常見錯誤包括:

  • endpoint has missing pod/nodeName field
  • endpoint corresponds to an non-existing pod/node
  • endpoint information for attach/detach operation is incorrect
解決方法

通常,這些事件是由暫時性狀態所導致,而且會自行修正。不過,自訂 EndpointSlice 物件設定錯誤導致的事件仍未解決。如要瞭解設定錯誤,請檢查與服務對應的 EndpointSlice 物件:

kubectl get endpointslice -l kubernetes.io/service-name=<service-name>

根據事件中的錯誤驗證每個端點。

如要解決這個問題,請手動修改 EndpointSlice 物件。更新會觸發 NEG 再次更新。修正設定錯誤後,輸出內容會類似如下:

NEG <service-namespace>/<service-name>-<neg-name>... is no longer in degraded mode

已知問題

GKE 上的容器原生負載平衡具有下列已知問題:

垃圾收集未完成

GKE 每兩分鐘就會對容器原生負載平衡器進行垃圾收集。如果叢集在負載平衡器完全移除前就遭到刪除,就必須手動刪除負載平衡器的 NEG。

請執行下列指令來查看專案裡的 NEG:

gcloud compute network-endpoint-groups list

在指令輸出中,找出相關的 NEG。

如要刪除 NEG,請執行下列指令,並將 NEG_NAME 替換為 NEG 的名稱:

gcloud compute network-endpoint-groups delete NEG_NAME

工作負載的發布以及端點的傳播應保持一致

將工作負載部署到叢集時,或更新現有工作負載時,容器原生負載平衡器傳播新端點的所需時間可能會超過工作負載發布完成的所需時間。您在本指南中部署的 Deployment 範例為了讓發布與端點的傳播保持一致,會使用下列兩個欄位:terminationGracePeriodSecondsminReadySeconds

terminationGracePeriodSeconds 允許 Pod 安全關閉,方法是等待連線在 Pod 預定刪除後終止。

minReadySeconds 會在 Pod 建立後新增延遲期間。您要指定下列狀態的最短秒數:在新 Pod 中沒有任何容器當機 (即系統認定 Pod 可用) 的情況下,新 Pod 處於 Ready 狀態的最短秒數。

您應將工作負載的 minReadySeconds 值和 terminationGracePeriodSeconds 值設成 60 秒以上,這樣就能確保服務不會因工作負載的發布而中斷。

terminationGracePeriodSeconds 在所有 Pod 規格中都可用;minReadySeconds 適用於 Deployment 和 DaemonSet。

如要進一步瞭解如何微調發布,請參閱 RollingUpdateStrategy

Pod readinessProbe 中的 initialDelaySeconds 不受尊重

您可能會認為 Pod 的 initialDelaySeconds 設定 (readinessProbe) 會受到容器原生負載平衡器尊重,但 readinessProbe 是由 kubelet 實作,而 initialDelaySeconds 設定控制的是 kubelet 健康狀態檢查,而非容器原生負載平衡器。容器原生負載平衡有自己的負載平衡健康狀態檢查。

後續步驟