【設定の配布先を意識しよう】IstioのVirtualServiceの設定内容でハマりがちな罠
本記事はUzabase Advent Calendar 2023の12日目の記事です。
こんにちは。UzabaseでSREをしている、@yashirookといいます。
背景
UzabaseのSaaS Product TeamではSPEEDA, FORCAS, INITIALなど複数のSaaSプロダクトを開発しています。
プロダクトに関わる多くのシステムコンポーネントはGKE上に展開されており、日々開発チームがクラスタにマイクロサービスをリリースしています。
マイクロサービスを安全にリリースするため、Istioを使ってBlue/Green方式でリリースを行なっています。
マイクロサービスへのトラフィック制御に必要なIstioのカスタムリソース (VirtualService
や DestinationRule
など) は基本的に開発チームでマニフェストを用意し、デプロイしています。
日々開発するなかで、アンチパターンとなる設定が本番環境に適用されてしまっていることが発覚したので、それがどんな内容であるのか、またなぜ発生するのかを VirtualService
の設定がどのようにクラスタに適用されていくのかを説明しながら解説します。
まず結論
gateway
に mesh
とその他の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.local
とhoge-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
は必要でないため削るのが適しているでしょう。
まとめ
gateway
に mesh
とその他のgatewayが同時に設定されている場合は、クラスタ内でトラフィックが意図しない形でルーティングされてしまう可能性があります。
基本的にクラスタ/メッシュ内部用、外部用(Ingress/Egress)ごとにVirtualService
を分けるスタンスが好ましいと考えます。
VirtualService
で、配布する設定を必要最小限に保つことは、意図しないルーティングによる障害発生を防ぐために有効なので、意識してVirtualService
を書いてみてください。
Discussion