在 GKE 上使用 Envoy Proxy 以達到 gRPC 服務負載平衡

Last reviewed 2019-05-30 UTC

本教學課程示範如何使用外部直通式網路負載平衡器Envoy Proxy,在單一外部 IP 位址上公開部署於 Google Kubernetes Engine (GKE) 的多個 gRPC 服務。本教學課程重點說明 Envoy 為 gRPC 提供的部分進階功能。

簡介

gRPC 是一種以 HTTP/2 為基礎的開放原始碼且與程式設計語言種類無關的遠端程序呼叫 (RPC) 架構,將通訊協定緩衝區用於高效傳輸中表示法和快速序列化。Stubby 為內部 Google RPC 架構帶來了許多啟發,gRPC 實現微服務之間以及行動用戶端與 API 之間的低延遲通訊。

gRPC 透過 HTTP/2 執行並透過 HTTP/1.1 提供多項優勢,例如高效二進位檔編碼、透過單一連線對要求和回應進行多工處理,以及自動流程控制。gRPC 也提供多個負載平衡的選項。本教學課程著重於用戶端不受信任的情況,例如行動用戶端以及在服務供應商信任範圍外執行的用戶端。gRPC 提供多種負載平衡選項,而您將在本教學課程中使用以 Proxy 為基礎的負載平衡。

在本教學課程中,您將部署 TYPE=LoadBalancer 的 Kubernetes 服務,這在Google Cloud上是以傳輸層 (第 4 層) 外部直通式網路負載平衡器的形式公開。這個服務提供單一公開 IP 位址,並將 TCP 連線直接傳送到設定的後端。在本教學課程中,後端是 Envoy 執行個體的 Kubernetes 部署。

Envoy 是一種開放原始碼應用程式層 (第 7 層) Proxy,可提供許多進階功能。在本教學課程中,您將使用 Envoy 終止 TLS 連線,並將 gRPC 流量轉送到適當的 Kubernetes 服務。相較於 Kubernetes Ingress 等其他應用程式層解決方案,直接使用 Envoy 可提供多個自訂選項,例如下列項目:

  • 服務探索
  • 負載平衡演算法
  • 轉換要求和回應,例如轉換為 JSON 或 gRPC-Web
  • 驗證 JWT 憑證以驗證要求
  • gRPC 健康狀態檢查

您可以將外部直通式網路負載平衡器與 Envoy 結合,藉此設定端點 (外部 IP 位址),將流量轉送到一組在 Google Kubernetes Engine 叢集中執行的 Envoy 執行個體。接著,這些執行個體將使用應用程式層資訊,透過 Proxy 將要求傳送到叢集中執行的不同 gRPC 服務。Envoy 執行個體針對每項服務,使用叢集 DNS 來識別傳入的 gRPC 要求,並將這些要求以負載平衡的方式傳到健康狀態良好且正在執行的 Pod。也就是說,系統會按照 RPC 要求 (而不是按照來自用戶端的 TCP 連線) 將流量以負載平衡的方式傳到 Pod。

架構

在本教學課程中,您將在 Google Kubernetes Engine (GKE) 叢集中部署兩個 gRPC 服務 (echo-grpcreverse-grpc),並將這些服務公開到公開 IP 位址的網際網路上。下圖顯示透過單一端點公開這兩個服務的架構:

透過單一端點公開「echo-grpc」和「reverse-grpc」的架構

外部直通式網路負載平衡可接受來自網際網路的傳入要求 (例如來自行動用戶端或公司以外的服務用戶)。外部直通式網路負載平衡器會執行下列工作:

  • 將傳入連線以負載平衡的方式傳到集區中的節點。系統會將流量轉送到於叢集的所有節點上公開的 envoy Kubernetes 服務。Kubernetes 網路會透過 Proxy 將這些連線轉送到執行 Envoy 的 Pod。
  • 對叢集中的節點執行 HTTP 健康狀態檢查。

Envoy 會執行下列工作:

  • 終止 TLS 連線。
  • 查詢內部叢集 DNS 服務以探索執行 gRPC 服務的 Pod。
  • 將流量以負載平衡的方式轉送到 gRPC 服務 Pod。
  • 根據 gRPC 健康狀態檢查通訊協定,執行 gRPC 服務的健康狀態檢查。
  • 透過外部直通式網路負載平衡器公開端點,進行 Envoy 執行個體的健康狀態檢查。

gRPC 服務 (echo-grpcreverse-grpc) 會公開為 Kubernetes 無介面服務。 也就是說,沒有指派任何 clusterIP 位址,且 Kubernetes 網路 Proxy 並未將流量以負載平衡的方式傳到 Pod。相反地,叢集 DNS 服務中會建立包含 Pod IP 位址的 DNS A 記錄。Envoy 會從這個 DNS 項目探索 Pod IP 位址,並根據 Envoy 中設定的政策在這些位址之間進行負載平衡。

