gRPC 無代理服務網格

Istio 對 gRPC 無代理服務網格功能支援的介紹。

2021 年 10 月 28 日 | 作者:Steven Landow - Google

Istio 使用一組稱為 xDS API 的探索 API 來動態配置其 Envoy Sidecar 代理。這些 API 的目標是成為 通用資料平面 API。gRPC 專案對 xDS API 有重要的支援,這表示您可以管理 gRPC 工作負載,而無需部署 Envoy Sidecar。您可以在 Megan Yahya 在 KubeCon EU 2021 的演講中了解更多關於整合的資訊。關於 gRPC 支援的最新更新,可以在他們的 提案及其實現狀態中找到。

Istio 1.11 新增了將 gRPC 服務直接新增至網格的實驗性支援。我們支援基本服務探索、一些基於 VirtualService 的流量策略以及相互 TLS。

支援的功能

與 Envoy 相比,目前在 gRPC 中實作的 xDS API 在某些方面受到限制。以下功能應該可以運作,儘管這並非詳盡列表,其他功能可能具有部分功能

其他功能,包括故障、重試、逾時、鏡像和重寫規則,可能會在未來版本中支援。其中某些功能正在等待 gRPC 中實作,其他功能則需要 Istio 中的工作才能支援。可以在這裡找到 gRPC 中 xDS 功能的狀態。Istio 支援的狀態將在未來的官方文件中提供。

架構概述

Diagram of how gRPC services communicate with the istiod
gRPC 服務如何與 istiod 通訊的圖表

儘管這不使用代理進行資料平面通訊,但仍然需要代理來進行初始化並與控制平面通訊。首先,代理會在啟動時產生一個引導檔案,就像它會為 Envoy 產生引導檔案一樣。這會告訴 gRPC 程式庫如何連線到 istiod,在哪裡可以找到資料平面通訊的憑證,以及要將哪些中繼資料傳送至控制平面。接下來,代理充當 xDS 代理,代表應用程式與 istiod 連線並進行驗證。最後,代理會擷取並輪替用於資料平面流量的憑證。

對應用程式程式碼的變更

若要在 gRPC 中啟用 xDS 功能,您的應用程式必須進行一些必要的變更。您的 gRPC 版本應至少為 1.39.0

在用戶端中

以下副作用匯入會在 gRPC 中註冊 xDS 解析器和平衡器。它應該新增至您的 main 封裝或呼叫 grpc.Dial 的相同封裝中。

import _ "google.golang.org/grpc/xds"

建立 gRPC 連線時,URL 必須使用 xds:/// 協定。

conn, err := grpc.DialContext(ctx, "xds:///foo.ns.svc.cluster.local:7070")

此外,為了 (m)TLS 支援,必須將特殊的 TransportCredentials 選項傳遞至 DialContext。當 istiod 未傳送安全性組態時,FallbackCreds 可讓我們成功。

import "google.golang.org/grpc/credentials/xds"

...

creds, err := xds.NewClientCredentials(xds.ClientOptions{
FallbackCreds: insecure.NewCredentials()
})
// handle err
conn, err := grpc.DialContext(
ctx,
"xds:///foo.ns.svc.cluster.local:7070",
grpc.WithTransportCredentials(creds),
)

在伺服器上

為了支援伺服器端組態(例如 mTLS),必須進行一些修改。

首先,我們使用特殊的建構函式來建立 GRPCServer

import "google.golang.org/grpc/xds"

...

server = xds.NewGRPCServer()
RegisterFooServer(server, &fooServerImpl)

如果您的 protoc 產生的 Go 程式碼已過期,您可能需要重新產生它才能與 xDS 伺服器相容。您產生的 RegisterFooServer 函式應如下所示

func RegisterFooServer(s grpc.ServiceRegistrar, srv FooServer) {
s.RegisterService(&FooServer_ServiceDesc, srv)
}

最後,與用戶端變更一樣,我們必須啟用安全性支援

creds, err := xds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
// handle err
server = xds.NewGRPCServer(grpc.Creds(creds))

在您的 Kubernetes 部署中

假設您的應用程式程式碼相容,Pod 只需要註釋 inject.istio.io/templates: grpc-agent。這會新增一個 Sidecar 容器,其中執行上述代理,以及一些 gRPC 用來尋找引導檔案並啟用某些功能的環境變數。

對於 gRPC 伺服器,您的 Pod 也應該使用 proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}' 進行註釋,以確保在初始化您的 gRPC 伺服器之前,代理中的 xDS 代理和引導檔案已準備就緒。

範例

在本指南中,您將部署 echo,這是一個已經支援伺服器端和用戶端無代理 gRPC 的應用程式。使用此應用程式,您可以嘗試一些支援的流量策略來啟用 mTLS。

先決條件

本指南需要先安裝 Istio (1.11+) 控制平面,然後才能繼續。

部署應用程式

建立啟用注入的命名空間 echo-grpc。接下來,部署兩個 echo 應用程式的執行個體以及服務。

$ kubectl create namespace echo-grpc
$ kubectl label namespace echo-grpc istio-injection=enabled
$ kubectl -n echo-grpc apply -f samples/grpc-echo/grpc-echo.yaml

請確定這兩個 Pod 正在執行中

$ kubectl -n echo-grpc get pods
NAME                       READY   STATUS    RESTARTS   AGE
echo-v1-69d6d96cb7-gpcpd   2/2     Running   0          58s
echo-v2-5c6cbf6dc7-dfhcb   2/2     Running   0          58s

