gRPC プロキシレスサービスメッシュ

gRPC のプロキシレスサービスメッシュ機能に対する Istio のサポートについて

2021年10月28日 | Steven Landow - Google 著

Istio は、xDS API として総称される一連のディスカバリー API を使用して、Envoy サイドカープロキシを動的に構成します。これらの API は、ユニバーサルデータプレーン API になることを目指しています。gRPC プロジェクトは、xDS API を大幅にサポートしており、これにより、Envoy サイドカーをデプロイしなくても gRPC ワークロードを管理できます。この統合について詳しくは、Megan Yahya による KubeCon EU 2021 の講演をご覧ください。gRPC のサポートに関する最新情報は、提案と実装状況で確認できます。

Istio 1.11 では、gRPC サービスを直接メッシュに追加するための実験的なサポートが追加されました。基本的なサービスディスカバリー、一部の VirtualService ベースのトラフィックポリシー、および相互 TLS をサポートしています。

サポートされている機能

gRPC 内の xDS API の現在の実装は、Envoy と比較していくつかの領域で制限されています。次の機能は動作するはずですが、これは網羅的なリストではなく、他の機能にも部分的な機能がある場合があります。

フォールト、リトライ、タイムアウト、ミラーリング、リライトルールなどの他の機能は、将来のリリースでサポートされる可能性があります。これらの機能の一部は gRPC での実装を待っており、他の機能は Istio でのサポートが必要です。gRPC での xDS 機能のステータスはこちらで確認できます。Istio のサポートのステータスは、将来の公式ドキュメントで提供されます。

アーキテクチャ概要

Diagram of how gRPC services communicate with the istiod
gRPC サービスが istiod と通信する方法の図

これはデータプレーン通信にプロキシを使用しませんが、初期化とコントロールプレーンとの通信にはエージェントが必要です。まず、エージェントは、Envoy 用のブートストラップを生成するのと同じ方法で、起動時にブートストラップファイルを生成します。これにより、gRPC ライブラリに、istiod に接続する方法、データプレーン通信用の証明書を見つける場所、コントロールプレーンに送信するメタデータが指示されます。次に、エージェントはアプリケーションの代わりに、istiod と接続して認証する xDS プロキシとして機能します。最後に、エージェントはデータプレーンのトラフィックで使用される証明書を取得およびローテーションします。

アプリケーションコードの変更

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 に渡す必要があります。FallbackCreds を使用すると、istiod がセキュリティ設定を送信しない場合に成功できます。

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 デプロイの場合

アプリケーションコードに互換性があることを前提とすると、ポッドにはアノテーション inject.istio.io/templates: grpc-agent のみが必要です。これにより、上記のエージェントを実行するサイドカーコンテナと、gRPC がブートストラップファイルを見つけ、特定の機能を有効にするために使用するいくつかの環境変数が追加されます。

gRPC サーバーの場合、ポッドには、エージェント内 xDS プロキシとブートストラップファイルが gRPC サーバーが初期化される前に準備ができていることを確認するために、proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}' というアノテーションも付ける必要があります。

このガイドでは、サーバー側とクライアント側のプロキシレス gRPC の両方をすでにサポートしているアプリケーションである echo をデプロイします。このアプリを使用すると、mTLS を有効にするいくつかのサポートされているトラフィックポリシーを試すことができます。

前提条件

このガイドでは、続行する前に Istio (1.11+) コントロールプレーンがインストールされている必要があります。

アプリケーションをデプロイする

インジェクション対応の名前空間 echo-grpc を作成します。次に、echo アプリの 2 つのインスタンスとサービスをデプロイします。

$ 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

2 つのポッドが実行されていることを確認してください

$ 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 リゾルバーをテストする

まず、ポッドの 1 つにポート 17171 をポートフォワードします。このポートは、ポートフォワードされたポッドからのリクエストを可能にする、非 xDS バックの gRPC サーバーです。

$ 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

Destination Rule を使用してサブセットを作成する

まず、ワークロードの各バージョンに対してサブセットを作成します。

$ 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 のリソース使用量への影響を示すのに役立ちます。

この投稿を共有する