下圖顯示本教學課程中包含的 Kubernetes 物件:

本教學課程中使用的 Kubernetes 物件,包括服務、YAML 檔案、DNS A 記錄、密鑰、Pod 和 Proxy 項目。

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

完成本文所述工作後,您可以刪除已建立的資源,避免繼續計費。詳情請參閱清除所用資源一節。

事前準備

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

準備環境

  1. 在 Cloud Shell 中,設定要用於本教學課程的 Google Cloud 專案:

    gcloud config set project PROJECT_ID

    PROJECT_ID 替換為您的專案 ID。Google Cloud

  2. 啟用 Artifact Registry 和 GKE API:

    gcloud services enable artifactregistry.googleapis.com \
        container.googleapis.com
    

建立 GKE 叢集

  1. 在 Cloud Shell 中,建立執行 gRPC 服務的 GKE 叢集:

    gcloud container clusters create envoy-grpc-tutorial \
        --enable-ip-alias \
        --release-channel rapid \
        --scopes cloud-platform \
        --workload-pool PROJECT_ID.svc.id.goog \
        --zone us-central1-f
    

    本教學課程使用 us-central1-f 區域。您可以使用其他可用區或區域

  2. 列出叢集中的節點,驗證已設定 kubectl 內容:

    kubectl get nodes --output name
    

    輸出結果看起來與下列內容相似:

    node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-1kpt
    node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-qn92
    node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-wf2h
    

建立 Artifact Registry 存放區

  1. 在 Cloud Shell 中,建立新的存放區來儲存容器映像檔:

    gcloud artifacts repositories create envoy-grpc-tutorial-images \
        --repository-format docker \
        --location us-central1
    

    您會在與 GKE 叢集相同的地區中建立存放區,以便節點提取容器映像檔時,延遲時間和網路頻寬達到最佳化。

  2. 將存放區的「Artifact Registry 讀取者」角色授予 GKE 叢集節點 VM 使用的 Google 服務帳戶:

    PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'value(projectNumber)')
    
    gcloud artifacts repositories add-iam-policy-binding envoy-grpc-tutorial-images \
        --location us-central1 \
        --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
        --role roles/artifactregistry.reader
    
  3. 在 Cloud Shell 主目錄的 Docker 設定檔中,為存放區主機名稱新增憑證輔助程式項目:

    gcloud auth configure-docker us-central1-docker.pkg.dev
    

    憑證輔助程式項目可讓在 Cloud Shell 中執行的容器映像檔工具,向 Artifact Registry 存放區位置進行驗證,以便提取及推送映像檔。

部署 gRPC 服務

如要將流量轉送到一個負載平衡器後方的多個 gRPC 服務,請部署兩個範例 gRPC 服務:echo-grpcreverse-grpc。這兩個服務都會公開使用 content 要求欄位中的字串的一元方法。echo-grpc 會提供未變更內容的回應,而 reverse-grpc 則會提供將內容字串反轉的回應。

  1. 在 Cloud Shell 中,複製包含 gRPC 服務的存放區,然後切換到存放區目錄:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
    
  2. 建立自行簽署的 TLS 憑證和私密金鑰:

    openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 365 \
        -keyout privkey.pem -out cert.pem -extensions san \
        -config \
        <(echo "[req]";
          echo distinguished_name=req;
          echo "[san]";
          echo subjectAltName=DNS:grpc.example.com
         ) \
        -subj '/CN=grpc.example.com'
    
  3. 建立名為 envoy-certs 的 Kubernetes 密鑰,其中包含自行簽署的 TLS 憑證和私密金鑰:

    kubectl create secret tls envoy-certs \
        --key privkey.pem --cert cert.pem \
        --dry-run=client --output yaml | kubectl apply --filename -
    

    Envoy 會在終止 TLS 連線時使用這組 TLS 憑證和私密金鑰。

  4. 使用 Skaffold 建構範例應用程式 echo-grpcreverse-grpc 的容器映像檔、將映像檔推送至 Artifact Registry,並將應用程式部署至 GKE 叢集:

    skaffold run \
        --default-repo=us-central1-docker.pkg.dev/PROJECT_ID/envoy-grpc-tutorial-images \
        --module=echo-grpc,reverse-grpc \
        --skip-tests
    

    Skaffold 是 Google 推出的開放原始碼工具,可自動執行開發、建構、推送及部署容器化應用程式的工作流程。

  5. 使用 Skaffold 將 Envoy 部署至 GKE 叢集:

    skaffold run \
        --digest-source=none \
        --module=envoy \
        --skip-tests
    
  6. 確認每個部署作業都有兩個 Pod 準備就緒:

    kubectl get deployments
    

    輸出結果看起來與下列內容相似。所有部署作業的 READY 值都應為 2/2

    NAME           READY   UP-TO-DATE   AVAILABLE   AGE
    echo-grpc      2/2     2            2           1m
    envoy          2/2     2            2           1m
    reverse-grpc   2/2     2            2           1m
    
  7. 確認 echo-grpcenvoyreverse-grpc 是否皆以 Kubernetes 服務的形式存在:

    kubectl get services --selector skaffold.dev/run-id
    

    輸出結果看起來與下列內容相似。echo-grpcreverse-grpc 都應有 TYPE=ClusterIPCLUSTER-IP=None

    NAME           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)         AGE
    echo-grpc      ClusterIP      None          <none>           8081/TCP        2m
    envoy          LoadBalancer   10.40.2.203   203.0.113.1      443:31516/TCP   2m
    reverse-grpc   ClusterIP      None          <none>           8082/TCP        2m
    

