【設定の配布先を意識しよう】IstioのVirtualServiceの設定内容でハマりがちな罠

2023/12/12に公開

本記事はUzabase Advent Calendar 2023の12日目の記事です。

https://qiita.com/advent-calendar/2023/uzabase

こんにちは。UzabaseでSREをしている、@yashirookといいます。

背景

UzabaseのSaaS Product TeamではSPEEDA, FORCAS, INITIALなど複数のSaaSプロダクトを開発しています。

プロダクトに関わる多くのシステムコンポーネントはGKE上に展開されており、日々開発チームがクラスタにマイクロサービスをリリースしています。

マイクロサービスを安全にリリースするため、Istioを使ってBlue/Green方式でリリースを行なっています。

マイクロサービスへのトラフィック制御に必要なIstioのカスタムリソース (VirtualServiceDestinationRuleなど) は基本的に開発チームでマニフェストを用意し、デプロイしています。

日々開発するなかで、アンチパターンとなる設定が本番環境に適用されてしまっていることが発覚したので、それがどんな内容であるのか、またなぜ発生するのかを VirtualService の設定がどのようにクラスタに適用されていくのかを説明しながら解説します。

まず結論

gatewaymeshとその他のgatewayが同時に設定されている場合は、クラスタ内でトラフィックが意図しない形でルーティングされてしまう可能性があり、クラスタ内部/外部の通信を意識して適切に設定を分離することが好ましいです。

ケーススタディ

たとえば、以下のようなマニフェストを見てみます。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: hoge-api-vs
  namespace: hoge
spec:
  gateways: # meshと他のgateway (istio-ingressgateway) が設定されている
  - istio-system/default-gateway 
  - mesh
  hosts:
  - hoge-api-svc.hoge.svc.cluster.local
  - hoge-api.ub-speeda.lan
  http:
  - route:
    - destination:
        host: hoge-api-svc.hoge.svc.cluster.local
        port:
          number: 16000

このマニフェストでは、Hostヘッダーがhoge-api-svc.hoge.svc.cluster.localhoge-api.ub-speeda.lanになっているリクエストを、Kubernetesの hoge-api-svc.hoge.svc.cluster.localにルーティングする設定が入っています。

設定の説明

hoge-api-svc.hoge.svc.cluster.localは、メッシュ内部のトラフィックを制御するために設定されており、hoge-api.ub-speeda.lanの設定は、Service Mesh内部のAPIをクラスタ外に公開するために設定されています。

これらのHostに対するリクエストは最終的にhoge-api-svc.hoge.svc.cluster.localのLabelSelectorにマッチするPodに到達します。

問題のある点

この設定は、hoge-api.ub-speeda.lanでアクセスしたいPodがクラスタ内部に存在する場合は目的のPodへルーティングされ、問題はありません。

図中のfuga-apiはhoge-api.ub-speeda.lanでhoge-apiにアクセスしていますが、これは問題ありません。

問題のない状況

なお、図でfuga-api->hoge-apiはクラスタ内部の通信ですが、マルチクラスタでシステムを構成しており、APIが存在するクラスタを意識せず接続することができるよう、敢えて Kuberentes 形式のホスト名(hoge-api-svc.hoge.svc.cluster.local)をしていないことがあります。

何かのきっかけでhoge-api.ub-speeda.lanでアクセスする先を別のKubernetesクラスタにデプロイしたPodへアクセスするようにDNSのレコードを変更しました(図中のBのPodへ切り替えた)。

これにより、hoge-api.ub-speeda.lanでアクセスしているアプリケーションは、透過的に向き先の切り替わったAPIを参照できることが期待されます。

しかし、この設定の入っているメッシュに属しているアプリケーションは、引き続き同じKubernetesクラスタのhoge-api-svc.hoge.svc.cluster.localにアクセスし続けてしまいます

このため、APIのデグレードが発生したり、最悪の場合参照されているにも関わらず移行が完了したものと判断し、削除されてしまったりする可能性があるため、このような設定は望ましくありません。

そしてこの運用はマルチクラスタでマイクロサービスを運用している弊社では、Kubernetesクラスタの移行、構成の最適化など、度々発生しており、意図しない挙動を防ぐために設定の改善が必要でした。

改善方法

上記の設定は、以下のようにKubernetesクラスタ内部と外部向けの通信に用いるVirtualServiceを分けて記載することで改善できます。

# クラスタ内部向けの設定
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: hoge-api-vs
  namespace: hoge
spec:
  gateways:
  - mesh
  hosts:
  - hoge-api-svc.hoge.svc.cluster.local
  http:
  - route:
    - destination:
        host: hoge-api-svc.hoge.svc.cluster.local
        port:
          number: 16000
---
# クラスタ外部向けの設定
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: hoge-api-vs-external
  namespace: hoge
spec:
  gateways:
  - istio-system/default-gateway
  hosts:
  - hoge-api.ub-speeda.lan
  http:
  - route:
    - destination:
        host: hoge-api-svc.hoge.svc.cluster.local
        port:
          number: 16000

ポイント

ポイントは、.gatewayの部分を分離し、それぞれに必要なhostのみを指定しているようにしている点です。

gatewayは、VirtualServiceの設定をどこに適用するかを指定しています。

  • Gatewayリソース名
    • Ingress/Egress Gatewayを指定するためのカスタムリソース
  • mesh
    • メッシュ全体に設定を適用する特別な予約語

の2通りの指定の仕方があります。

今回の場合、gatewayがistio-system/default-gatewayのものはデフォルトのIngressgatewayを指しており、外部からリクエストをプロキシするユースケースで利用されています。このため、hostの指定はhoge-api.ub-speeda.lanで十分です。

一方でmeshは前述した通りGatewayを除くメッシュ全体を指しており、外部からリクエストをプロキシするユースケースで利用されています。この場合は、hoge-api.ub-speeda.lanは必要でないため削るのが適しているでしょう。

まとめ

gatewaymeshとその他のgatewayが同時に設定されている場合は、クラスタ内でトラフィックが意図しない形でルーティングされてしまう可能性があります。

基本的にクラスタ/メッシュ内部用、外部用(Ingress/Egress)ごとにVirtualServiceを分けるスタンスが好ましいと考えます。

VirtualServiceで、配布する設定を必要最小限に保つことは、意図しないルーティングによる障害発生を防ぐために有効なので、意識してVirtualServiceを書いてみてください。

Discussion