← blog.yadon3141.com

Istioを体系的に理解する

Istioとは

マイクロサービス化が進むにつれてA/Bテストやカナリーリリースに20%のアクセスのだけ割り当てたいなどの要望により、サービスメッシュ2はより大きく、より複雑になりました。 Istioは、このような「複雑なサービスメッシュの管理が追いつかない」という課題を解決するため開発されています。

Architecture

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 ルーティングする。以下の場合だと/productpagelogoutなどにマッチングした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。 Istio Architecture

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が状態としてかなり違うので理解しがたい。