測試 gRPC 服務

如要測試服務,請使用 grpcurl 指令列工具。

  1. 在 Cloud Shell 中安裝 grpcurl

    go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
    
  2. 取得 envoy Kubernetes 服務的外部 IP 位址,並儲存在環境變數中:

    EXTERNAL_IP=$(kubectl get service envoy \
        --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  3. 將要求傳送至 echo-grpc 範例應用程式:

    grpcurl -v -d '{"content": "echo"}' \
        -proto echo-grpc/api/echo.proto \
        -authority grpc.example.com -cacert cert.pem \
        $EXTERNAL_IP:443 api.Echo/Echo
    

    輸出結果看起來與下列內容相似:

    Resolved method descriptor:
    rpc Echo ( .api.EchoRequest ) returns ( .api.EchoResponse );
    
    Request metadata to send:
    (empty)
    
    Response headers received:
    content-type: application/grpc
    date: Wed, 02 Jun 2021 07:18:22 GMT
    hostname: echo-grpc-75947768c9-jkdcw
    server: envoy
    x-envoy-upstream-service-time: 3
    
    Response contents:
    {
      "content": "echo"
    }
    
    Response trailers received:
    (empty)
    Sent 1 request and received 1 response
    

    hostname 回應標頭會顯示已處理要求之 echo-grpc Pod 的名稱。如果您重複指令數次,應該會看到 hostname 回應標頭有兩個不同的值,與 echo-grpc Pod 的名稱相對應。

  4. 驗證 Reverse gRPC 服務的行為是否相同:

    grpcurl -v -d '{"content": "reverse"}' \
        -proto reverse-grpc/api/reverse.proto \
        -authority grpc.example.com -cacert cert.pem \
        $EXTERNAL_IP:443 api.Reverse/Reverse
    

    輸出結果看起來與下列內容相似:

    Resolved method descriptor:
    rpc Reverse ( .api.ReverseRequest ) returns ( .api.ReverseResponse );
    
    Request metadata to send:
    (empty)
    
    Response headers received:
    content-type: application/grpc
    date: Wed, 02 Jun 2021 07:20:15 GMT
    hostname: reverse-grpc-5c9b974f54-wlfwt
    server: envoy
    x-envoy-upstream-service-time: 1
    
    Response contents:
    {
      "content": "esrever"
    }
    
    Response trailers received:
    (empty)
    Sent 1 request and received 1 response
    

Envoy 設定

如要進一步瞭解 Envoy 設定,請參閱 Git 存放區中的設定檔 envoy/k8s/envoy.yaml

route_config 區段會指定傳入要求如何轉送至 echo-grpcreverse-grpc 範例應用程式。

route_config:
  name: local_route
  virtual_hosts:
  - name: local_service
    domains:
    - "*"
    routes:
    - match:
        prefix: "/api.Echo/"
      route:
        cluster: echo-grpc
    - match:
        prefix: "/api.Reverse/"
      route:
        cluster: reverse-grpc

範例應用程式定義為 Envoy 叢集

clusters:
- name: echo-grpc
  connect_timeout: 0.5s
  type: STRICT_DNS
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  http2_protocol_options: {}
  load_assignment:
    cluster_name: echo-grpc
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: echo-grpc.default.svc.cluster.local
              port_value: 8081
  health_checks:
    timeout: 1s
    interval: 10s
    unhealthy_threshold: 2
    healthy_threshold: 2
    grpc_health_check: {}

叢集定義中的 type: STRICT_DNSlb_policy: ROUND_ROBIN 欄位會指定 Envoy 對 address 欄位中指定的主機名稱執行 DNS 查詢,並在 DNS 查詢的回應中,對 IP 位址進行負載平衡。由於定義範例應用程式的 Kubernetes Service 物件指定了無標頭服務,因此回應包含多個 IP 位址。

http2_protocol_options 欄位指定 Envoy 對範例應用程式使用 HTTP/2 通訊協定。

health_checks 區段中的 grpc_health_check 欄位指定 Envoy 使用 gRPC 健康狀態檢查通訊協定,判斷範例應用程式的健康狀態。

疑難排解

如果您在使用本教學課程時發生問題,建議查看下列文件:

您也可以探索 Envoy 管理介面,使用 Envoy 設定診斷問題。

  1. 如要開啟管理介面,請從 Cloud Shell 將通訊埠轉送設定到其中一個 Envoy Pod 的 admin 通訊埠:

    kubectl port-forward \
        $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
    
  2. 請等到您在主控台中看到下列輸出:

    Forwarding from 127.0.0.1:8080 -> 8090
    
  3. 按一下 Cloud Shell 中的 [Web preview] (網頁預覽) 按鈕,然後選取 [Preview on port 8080] (透過以下通訊埠預覽:8080)。這會開啟新瀏覽器視窗,顯示管理介面。

    已選取預覽的 Envoy 管理介面

  4. 完成後,請切換回 Cloud Shell,然後按下 Control+C 以結束通訊埠轉送。

其他用於轉送 gRPC 流量的方式

您可以透過多種方式將這個解決方案修改為適合您的環境。

替代應用程式層負載平衡器

Envoy 提供的某些應用程式層功能也能由其他負載平衡解決方案提供:

  • 您可以改用全域外部應用程式負載平衡器或區域外部應用程式負載平衡器,相較於外部直通網路負載平衡器,使用外部應用程式負載平衡器可提供多項優勢,例如進階流量管理功能、代管 TLS 憑證,以及與 Cloud CDN、Google Cloud Armor 和 IAP 等其他 Google Cloud 產品整合。

    如果全域外部應用程式負載平衡器或區域外部應用程式負載平衡器提供的流量管理功能符合您的用途,且您不需要支援以用戶端憑證為基礎的驗證 (又稱雙向 TLS (mTLS) 驗證),建議使用這兩種負載平衡器。如需詳細資訊,請參閱下列文件:

  • 如果您使用 Cloud Service Mesh 或 Istio,可以使用其功能以負載平衡的方式轉送 gRPC 流量。Cloud Service Mesh 和 Istio 都提供Ingress Gateway,可部署為具有 Envoy 後端的外部直通式網路負載平衡器,與本教學課程中的架構類似。主要差異在於,Envoy 是透過 Istio 的流量轉送物件設定。

    如要讓本教學課程中的服務範例能在 Cloud Service Mesh 或 Istio 服務網格中轉送,您必須從 Kubernetes 服務資訊清單 (echo-service.yamlreverse-service.yaml) 中移除 clusterIP: None 這一行。這表示使用 Cloud Service Mesh 或 Istio 的服務探索和負載平衡功能,而不使用 Envoy 中的類似功能。

    如果您已使用 Cloud Service Mesh 或 Istio,建議使用 Ingress Gateway 將流量轉送至 gRPC 服務。

  • 您可以使用 NGINX 取代 Envoy,無論是部署的形式或是使用 Kubernetes 適用的 NGINX Ingress Controller。本教學課程使用 Envoy,因為它提供更進階的 gRPC 功能,例如 gRPC 健康狀態檢查通訊協定的支援。

內部虛擬私人雲端網路連線

如要在 GKE 叢集以外但只在虛擬私有雲網路內公開服務,您可以使用內部直通式網路負載平衡器內部應用程式負載平衡器

如要使用內部直通式網路負載平衡器取代外部直通式網路負載平衡器,請將註解 cloud.google.com/load-balancer-type: "Internal" 新增至 envoy-service.yaml 資訊清單。

如要使用內部應用程式負載平衡器,請參閱設定內部應用程式負載平衡器的 Ingress 說明文件。

清除所用資源

完成教學課程後,您可以清除所建立的資源,這樣資源就不會繼續使用配額,也不會產生費用。下列各節將說明如何刪除或關閉這些資源。

刪除專案

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

刪除資源

如要保留您在本教學課程中使用的 Google Cloud 專案,請刪除個別的資源:

  1. 在 Cloud Shell 中,刪除本機 Git 存放區複製項目:

    cd ; rm -rf kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
    
  2. 刪除 GKE 叢集:

    gcloud container clusters delete envoy-grpc-tutorial \
        --zone us-central1-f --async --quiet
    
  3. 刪除 Artifact Registry 中的存放區:

    gcloud artifacts repositories delete envoy-grpc-tutorial-images \
        --location us-central1 --async --quiet
    

後續步驟