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

Last reviewed 2019-05-30 UTC

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

簡介

gRPC 是一種開放原始碼、與語言無關的 RPC 架構,以 HTTP/2 為基礎,使用通訊協定緩衝區,可有效率地表示傳輸中的資料,並快速序列化。gRPC 的靈感來自 Google 內部 RPC 架構 Stubby,可在微服務之間,以及行動用戶端和 API 之間,實現低延遲通訊。

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

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

Envoy 是一種開放原始碼應用程式層 (第 7 層) Proxy,可提供許多進階功能。在本教學課程中,您會使用 Nginx Ingress 終止 TLS 連線,並將 gRPC 流量路由至適當的 Kubernetes Service。與 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-grpc 和 reverse-grpc),並透過公開 IP 位址將服務公開至網際網路。下圖顯示透過單一端點公開這兩個服務的架構:

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

區域性外部直通式網路負載平衡器會接受來自網際網路的連入要求 (例如來自行動用戶端或貴公司外部的服務消費者)。區域性外部直通式網路負載平衡器會執行下列工作:

  • 將傳入的連線負載平衡至集區中的節點。流量會轉送至 envoy Kubernetes Service,該 Service 會在叢集中的所有節點上公開。Kubernetes 網路會透過 Proxy 將這些連線轉送到執行 Envoy 的 Pod。
  • 對叢集中的節點執行 HTTP 健康狀態檢查。

Envoy 會執行下列工作:

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

gRPC 服務 (echo-grpc 和 reverse-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. 在 Google Cloud 控制台的專案選擇器頁面中,選取或建立 Google Cloud 專案。

    選取或建立專案所需的角色

    • 選取專案:選取專案時,不需要具備特定 IAM 角色,只要您在專案中獲派角色,即可選取該專案。
    • 建立專案:如要建立專案,您需要專案建立者角色 (roles/resourcemanager.projectCreator),其中包含 resourcemanager.projects.create 權限。瞭解如何授予角色。

    前往專案選取器

  2. 確認 Google Cloud 專案已啟用計費功能。

  3. 在 Google Cloud 控制台中啟用 Cloud Shell。

    啟用 Cloud Shell

準備環境

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

    gcloud config set project PROJECT_ID

    將 PROJECT_ID 替換為Google Cloud 專案 ID。

  2. 啟用 Artifact Registry 和 GKE API:

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

建立 GKE 叢集

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

    gcloud container clusters create envoy-grpc-tutorial \
        --enable-ip-alias \
        --release-channel rapid \
        --scopes cloud-platform \
        --workload-pool PROJECT_ID.svc.id.goog \
        --location 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-grpc 和 reverse-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 Secret,其中包含自行簽署的 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-grpc 和 reverse-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. 確認每個 Deployment 都有兩個 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-grpc、envoy 和 reverse-grpc 是否為 Kubernetes 服務:

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

    輸出結果看起來與下列內容相似。echo-grpc 和 reverse-grpc 都應有 TYPE=ClusterIP 和 CLUSTER-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-grpc 和 reverse-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_DNS 和 lb_policy: ROUND_ROBIN 欄位指定 Envoy 對 address 欄位中指定的主機名稱執行 DNS 查詢,並在 DNS 查詢的回應中對 IP 位址進行負載平衡。回應包含多個 IP 位址,因為定義範例應用程式的 Kubernetes Service 物件指定了無頭服務。

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 提供的某些應用程式層功能也能由其他負載平衡解決方案提供:

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

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

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

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

    如果您已使用 Cloud Service Mesh 或 Istio,建議使用 Ingress 閘道將流量導向 gRPC 服務。

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

內部虛擬私有雲網路連線

如要將服務公開至 GKE 叢集外部,但僅限於虛擬私有雲網路內部,可以使用內部直通式網路負載平衡器或內部應用程式負載平衡器。

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

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

清除所用資源

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

刪除專案

  1. 前往 Google Cloud 控制台的「Manage resources」(管理資源) 頁面。

    前往「Manage resources」(管理資源)

  2. 在專案清單中選取要刪除的專案,然後點選「Delete」(刪除)。
  3. 在對話方塊中輸入專案 ID,然後按一下 [Shut down] (關閉) 以刪除專案。

刪除資源

如要保留在本教學課程中使用的 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 \
        --location us-central1-f --async --quiet
    
  3. 刪除 Artifact Registry 中的存放區:

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

後續步驟