Istioを体系的に理解する
Istioとは
マイクロサービス化が進むにつれてA/Bテストやカナリーリリースに20%のアクセスのだけ割り当てたいなどの要望により、サービスメッシュ2はより大きく、より複雑になりました。 Istioは、このような「複雑なサービスメッシュの管理が追いつかない」という課題を解決するため開発されています。
Architecture
Istio のサービスメッシュは、データプレーン(data plane) と コントロールプレーン(control plane) の 2 つに論理的に分割される。
data plane: Envoy
control plane: Istiod
Envoy
Envoyは、Istioのデータプレーンを構成するプロキシ。podのサイドカーとして存在しており、サービス間の通信を仲介する。
Istiod
Pilot: Envoy に送る設定(Cluser、Listener、Route など)を生成。VirtualService / DestinationRule を解析し、Envoy 用の設定に変換 Citadel: サービス間の mTLS 通信に必要な証明書を発行・管理 Galley: Istio の設定を検証・配布 Sidecar Injector: pod 作成時に Envoy サイドカーを自動的に注入
気になり
IstioIngressGatewayの実態
Istio Ingress Gateway の実態は “Envoy プロキシそのもの”
なのでクラスタ外からクラスタ内に来るリクエストは実質Envoy Proxyを通してリクエストが到達する。Sidecar EnvoyはPod < - > Podで動作するがIstioIngressGatewayはクラスタ内外を疎通するだけの違い。
IstioIngressGatewayとVirtualService / Gateway / DestinationRule
Gateway
外部(インターネット)から Mesh に入る “入口” を定義する。TraefikでいうIngressのようなもの。
kind: Gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
protocol: HTTPS
hosts:
- "api.example.com"
tls:
credentialName: api-cert
これに基づいてIstioIngressGateway内のEnvoyが反応して443ポートをListenし、api.example.comの時のみ受け入れるようになる。
Gatewayリソースはトラフィックを受ける設定をするだけで、どこにどうやって流すかはVirtualServiceリソースが担当する。
stio IngressGatewayが受けたHTTPリクエストは、ホスト名, URL, User Agent, プロトコル, ポート番号, クエリパラメータ, その他HTTPヘッダーなどの情報を元に適切なIstio Gatewayに振り分けられます。
1つのIstio Gatewayには、リクエストの振り分けを行うための詳細な条件を1つ以上設定することができます。例えば、下の例のように example.com と foobar.com の2つのホスト名を設定すれば複数のホスト名に宛てたリクエストを1つのIstio Gatewayに集約することも可能です。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: sample-gateway
spec:
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- example.com
- foobar.com
おそらく
Istio Ingress Gateway -> Gateway(各hostごとに集約) -> VirtualService(具体的な向き先のServiceを決定)
VirtualService
先ほどistio-ingressgatewayで受けたトラフィックをどこにどうやって流すかのルールを設定するためのリソース。
Envoy が VirtualService のルールに基づいて L7 ルーティングする。以下の場合だと/productpageやlogoutなどにマッチングしたroutingがproductpageに流れる。
productpageはおそらくVirtualService。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
productpageではどのDestinationRuleでroutingを行うか決定する
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage
namespace: ""
resourceVersion: ""
spec:
hosts:
- productpage <= productpageへのリクエストを受けるVirtualService
http:
- route:
- destination:
host: productpage <= productpageに
subset: v1 <= subset: v1というDestinationRuleでルーティングを行う
なのでserviceに向けてるものも、実態はnginx-service.default.svc.cluster.localをEndpointSliceに問い合わせて、CluterIpを抜いてきてCluterIpに紐づいているPodIpを抜いてきて、それらをもとにPilotが設定を組み立ててEnvoyに流して、Envoyが向ける先のPodを決定して流しているのかなと
DestinationRule
例えば以下のようなパターンだとmy-appへ転送される際にHTTP/2 の同時リクエスト数の上限を 120 にする。
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app
namespace: default
spec:
host: my-app.default.svc.cluster.local
subsets:
- name: my-app-get-user
trafficPolicy:
connectionPool:
http:
http2MaxRequests: 120
- name: my-app-default
恐らく以下の画像の認識が正しくて、VirtualServiceはL7で後ろに存在するServiceをroutingする。その具体的な送信ルールを決定するのがDestinationRule。

DestinationRuleは以下のようなことができるので、L4, L7でのロードバランシングができる。できるとは言っても実態はEnvoyProxyだと思うが。 だから割とDestinationRuleもVirtualServiceもL7, L4の教会が曖昧で、それはEnvoyProxyがなんでもできちゃうから。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
namespace: default
spec:
host: reviews
subsets:
- labels:
version: v1
name: v1
- labels:
version: v2
name: v2
- labels:
version: v3
name: v3
こんなイメージなのかなと
App A
↓
Envoy (A sidecar)
↓
VirtualService(reviews の subset を選ぶ)
↓
DestinationRule(subset v2 = version=v2 の Pod 集合)
↓
Envoy が Pod IP に直接ロードバランス
↓
reviews Pod (v2)
Serviceはどうなったのか
一般的にServiceがkubeproxyによってpodのL4ロードバランシングを行っているイメージだったが、Istio配下においてその部分をVirtualService, DestinationRuleに則ってEnvoy Proxyが行ってしまう。
通信は全てProxyコンテナ間で行われ、各ProxyコンテナはPilotによって作成されたルーティングルールに沿って通信を行う。
では一体Serviceは何に使われるのかというと、Pilotがルーティングルールを作成する際に利用している。おそらくServiceが作成された際のEndpointを見てそのClusterIpを使用してPilotが名前解決している。
EndpointはServiceが作成されるとと作られるのでServiceが必要。
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.0.0.31 <none> 9080/TCP 6m
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 7d
productpage ClusterIP 10.0.0.120 <none> 9080/TCP 6m
ratings ClusterIP 10.0.0.15 <none> 9080/TCP 6m
reviews ClusterIP 10.0.0.170 <none> 9080/TCP 6m
感想
Istioの世界観(VirtualService / Gateway / DestinationRule)と、それを実際に構成しているEnvoy Proxyが状態としてかなり違うので理解しがたい。