IstioのTraffic Managementの動作イメージを掴もう
本記事について
Istioを使う上で、Traffic Managementを司るVirtual Service, DestinationRuleについて、 どう作用しているのかという点がわかりにくかったため、本記事にて整理し実際に動かしながら検証する。 前提として、サービスメッシュやEnvoyの基本的な設定についてはざっくりと理解している読者を想定している。
Istioとは?
Istioは、サービスメッシュの機能を統括的に導入、管理を行うためのOSSである。 内部ではEnvoyを使用していて、IstioとしてはとしてはこのEnvoyのコントロールプレーンとしての機能も担っている。 What is Istio?
主に、
Traffic Management
- 割合やヘッダーによるルーティングのコントロールやロードバランシングなどを行うための機能
- Envoyの機能であるような、サーキットブレーカー、Fault Injectionなどの機能も基本的に使える
Observability
- Promheusなどメトリクスを集計するためのソフトウェアにデータを転送する機能
Security
- mTLS通信による認証された通信や、JWTを活用した認可処理をプロキシとして挟んだりすることができる
- mTLSに用いられる証明書はIstioが独自証明書を発行してくれ、特に意識せずともmTLSをサービス間通信に導入することができる
といった機能を提供しており、Kubernetsなどのプラットフォーム連携が充実している。
Traffic Managementについて
Istioは、k8sにデプロイされたPodに自動的にサイドカーとしてEnvoyベースのProxyをInjectする機能が備わっている。 基本的には、リクエスト元サービス -> Envoy -> Envoy -> リクエスト先サービスという流れで通信することになる。 そして、このEnvoyが実際にルーティングの制御を行っている。 このEnvoyに設定情報を提供するのがk8sのCRDである下記3種類のリソースである。
- Virtual Service
- DestinationRule
各リソースについての概念説明などはなどは、 公式ドキュメント に記載されている通りである。 このさきは、minikubeにIstioと、Istioのデモアプリである、Bookinfo をデプロイして挙動を確認していく。
事前準備
- minikube v1.9.2
- Istio v1.5.2
- Kubernets v1.18.0
Ingress Gatewayの実態を見てみよう
初期状態
Ingress Gatewayを有効にしてIstioをk8sのクラスタにインストールすると、
- Service(Load Balancer Type)
- Deployment
のリソースがそれぞれ作られていることが分かる。
❯❯❯ kubectl get service -n istio-system istio-ingressgateway LoadBalancer 10.103.255.236 10.103.255.236 15020:31097/TCP,80:31587/TCP,443:32153/TCP,15029:30056/TCP,15030:32475/TCP,15031:32379/TCP,15032:32322/TCP,31400:32143/TCP,15443:32376/TCP 3d15h
❯❯❯ kubectl get deployments.apps -n istio-system NAME READY UP-TO-DATE AVAILABLE AGE istio-ingressgateway 1/1 1 1 3d15h
そして、このDeploymentによって管理されているPodの定義を見てみると、
❯❯❯ kubectl get pod -n istio-system istio-ingressgateway-6489d9556d-sp8f2 -o yaml | grep image: image: docker.io/istio/proxyv2:1.5.2
のようになっているのが分かる。 このproxyvというのが中でEnvoyが動いてる。 この状態でRoutingの設定を覗いてみると、まだなにも定義していないためすべて404になるようになっている。
❯❯❯ istioctl -n istio-system proxy-config route istio-ingressgateway-6489d9556d-sp8f2 -o json | yq -y '.' - - name: http.80 virtualHosts: - name: blackhole:80 domains: - '*' routes: - name: default match: prefix: / directResponse: status: 404 validateClusters: false
GatewayリソースとGatewayリソースに紐付けたVirtualServiceの作成
では次に下記リソースをapplyし、IngressGatewayにAttachしたGatewayリソースと、VirtualServiceを作ってみる。
❯❯❯ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
そうすると、下記のようなルーティングルールが作成されている。
❯❯❯ istioctl -n istio-system proxy-config route istio-ingressgateway-6489d9556d-sp8f2 -o json | yq -y '.' - - name: http.80 virtualHosts: - name: '*:80' domains: - '*' - '*:80' routes: ... - match: path: /productpage caseSensitive: true route: cluster: outbound|9080||productpage.default.svc.cluster.local timeout: 0s retryPolicy: retryOn: connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes numRetries: 2 retryHostPredicate: - name: envoy.retry_host_predicates.previous_hosts hostSelectionRetryMaxAttempts: '5' retriableStatusCodes: - 503 maxGrpcTimeout: 0s metadata: filterMetadata: istio: config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo decorator: operation: productpage.default.svc.cluster.local:9080/productpage ...
このように、IngressGatewayにAttachしたGatewayを作成し、さらにそのGateway指定したVirtual Serviceを作ると、 L4/LBから直接ルーティングされてくるEnvoyにルーティングルールが追加される。 また、向き先としてはPodのIPが解決されている。
❯❯❯ istioctl -n istio-system proxy-config endpoint istio-ingressgateway-6489d9556d-sp8f2 -o json | yq -y '.' - | grep "outbound|9080||productpage.default.svc.cluster.local" -A 30 - name: outbound|9080||productpage.default.svc.cluster.local addedViaApi: true hostStatuses: - address: socketAddress: address: 172.17.0.12 portValue: 9080 weight: 1
Gatewayに紐付けないVirtualService
次に下記リソースをapplyし、reviewsサービスにはv2に50%、v3に50%の割合でルーティングされるようにしてみる。
❯❯❯ kubectl apply -f samples/bookinfo/networking/destination-rule-reviews.yaml ❯❯❯ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-v2-v3.yaml
まずIngress Gateway側のEnvoyの設定を確認する。
❯❯❯ istioctl -n istio-system proxy-config route istio-ingressgateway-6489d9556d-sp8f2 -o json | yq -y '.' -
今回はGatewayにAttachしていないVirtualServiceを作成したので、特にルーティング設定に変化がないことが分かる。
ではアプリケーション側のPodに配置されたEnvoyの設定はどうだろうか?
❯❯❯ istioctl proxy-config route productpage-v1-7f44c4d57c-nfnjd -o json | yq -y '.' - - name: reviews.default.svc.cluster.local:80 domains: - reviews.default.svc.cluster.local - reviews.default.svc.cluster.local:80 routes: - match: prefix: / route: weightedClusters: clusters: - name: outbound|80|v2|reviews.default.svc.cluster.local weight: 50 - name: outbound|80|v3|reviews.default.svc.cluster.local weight: 50 timeout: 0s retryPolicy: retryOn: connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes numRetries: 2 retryHostPredicate: - name: envoy.retry_host_predicates.previous_hosts hostSelectionRetryMaxAttempts: '5' retriableStatusCodes: - 503 maxGrpcTimeout: 0s metadata: filterMetadata: istio: config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/reviews decorator: operation: reviews:80/*
長いので省略しているが、reviewsへのトラフィックのときに下記2つの向き先へ50%の割合でルーティングしているのが分かる。
endpointの設定を見てみると、それぞれv3のPodのIP、v2のPodのIPを指していることが読み取れる。
❯❯❯ istioctl proxy-config endpoint productpage-v1-7f44c4d57c-nfnjd -o json | yq -y '.' - | grep reviews -A 30 - name: outbound|9080|v3|reviews.default.svc.cluster.local addedViaApi: true hostStatuses: - address: socketAddress: address: 172.17.0.10 portValue: 9080 - name: outbound|9080|v2|reviews.default.svc.cluster.local addedViaApi: true hostStatuses: - address: socketAddress: address: 172.17.0.11 portValue: 9080
ちなみにこのルーティング設定は、Bookinfoアプリケーション内のそれぞれのサービスのサイドカーEnvoyの設定に組み込まれている。 (正確にはIstio Pilotの仕組みを使って設定が動的にInjectされている) つまり、リクエストを送る側、つまりクライアント側のプロキシ(Envoy)でロードバランシング、ルーティング制御を行い、Podにトラフィックを送っているのである。
DestinationRuleを削除してみる
最後に上記までの状態からDestinationRuleだけ削除してみる。
❯❯❯ kubectl apply -f samples/bookinfo/networking/destination-rule-reviews.yaml
ルーティングルールとしては変化していないように見える。
❯❯❯ istioctl proxy-config route productpage-v1-7f44c4d57c-nfnjd -o json | yq -y '.' - clusters: - name: outbound|80|v2|reviews.default.svc.cluster.local weight: 50 - name: outbound|80|v3|reviews.default.svc.cluster.local weight: 50
だが、エンドポイント定義としてはv2, v3が消えてしまっているのが分かる。
❯❯❯ istioctl proxy-config endpoint productpage-v1-7f44c4d57c-nfnjd -o json | yq -y '.' - | grep reviews - name: outbound|9080||reviews.default.svc.cluster.local