gRPC 無代理服務網格
Istio 對 gRPC 無代理服務網格功能支援的介紹。
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 服務可以連線到網格中註冊的其他 Pod 和虛擬機器。
DestinationRule
:- 子集:您的 gRPC 服務可以根據標籤選取器將流量分割到不同的執行個體群組。
- 目前唯一支援的 Istio
loadBalancer
是ROUND_ROBIN
,consistentHash
將在未來版本的 Istio 中加入(gRPC 支援)。 tls
設定僅限於DISABLE
或ISTIO_MUTUAL
。其他模式將被視為DISABLE
。
VirtualService
:- 標頭比對和 URI 比對的格式為
/ServiceName/RPCName
。 - 覆寫目標主機和子集。
- 加權流量轉移。
- 標頭比對和 URI 比對的格式為
PeerAuthentication
:- 僅支援
DISABLE
和STRICT
。其他模式將被視為DISABLE
。 - 未來版本可能會支援自動 mTLS。
- 僅支援
其他功能,包括故障、重試、逾時、鏡像和重寫規則,可能會在未來版本中支援。其中某些功能正在等待 gRPC 中實作,其他功能則需要 Istio 中的工作才能支援。可以在這裡找到 gRPC 中 xDS 功能的狀態。Istio 支援的狀態將在未來的官方文件中提供。
架構概述
儘管這不使用代理進行資料平面通訊,但仍然需要代理來進行初始化並與控制平面通訊。首先,代理會在啟動時產生一個引導檔案,就像它會為 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
限制
初始版本有一些限制,可能會在未來版本中修正
- 不支援自動 mTLS,也不支援允許模式。相反地,我們需要在伺服器上使用
STRICT
,在用戶端上使用ISTIO_MUTUAL
來明確配置 mTLS。在遷移至STRICT
的過程中可以使用 Envoy。 - 在寫入引導或 xDS 代理準備就緒之前呼叫
grpc.Serve(listener)
或grpc.Dial("xds:///...")
可能會導致失敗。可以使用holdApplicationUntilProxyStarts
來解決此問題,或者應用程式可以更穩健地應對這些失敗。 - 如果已啟用 xDS 的 gRPC 伺服器使用 mTLS,則您需要確保您的健康檢查可以解決此問題。應該使用個別的連接埠,或者您的健康檢查用戶端需要一種方法來取得正確的用戶端憑證。
- gRPC 中 xDS 的實作與 Envoy 不符。某些行為可能會有所不同,並且可能會遺失某些功能。gRPC 的功能狀態提供了更多詳細資訊。請務必測試任何 Istio 組態是否實際套用在您的無代理 gRPC 應用程式上。
效能
實驗設定
- 使用 Fortio,這是一個基於 Go 的負載測試應用程式
- 稍作修改,以支援 gRPC 的 XDS 功能 (PR)
- 資源
- 具有 3 個
e2-standard-16
節點(每個節點 16 個 CPU + 64 GB 記憶體)的 GKE 1.20 叢集 - Fortio 用戶端和伺服器應用程式:1.5 個 vCPU、1000 MiB 記憶體
- Sidecar (istio-agent 和可能的 Envoy 代理):1 個 vCPU、512 MiB 記憶體
- 具有 3 個
- 測試的工作負載類型
- 基準:未使用 Envoy 代理或無代理 xDS 的常規 gRPC
- Envoy:標準 istio-agent + Envoy 代理 Sidecar
- 無代理:gRPC 在用戶端上使用 xDS gRPC 伺服器實作和
xds:///
解析器 - 透過
PeerAuthentication
和DestinationRule
啟用/停用 mTLS
延遲
使用無代理 gRPC 解析器時,延遲會略微增加。與 Envoy 相比,這是一個巨大的改進,它仍然允許使用進階流量管理功能和 mTLS。
istio-proxy 容器資源使用率
用戶端 mCPU | 用戶端記憶體 (MiB ) | 伺服器 mCPU | 伺服器記憶體 (MiB ) | |
---|---|---|---|---|
Envoy 純文字 | 320.44 | 66.93 | 243.78 | 64.91 |
Envoy mTLS | 340.87 | 66.76 | 309.82 | 64.82 |
無代理純文字 | 0.72 | 23.54 | 0.84 | 24.31 |
無代理 mTLS | 0.73 | 25.05 | 0.78 | 25.43 |
即使我們仍然需要一個代理程式,該代理程式使用的資源也少於一個完整 vCPU 的 0.1%,並且僅佔用 25 MiB 的記憶體,這還不到執行 Envoy 所需資源的一半。
這些指標不包括應用程式容器中 gRPC 的額外資源使用量,但可以說明 istio-agent 在此模式下執行時對資源使用的影響。