測試 gRPC 解析器

首先,將 17171 連接埠轉送到其中一個 Pod。此連接埠是不支援 xDS 的 gRPC 伺服器,可讓您從連接埠轉送的 Pod 發出請求。

$ kubectl -n echo-grpc port-forward $(kubectl -n echo-grpc get pods -l version=v1 -ojsonpath='{.items[0].metadata.name}') 17171 &

接下來,我們可以發出一批 5 個請求

$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070", "count": 5}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'  | grep Hostname
Handling connection for 17171
[0 body] Hostname=echo-v1-7cf5b76586-bgn6t
[1 body] Hostname=echo-v2-cf97bd94d-qf628
[2 body] Hostname=echo-v1-7cf5b76586-bgn6t
[3 body] Hostname=echo-v2-cf97bd94d-qf628
[4 body] Hostname=echo-v1-7cf5b76586-bgn6t

您也可以使用類似 Kubernetes 的名稱解析來表示簡短名稱

$ grpcurl -plaintext -d '{"url": "xds:///echo:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join
("")'  | grep Hostname
[0 body] Hostname=echo-v1-7cf5b76586-ltr8q
$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r
'.output | join("")'  | grep Hostname
[0 body] Hostname=echo-v1-7cf5b76586-ltr8q
$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r
'.output | join("")'  | grep Hostname
[0 body] Hostname=echo-v2-cf97bd94d-jt5mf

使用目的地規則建立子集

首先,為每個工作負載版本建立一個子集。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: echo-versions
  namespace: echo-grpc
spec:
  host: echo.echo-grpc.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
EOF

流量轉移

使用上述定義的子集,您可以將 80% 的流量傳送到特定版本

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: echo-weights
  namespace: echo-grpc
spec:
  hosts:
  - echo.echo-grpc.svc.cluster.local
  http:
  - route:
    - destination:
        host: echo.echo-grpc.svc.cluster.local
        subset: v1
      weight: 20
    - destination:
        host: echo.echo-grpc.svc.cluster.local
        subset: v2
      weight: 80
EOF

現在,傳送一組 10 個請求

$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070", "count": 10}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'  | grep ServiceVersion

回應應包含大部分 v2 回應

[0 body] ServiceVersion=v2
[1 body] ServiceVersion=v2
[2 body] ServiceVersion=v1
[3 body] ServiceVersion=v2
[4 body] ServiceVersion=v1
[5 body] ServiceVersion=v2
[6 body] ServiceVersion=v2
[7 body] ServiceVersion=v2
[8 body] ServiceVersion=v2
[9 body] ServiceVersion=v2

啟用 mTLS

由於在 gRPC 中啟用安全性所需的應用程式本身發生變更,Istio 自動偵測 mTLS 支援的傳統方法不可靠。因此,初始版本需要在用戶端和伺服器上明確啟用 mTLS。

若要啟用用戶端 mTLS,請套用具有 tls 設定的 DestinationRule

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: echo-mtls
  namespace: echo-grpc
spec:
  host: echo.echo-grpc.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
EOF

現在,嘗試呼叫尚未針對 mTLS 設定的伺服器將會失敗。

$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
Handling connection for 17171
ERROR:
Code: Unknown
Message: 1/1 requests had errors; first error: rpc error: code = Unavailable desc = all SubConns are in TransientFailure

若要啟用伺服器端 mTLS,請套用 PeerAuthentication

$ cat <<EOF | kubectl apply -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: echo-mtls
  namespace: echo-grpc
spec:
  mtls:
    mode: STRICT
EOF

套用原則後,請求將開始成功。

$ grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
Handling connection for 17171
[0] grpcecho.Echo(&{xds:///echo.echo-grpc.svc.cluster.local:7070 map[] 0  5s false })
[0 body] x-request-id=0
[0 body] Host=echo.echo-grpc.svc.cluster.local:7070
[0 body] content-type=application/grpc
[0 body] user-agent=grpc-go/1.39.1
[0 body] StatusCode=200
[0 body] ServiceVersion=v1
[0 body] ServicePort=17070
[0 body] Cluster=
[0 body] IP=10.68.1.18
[0 body] IstioVersion=
[0 body] Echo=
[0 body] Hostname=echo-v1-7cf5b76586-z5p8l

限制

初始版本有一些限制,可能會在未來版本中修正

效能

實驗設定

延遲

p50 latency comparison chart
p50 延遲比較圖表
p99 latency comparison chart
p99 延遲比較圖表

使用無代理 gRPC 解析器時,延遲會略微增加。與 Envoy 相比,這是一個巨大的改進,它仍然允許使用進階流量管理功能和 mTLS。

istio-proxy 容器資源使用率

用戶端 mCPU用戶端記憶體 (MiB)伺服器 mCPU伺服器記憶體 (MiB)
Envoy 純文字320.4466.93243.7864.91
Envoy mTLS340.8766.76309.8264.82
無代理純文字0.7223.540.8424.31
無代理 mTLS0.7325.050.7825.43

即使我們仍然需要一個代理程式,該代理程式使用的資源也少於一個完整 vCPU 的 0.1%,並且僅佔用 25 MiB 的記憶體,這還不到執行 Envoy 所需資源的一半。

這些指標不包括應用程式容器中 gRPC 的額外資源使用量,但可以說明 istio-agent 在此模式下執行時對資源使用的影響。

分享此文章