Istio Ingress Gateway越しgRPC streamingのハマりポイント

 SREグループ・ヒロチカです。弊社の持つk8sクラスタの整理の一環で、gRPC streamingを使っているサービスについて、通信経路にIstio Ingress Gatewayを経由する形へと構成変更を行った際のポイントや知見をご紹介いたします。

はじめに

 SREグループ・ヒロチカです。GO株式会社では、サービスのクラウドインフラの設計から構築・運用までを担当しています。

 GO株式会社ではアプリケーション間のやり取りについてgRPCを採用する機会が多くあり、いくつかのアプリケーションではgRPC streamingの技術を利用した通信も行っています。

 今回、弊社の持つk8sクラスタの整理の一環で、gRPC streamingを使っているサービスの一つについて、通信経路をIstio Ingress Gatewayを経由する形へ構成変更を行いました。

 この構成変更は別クラスターにあるgRPC streamingを利用したサービスを、複数のアプリケーションを統合的に管理してる既存クラスターへと移す作業の中で発生した事案になります。

 これは、SREチームがインフラ管理をしたいアプリケーションについて、点在しているクラスターを集約し監視やデプロイ環境を統一させインフラコスト・運用コストを下げていくことを大きな目的としたもので、既存構成のままではなく統合的に管理しているクラスターの監視ログの取得やアラート管理方法と統一するために、Istio Ingress Gatewayを経由する形の構成へと変更していきました。

 上記作業の中でいくつかハマった点や知見があったため、こちらに書き残しておきたいと思います。

構成

変更前

変更前の構成図

変更後

変更後の構成図

  • gRPC ストリーミング技術にはいくつかの種類がありますが、今回の構成は双方向gRPCストリーミングによる通信となります
  • サーバ側の認証処理をGoogle Cloud PlatformのCloud Endpointsを利用して実現させるため、gRPC streamingのアプリケーションの前段にExtensible Service Proxy(ESP)を起動させています。

Cloud Endpoints / Extensible Service Proxy(ESP)

 今回移行したアプリケーションは、Google Cloud Platformで提供されている分散型API管理サービスのCloud Endpointsを利用しています。

 Cloud Endpointsは、APIの開発/デプロイ/保護/モニタリングなどを行うためのツールを提供してくれるプラットフォームで、効率的にAPIを管理する機能を持ちます。Extensible Service Proxy(ESP)という、認証、モニタリング、ロギングなどの API 管理機能を提供するために起動させるNGINXベースのプロキシを、gRPCのアプリケーションの前段部分にくるようコンテナで起動させ、Cloud Endpointsのプラットフォームで設定するAPIの管理ルールを適用します。

 今回のアプリケーションでは、API保護の部分にあたるアクセスユーザ認証のために利用しています。

参考: Cloud Endpointsについて

ポイント

TLS terminationされる場所の変更

 今までの構成は暗号化された通信をESPに振ったアドレスのport443(gRPC,TLS)で直受けしていましたが、Istioを経由することでESPの前段であるIstio Proxy部分までがHTTPSの通信、そこから先の内部の通信は平文のHTTP/2(gRPC)で通ることとなります。

 そのため、ESP側でgRPC通信を受ける箇所についてはport443のHTTPSではなく、平文のHTTP/2(gRPC)で受けられるように設定変更する必要がありました。

 ESPとして変更箇所は以下コメント部分になります。Istio側で接続する経路についてもhttp2_portのパラメータで指定したport番号へと向けます。

変更前(ESP起動パラメータ,パラメータは参考値)

- name: esp
        image: gcr.io/endpoints-release/endpoints-runtime:1
        args: [
          --service=endpoints.example-project-12345.cloud.goog
          --rollout_strategy=managed
          --ssl_port=443 # SSLの受け口
          --backend=grpc://127.0.0.1:8000
          --client_body_timeout=Ns
          --healthz=/healthz
        ]

変更後(ESP起動パラメータ,パラメータは参考値)

- name: esp
        image: gcr.io/endpoints-release/endpoints-runtime:1
        args: [
          --service=endpoints.example-project-12345.cloud.goog
          --rollout_strategy=managed
          --http2_port=8002 # HTTP/2の受け口
          --backend=grpc://127.0.0.1:8000
          --client_body_timeout=Ns
          --healthz=/healthz
        ]

NGINXのhealthcheck

 上記の変更後、ESPの起動がただしく行われているかk8s側からhealthcheckを投げる部分にて、正常なレスポンスが返ってこずにpodが起動していないとみなされてしまっていました。

 これはESP内で利用していたNGINXがHTTPと平文のHTTP/2の両方を同じportで同時に受けつけできないバージョンであったため、k8s側のhealthcheckがHTTP通信でheakcthcheckにアクセスしようとするも、HTTP/2に有効化されたportであるためアクセスできないと判断してしまっているようでした。

 そのため、今回ESPについてはhealthcheck用のportも並行で稼働させ、healthcheckについてはHTTP、gRPC streamingについてはHTTP/2を通るような形でESPを起動させました。

実際の設定は下記になります。

- name: esp
        image: gcr.io/endpoints-release/endpoints-runtime:1
        args: [
          --service=endpoints.example-project-12345.cloud.goog
          --rollout_strategy=managed
          --http_port=8001        # healthcheckの受け口
          --http2_port=8002       # HTTP/2の受け口
          --backend=grpc://127.0.0.1:8000
          --client_body_timeout=Ns
          --healthz=/healthz
        ]

 上記の例では、healthcheckは8001、gRPC streamingは8002に向けた通信を行うようにしました。

 SSL通信の場合は、HTTP/2と同じportでもhealthcheckが受け付けられたため、この辺りを意識していませんでした。また現在出ている1.25.1以降のNGINXのバージョンではHTTPとHTTP/2の両方を同じportで受けられるようにサポートされているようです。

 参考: https://trac.nginx.org/nginx/ticket/816

おわりに

 gRPC streamingのサーバ構成変更に伴う通信周りの設定変更についてご紹介しました。

 今回はESPを使った通信でしたが、ESP以外でもSSL通信を行っているパターンであれば同様のNGINXの調整が必要となってくるかと思われます。

 また今回の移行に関してはアプリケーション内部には大きく手を加えずまず移行することを前提としたため、既存構成のサービスで利用していたESPをそのまま流用しましたが、現在リリースされているEnvoyベースの Extensible Service Proxy V2(ESPv2) への移行や、監視まわりを別で担保できる環境が整ったことをきっかけとし、認証を別途機能として持たせESP自体を通さないような構成変更も今後の検討事項として挙げられます。

 この記事が、どなたかの参考になれば幸いです。