gRPC プロキシレスサービスメッシュ
gRPC のプロキシレスサービスメッシュ機能に対する Istio のサポートについて
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 サービスは、メッシュに登録されている他のポッドや仮想マシンに到達できます。
DestinationRule
:- サブセット:gRPC サービスは、ラベルセレクターに基づいて、トラフィックを異なるインスタンスグループに分割できます。
- 現在サポートされている Istio の
loadBalancer
はROUND_ROBIN
のみです。consistentHash
は、将来のバージョンの Istio で追加される予定です(gRPC でサポートされています)。 tls
設定はDISABLE
またはISTIO_MUTUAL
に制限されています。他のモードはDISABLE
として扱われます。
VirtualService
:- 形式
/ServiceName/RPCName
のヘッダーマッチと URI マッチ。 - 宛先ホストとサブセットのオーバーライド。
- 加重トラフィックシフト。
- 形式
PeerAuthentication
:DISABLE
とSTRICT
のみがサポートされています。他のモードはDISABLE
として扱われます。- 自動 mTLS のサポートは、将来のリリースで存在する可能性があります。
フォールト、リトライ、タイムアウト、ミラーリング、リライトルールなどの他の機能は、将来のリリースでサポートされる可能性があります。これらの機能の一部は gRPC での実装を待っており、他の機能は Istio でのサポートが必要です。gRPC での xDS 機能のステータスはこちらで確認できます。Istio のサポートのステータスは、将来の公式ドキュメントで提供されます。
アーキテクチャ概要
これはデータプレーン通信にプロキシを使用しませんが、初期化とコントロールプレーンとの通信にはエージェントが必要です。まず、エージェントは、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
制限事項
最初のリリースには、将来のバージョンで修正される可能性があるいくつかの制限事項があります
- 自動 mTLS はサポートされておらず、パーミッシブモードはサポートされていません。代わりに、サーバーでは
STRICT
で、クライアントではISTIO_MUTUAL
で明示的な mTLS 構成が必要です。Envoy は、STRICT
への移行中に使用できます。 - ブートストラップが書き込まれる前、または xDS プロキシの準備が整う前に呼び出された
grpc.Serve(listener)
またはgrpc.Dial("xds:///...")
が原因で、障害が発生する可能性があります。これを回避するにはholdApplicationUntilProxyStarts
を使用するか、アプリケーションがこれらの障害に対してより堅牢になるようにできます。 - xDS 対応の gRPC サーバーが mTLS を使用している場合は、ヘルスチェックがこれを回避して動作できることを確認する必要があります。別のポートを使用するか、ヘルスチェッククライアントが適切なクライアント証明書を取得する方法が必要です。
- gRPC での xDS の実装は Envoy と一致しません。特定の動作が異なる場合があり、一部の機能が欠落している場合があります。gRPC の機能ステータスで、詳細を確認できます。Istio 構成がプロキシレス gRPC アプリに実際に適用されることを必ずテストしてください。
パフォーマンス
実験設定
- Go ベースの負荷テストアプリである Fortio を使用する
- gRPC の XDS 機能をサポートするようにわずかに変更 (PR)
- リソース
- 3 つの
e2-standard-16
ノード (それぞれ 16 CPU + 64 GB メモリ) を備えた GKE 1.20 クラスター - Fortio クライアントおよびサーバーアプリ:1.5 vCPU、1000 MiB メモリ
- サイドカー (istio-agent および場合によっては Envoy プロキシ): 1 vCPU、512 MiB メモリ
- 3 つの
- テストされたワークロードの種類
- ベースライン:Envoy プロキシまたはプロキシレス xDS を使用しない通常の gRPC
- Envoy:標準の istio-agent + Envoy プロキシサイドカー
- プロキシレス:クライアントで xDS gRPC サーバーの実装と
xds:///
リゾルバーを使用する gRPC 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 のリソース使用量への影響を示すのに役立ちます。