Gatewayは扉を開け、VirtualServiceは道を作る
一言でいうと
Gatewayは、どのポート・ホスト・TLSでトラフィックを受けるかだけを決め、受けたあとのルーティングはすべてVirtualServiceが行います。両者をつなぐのは、VirtualServiceのgatewaysフィールドの1行です。
なぜ必要なのか
KubernetesのIngressは、1つのリソースに入口とルーティングが混在していて、表現力が不足していました。ヘッダーベースのルーティング、重みによる分配、TLSのパススルーモードのような要求は、すべてコントローラーごとのアノテーションに流れ込み、結果としてマニフェストが特定のIngressコントローラーに縛られてしまいました。
Istioは、この問題を責任の分離で解決しました。Gatewayはゲートウェイ用Podのリスナーを宣言し、ルーティングは、メッシュの内部で使っていたVirtualServiceをそのまま再利用します。おかげで、「内部のeast-westルーティング」と「外部から入ってくるnorth-southルーティング」を、同じ文法で書けるようになりました。
どう動くのか
Gatewayは、spec.selectorでどのゲートウェイ用Podにこの設定を付けるかを選びます。通常はistio: ingressgatewayです。Gatewayリソースはプロキシを作りません。すでに動いているゲートウェイのDeploymentに、リスナーの設定を載せるだけです。
TLSのモードは、3つを区別する必要があります。
| モード | ゲートウェイが行うこと | 必要なもの |
|---|---|---|
| SIMPLE | TLSを終端して、平文で後ろに渡します | サーバー証明書(credentialName) |
| MUTUAL | TLSの終端とクライアント証明書の検証 | サーバー証明書とCA |
| PASSTHROUGH | 終端せずに、SNIだけを見て渡します | 証明書は不要です |
PASSTHROUGHは、プロトコルをHTTPSではなくTLSと書く必要があります。ゲートウェイがHTTPをパースしないからで、そのため、この場合のルーティングもHTTPのルールではなく、tlsブロックのsniHostsで行います。credentialNameが指すkubernetes.io/tlsのSecretは、ゲートウェイ用Podが動いているネームスペースにある必要があります。アプリケーションのネームスペースに置いて、なぜ付かないのかと尋ねる事故がよくあります。
バインディングは、2つの条件がどちらも合って初めて成立します。VirtualServiceのgatewaysにGatewayの名前があり、hostsがGatewayのserver hostsと重なっている必要があります。メッシュ内部のトラフィックにも同じルールを適用するには、gatewaysに予約語のmeshを追加します。
ServiceEntryは反対方向、つまりメッシュの外のサービスを内側に連れてくるリソースです。登録すれば、外部APIにもタイムアウト・リトライ・ミラーリング・メトリクスを付けられます。resolutionは、STATIC(endpointsに書いたIPを使う)、DNS(名前を照会する)、NONE(元のリクエストのアドレスを使う)を区別しておけば十分です。メッシュ全体でoutboundTrafficPolicy: REGISTRY_ONLYを有効にすると、ServiceEntryにない外部ホストはブロックされます。
Sidecarリソースは性格が異なります。デフォルトでは、すべてのサイドカーが、メッシュのすべてのサービスの設定を受け取ります。サービスが数千個になると、この設定だけでEnvoyのメモリが数百MiBに膨れ上がります。Sidecarでegress.hostsを絞れば、そのワークロードが知る必要のある範囲だけが送られてきます。形式は네임스페이스/호스트で(プレースホルダーはネームスペースとホストです)、./*は自分のネームスペース、istio-system/*はコントロールプレーンのネームスペースです。
今後の方向性も知っておく必要があります。新規のIngressはKubernetes Gateway API(Gateway + HTTPRoute)で書くことが推奨され、アンビエントモードのwaypointプロキシ自体が、Gateway APIのGatewayリソースとして宣言されます。Istio独自のGateway/VirtualServiceは引き続きサポートされますが、新しい標準の機能は、Gateway APIのほうに先に載ります。
現場での姿
筆者のホームラボでは、CiliumのGateway API実装を立ち上げようとして、一度つまずきました。Gateway APIのCRDをv1.2でインストールしたところ、コントローラーが起動を拒否しましたが、原因は、tlsroutesとreferencegrantsがまだv1ではなかったからでした。CRDをv1.6.1に上げて、ようやくつながりました。
教訓は、Istioにもそのまま当てはまります。Gateway APIは、クラスターにCRDが先にある必要がある別のプロジェクトであり、実装(Istio、Cilium)とCRDのバージョンが合っている必要があります。「istioctl installしたのに、なぜHTTPRouteを作れないのか」という質問の答えがこれです。同じホームラボで、hubble-relayとhubble-uiがPendingのままだったのも、似た性質の問題でした。これらはDaemonSetではなくDeploymentなので、コントロールプレーンのtaintをトレレートせず、ワーカーがジョインするとすぐに解消しました。ゲートウェイのDeploymentも、同じ理由でスケジュールされないことがよくあります。
次のラボですること
バックエンドのワークロードとkubernetes.io/tlsのSecretを実際に作ったあと、SIMPLEとPASSTHROUGHのサーバーを1つのGatewayに入れ、VirtualServiceでバインドし、外部の決済APIをServiceEntryで登録してから、Sidecarで、そのワークロードの視野を3つの項目に絞ります。