記録。

めも。

IstioのSidecarリソースについてのちょっとメモ。

なぜ書いているのか?

Istioをドキュメントを読んだり、動かしてみたりしていました。

しかし、タイトルに記載したSidecarリソースについてはいまいち理解できていませんでした。

個人的に抱えているこのモヤモヤをなくすためにIstioのSidecarリソースについて理解したことを書いていこうと思います。

IstioのSidecarリソースはいったい何者なのか

Istioの1.1にリリースされたカスタムリソースです。

ドキュメントには下記のように記載されています。

Sidecar describes the configuration of the sidecar proxy that mediates inbound and outbound communication to the workload instance it is attached to.

The Sidecar configuration provides a way to fine tune the set of ports, protocols that the proxy will accept when forwarding traffic to and from the workload. In addition, it is possible to restrict the set of services that the proxy can reach when forwarding outbound traffic from workload instances.

https://istio.io/latest/docs/reference/config/networking/sidecar/より抜粋

  • Fine-tune the set of ports and protocols that an Envoy proxy accepts.
  • Limit the set of services that the Envoy proxy can reach.

You can specify that you want a sidecar configuration to apply to all workloads in a particular namespace, or choose specific workloads using a workloadSelector.

https://istio.io/latest/docs/concepts/traffic-management/#sidecarsより抜粋

デフォルトの状態でアプリケーションをデプロイするとistio proxyはメッシュ内のすべてのワークロードと通信できるように設定を持ちます。

このSidecarリソースは、istio proxyが持つ通信の設定を調整したりすることができるリソースです。

例えば、ある特定のNamespaceにデプロイされているPodはそのNamespace内の他のPodとしか通信を行わないのに

デフォルトのままだと他のNamespaceと通信できるようにistio proxyに設定が注入されてしまいます。

セキュリティ面や後述するistio proxyのリソースの面でもSidecarリソースを使うことによって通信できる範囲を絞ることができます。

このあたりは実際にデプロイされているistio proxyの設定を確認したほうが見たほうがよさそうです。

しかし、設定をどう見るかについはEnvoyを少し知る必要があったので、少し整理してみたいと思います。

Envoyをざっくり理解する

ここではEnvoyが何者なのかは割愛しますが、istio proxyの設定を見るためにEnvoyで出てくる用語などについて書きます。

主な用語

downstream:Envoyに接続する。 Envoyをプロキシとしたサイドカーアプリケーションがデプロイされている場合

リクエストを送るクライアントがダウンストリームにあたる。 レスポンスを返すサイドカーアプリケーションがダウンストリームにあたる。

upstream : Envoyが受け取ったリクエストを転送する先を指す。 Envoyをプロキシとしたサイドカーアプリケーションがデプロイされている場合

Envoyがクラアントから受け取ったリクエストを転送する先であるサイドカーアプリケーションがアップストリームにあたる。 Envoyがサイドカーアプリケーションから受け取ったレスポンスを転送する先であるクライアントがアップストリームにあたる。

listener : Envoyがダウンストリームからの接続を受け入れるためのIPやポートなどの情報。

cluster : Envoyがアップストリームへ接続するため(転送するため)のエンドポイントのグループ

endpoint : クラスタが持つエンドポイントグループの1つ。クラスタとは親子関係になる。

router: Envoyが受け取ったリクエストをどのクラスタを選択してアップストリームに転送するかを決める経路情報。 HTTPヘッダで転送先を切り替えたり、リクエストのリトライの回数を制御したり、 パーセンテージをベースにしたトラフィックの分割、優先順位ベースのルーティング、、、などルートで設定できることは多岐にわたります。

Envoyがリクエストを受け取る情報はリスナーで管理され、そのリクエストを転送するための情報がクラスタで管理されている形になります。 routerによってリスナーとクラスタが紐づき、ダウンストリームからアップストリームへと転送できるようになります。

参考 :

https://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request

EnvoyのxDSについて

Envoyは静的にも、動的にも設定を適用することができますが、ここではIstioが採用している形式に合わせて動的な設定にフォーカスします。

Envoyはファイルシステムによる設定変更やREST APIや、gRPCなど様々な手段で設定を変更することができますが、

特にREST APIやgRPCによる設定変更では、Envoyが管理サーバーに問い合わせて動的に設定を受け取り更新します。

この設定を提供するサービスとAPIをまとめてxDSと呼ばれています。

xには用語で説明したclusterやlistenerがあてはまります。

LDS(Listener Discovery Service) : Listenerの設定を受け取るためのサービス

CDS(Cluster Discovery Servide) : Clusterの設定を受け取るためのサービス

EDS(Endpoint Discovery Service) : Endpointの設定を受け取るためのサービス

RDS(Route Discovery Service) : Routeの設定を受け取るためのサービス

(ADSやSDSは省略しています。)

設定を提供するAPIのサーバーをコントロールプレーンと呼び、問い合せるEnvoyプロキシをデータプレーンと呼びます。

Istioのコントロールプレーン内のPilotというコンポーネントがまさにIstio Proxyの設定を提供していますね。

Istio Proxyの設定を見てみる

Envoyについてざっくりと確認したため実際にIstio Proxyにはどんな設定されているか見てみたいと思います。

例として Namespace foobar にそれぞれdeployment(Pod1台)をapplyしてみます。

(それぞれのNamespaceにデプロイされたPod間での内部通信する要件はないとします。)

Namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: foo
  labels:
    istio-injection: enabled
---
apiVersion: v1
kind: Namespace
metadata:
  name: bar
  labels:
    istio-injection: enabled

Deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  namespace: foo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
        - name: test-app
          image: test-app
          ports:
            - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  namespace: bar
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
        - name: test-app
          image: test-app
          ports:
            - containerPort: 80

Service.yaml

apiVersion: v1
kind: Service
metadata:
  name: foo
  namespace: foo
spec:
  selector:
    app: test-app
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: bar
  namespace: bar
spec:
  selector:
    app: test-app
  ports:
    - port: 80
      targetPort: 80

これでIngressの設定次第などでは、Port 80で通信できるような構成になりました。

NamespaceとDeploymentをapplyしたので、istio-proxyの設定を確認してみたいと思います。

Istioにはデバッグ目的も含めproxyのステータスを確認するコマンドが用意されています。

% istioctl proxy-status

NAME                                                   CDS        LDS        EDS        RDS          ISTIOD                     VERSION
istio-ingressgateway-7576658c9b-gqrgh.istio-system     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-c85d85ddd-6nfmg     1.8.1
test-app-9dfd8f5-58t4d.foo                             SYNCED     SYNCED     SYNCED     SYNCED       istiod-c85d85ddd-6nfmg     1.8.1
test-app-9dfd8f5-x9m2q.bar                             SYNCED     SYNCED     SYNCED     SYNCED       istiod-c85d85ddd-6nfmg     1.8.1

前述のEnvoyのxDSに沿ってそれぞれのステータスが記載されています。

test-appのistio-proxyはどのサービスディスカバリでも SYNCED になっているためistio-proxyは正常にinjectされているようです。

ちなみにステータスの状態は3種類存在します。

SYNCED : Istiodが最後に送った情報をistio-proxyが受け取り、確認応答を返した状態

NOT SENT : 送信するべき設定がなく、Istiodが何も送ってないことを示す状態

STALE : Istiodが送った設定の確認応答が返ってきていない状態

istio-proxyが正常に動いていることを確認することができたため、各設定も確認していこうと思います。

Listener

 % istioctl proxy-config listener test-app-9dfd8f5-58t4d.foo
ADDRESS      PORT  MATCH                                                               DESTINATION
10.68.0.10   53    ALL                                                                 Cluster: outbound|53||kube-dns.kube-system.svc.cluster.local
0.0.0.0      80    Trans: raw_buffer; App: HTTP                                        Route: 80
0.0.0.0      80    ALL                                                                 PassthroughCluster
10.68.13.187 80    Trans: raw_buffer; App: HTTP                                        Route: bar.bar.svc.cluster.local:80
10.68.13.187 80    ALL                                                                 Cluster: outbound|80||bar.bar.svc.cluster.local
10.68.4.198  80    Trans: raw_buffer; App: HTTP                                        Route: foo.foo.svc.cluster.local:80
10.68.4.198  80    ALL                                                                 Cluster: outbound|80||foo.foo.svc.cluster.local
10.68.0.1    443   ALL                                                                 Cluster: outbound|443||kubernetes.default.svc.cluster.local
10.68.0.60   443   ALL                                                                 Cluster: outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.68.10.133 443   ALL                                                                 Cluster: outbound|443||istiod.istio-system.svc.cluster.local
10.68.11.98  443   Trans: raw_buffer; App: HTTP                                        Route: metrics-server.kube-system.svc.cluster.local:443
10.68.11.98  443   ALL                                                                 Cluster: outbound|443||metrics-server.kube-system.svc.cluster.local
0.0.0.0      15001 ALL                                                                 PassthroughCluster
0.0.0.0      15001 Addr: *:15001                                                       Non-HTTP/Non-TCP
0.0.0.0      15006 Addr: *:15006                                                       Non-HTTP/Non-TCP
0.0.0.0      15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0                           InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: raw_buffer; Addr: 0.0.0.0/0                                  InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: tls; App: HTTP TLS; Addr: 0.0.0.0/0                          InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: raw_buffer; App: HTTP; Addr: 0.0.0.0/0                       InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:80 Cluster: inbound|80||
0.0.0.0      15006 Trans: raw_buffer; App: HTTP; Addr: *:80                            Cluster: inbound|80||
0.0.0.0      15006 Trans: tls; App: TCP TLS; Addr: *:80                                Cluster: inbound|80||
0.0.0.0      15006 Trans: raw_buffer; Addr: *:80                                       Cluster: inbound|80||
0.0.0.0      15006 Trans: tls; Addr: *:80                                              Cluster: inbound|80||
0.0.0.0      15010 Trans: raw_buffer; App: HTTP                                        Route: 15010
0.0.0.0      15010 ALL                                                                 PassthroughCluster
10.68.0.60   15012 ALL                                                                 Cluster: outbound|15012||istio-ingressgateway.istio-system.svc.cluster.local
10.68.10.133 15012 ALL                                                                 Cluster: outbound|15012||istiod.istio-system.svc.cluster.local
0.0.0.0      15014 Trans: raw_buffer; App: HTTP                                        Route: 15014
0.0.0.0      15014 ALL                                                                 PassthroughCluster
0.0.0.0      15021 ALL                                                                 Inline Route: /healthz/ready*
10.68.0.60   15021 Trans: raw_buffer; App: HTTP                                        Route: istio-ingressgateway.istio-system.svc.cluster.local:15021
10.68.0.60   15021 ALL                                                                 Cluster: outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
0.0.0.0      15090 ALL                                                                 Inline Route: /stats/prometheus*
10.68.0.60   15443 ALL                                                                 Cluster: outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local

routes

% istioctl proxy-config routes test-app-9dfd8f5-58t4d.foo
NOTE: This output only contains routes loaded via RDS.
NAME                                                          DOMAINS                               MATCH                  VIRTUAL SERVICE
80                                                            bar.bar                               /*
80                                                            default-http-backend.kube-system      /*
80                                                            foo                                   /*
80                                                            istio-ingressgateway.istio-system     /*
15010                                                         istiod.istio-system                   /*
15014                                                         istiod.istio-system                   /*
istio-ingressgateway.istio-system.svc.cluster.local:15021     istio-ingressgateway.istio-system     /*
metrics-server.kube-system.svc.cluster.local:443              metrics-server.kube-system            /*
foo.foo.svc.cluster.local:80                                  foo                                   /*
bar.bar.svc.cluster.local:80                                  bar.bar                               /*
                                                              *                                     /stats/prometheus*
InboundPassthroughClusterIpv4                                 *                                     /*
InboundPassthroughClusterIpv4                                 *                                     /*
inbound|80||                                                  *                                     /*
                                                              *                                     /healthz/ready*
inbound|80||                                                  *                                     /*

cluster

% istioctl proxy-config cluster test-app-9dfd8f5-58t4d.foo
SERVICE FQDN                                            PORT      SUBSET     DIRECTION     TYPE             DESTINATION RULE
                                                        80        -          inbound       STATIC
BlackHoleCluster                                        -         -          -             STATIC
InboundPassthroughClusterIpv4                           -         -          -             ORIGINAL_DST
PassthroughCluster                                      -         -          -             ORIGINAL_DST
agent                                                   -         -          -             STATIC
bar.bar.svc.cluster.local                               80        -          outbound      EDS
default-http-backend.kube-system.svc.cluster.local      80        -          outbound      EDS
foo.foo.svc.cluster.local                               80        -          outbound      EDS
istio-ingressgateway.istio-system.svc.cluster.local     80        -          outbound      EDS
istio-ingressgateway.istio-system.svc.cluster.local     443       -          outbound      EDS
istio-ingressgateway.istio-system.svc.cluster.local     15012     -          outbound      EDS
istio-ingressgateway.istio-system.svc.cluster.local     15021     -          outbound      EDS
istio-ingressgateway.istio-system.svc.cluster.local     15443     -          outbound      EDS
istiod.istio-system.svc.cluster.local                   443       -          outbound      EDS
istiod.istio-system.svc.cluster.local                   15010     -          outbound      EDS
istiod.istio-system.svc.cluster.local                   15012     -          outbound      EDS
istiod.istio-system.svc.cluster.local                   15014     -          outbound      EDS
kube-dns.kube-system.svc.cluster.local                  53        -          outbound      EDS
kubernetes.default.svc.cluster.local                    443       -          outbound      EDS
metrics-server.kube-system.svc.cluster.local            443       -          outbound      EDS
prometheus_stats                                        -         -          -             STATIC
sds-grpc                                                -         -          -             STATIC
xds-grpc                                                -         -          -             STATIC
zipkin                                                  -         -          -             STRICT_DNS

endpoints

% istioctl proxy-config endpoints test-app-9dfd8f5-58t4d.foo
ENDPOINT                         STATUS      OUTLIER CHECK     CLUSTER
10.64.0.10:80                    HEALTHY     OK                outbound|80||foo.foo.svc.cluster.local
10.64.0.4:53                     HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.64.0.8:443                    HEALTHY     OK                outbound|443||metrics-server.kube-system.svc.cluster.local
10.64.0.9:8080                   HEALTHY     OK                outbound|80||default-http-backend.kube-system.svc.cluster.local
10.64.1.3:53                     HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.64.1.4:8080                   HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:8443                   HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15012                  HEALTHY     OK                outbound|15012||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15021                  HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15443                  HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.64.2.2:15010                  HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.64.2.2:15012                  HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.64.2.2:15014                  HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.64.2.2:15017                  HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.64.2.3:80                     HEALTHY     OK                outbound|80||bar.bar.svc.cluster.local
127.0.0.1:80                     HEALTHY     OK                inbound|80||
127.0.0.1:15000                  HEALTHY     OK                prometheus_stats
127.0.0.1:15020                  HEALTHY     OK                agent
35.189.153.189:443               HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/SDS     HEALTHY     OK                sds-grpc
unix://./etc/istio/proxy/XDS     HEALTHY     OK                xds-grpc

どの設定もたくさんあります。

ちょっと気になるのがfooのPodはbarのPodとは通信する予定はないのに、通信できるような設定があります。

冒頭に記載しているようにデフォルトではサービスメッシュ内のすべてのワークロードを通信できるような設定が適用されてしまいます。

本来であればbarのPodとは疎通できなくても問題ないため上記のbarに関する設定は消したいところです。

消すためにはSidecarリソースを使用します。

やっと本題であるSidecarリソースを使っていきたいと思います。

Sidecarリソースを使ってみる

ドキュメントを参考にSidecarリソースをapplyしてみようと思います。

今回はメッシュ内の、istio-systemと疎通できるような設定にしてみます。

Sidecar.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: foo
  namespace: foo
spec:
  workloadSelector:
    labels:
      app: test-app
  ingress:
  - port:
      number: 80
      protocol: HTTP
    defaultEndpoint: 127.0.0.1:80
  egress:
  - hosts:
    - "istio-system/*"
---
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: bar
  namespace: bar
spec:
  workloadSelector:
    labels:
      app: test-app
  ingress:
  - port:
      number: 80
      protocol: HTTP
    defaultEndpoint: 127.0.0.1:80
  egress:
  - hosts:
    - "istio-system/*"

適用後のendpointsの設定を確認してみます。

% istioctl proxy-config endpoints test-app-9dfd8f5-58t4d.foo
ENDPOINT                         STATUS      OUTLIER CHECK     CLUSTER
10.64.1.4:8080                   HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:8443                   HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15012                  HEALTHY     OK                outbound|15012||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15021                  HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.64.1.4:15443                  HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.64.2.2:15010                  HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.64.2.2:15012                  HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.64.2.2:15014                  HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.64.2.2:15017                  HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
127.0.0.1:80                     HEALTHY     OK                inbound|80||
127.0.0.1:15000                  HEALTHY     OK                prometheus_stats
127.0.0.1:15020                  HEALTHY     OK                agent
unix://./etc/istio/proxy/SDS     HEALTHY     OK                sds-grpc
unix://./etc/istio/proxy/XDS     HEALTHY     OK                xds-grpc

先ほどまで存在していたbarへの通信の設定がなくなりました。

Sidecarリソースを使うことで設定を必要以上に持たなくて済みました。

Sidecarリソースをもう少し理解したい

先ほどのSidecarリソースを使ってみた例ではいまいち恩恵を受けることができているのか、いないのかという印象は薄かったかもしれません。

しかし、istio-proxyのリソース使用量などパフォーマンスの側面で見るとSidecarリソースを使用することはとても大切になるようです。

大規模なシステムでistio-proxyが必要以上に設定を持ってしまうとCPUやメモリの消費も増えてしまいます。

issueなどにもVitualServiceリソースが増加するごとにメモリの使用量が増加した事例などがありました。

自身もどれくらいの負荷の増減が発生するのか検証してみました。

今回はVirtualServiceやリクエストによる負荷ではなく、単純にSidecarリソースを使用せずにNamespaceとPodを増やしてみてリソースの使用量に差異が見られるのか検証してみました。

Pod数を初回6台くらいから徐々に増やしていき最終的に120台まで増やしてみました。120台まで増やした後にSidecarリソースを適用してみました。

以下がその際のメモリの使用量の推移です。

f:id:jksdaba:20210329010836p:plain

全体として見ると増減は見られますが、5MB~10MBほどの増減でした。

時刻で見ると19:00頃から少しずつ使用量が増加しています。

19:00頃の Podの台数は50~60台ほどでした。そこから120台まで徐々に増加していったことが確認できました。

2:00前くらいから使用量が減少していますが、このときにSidecarリソースを適用しました。

一応メモリ使用量の変化を確認することができましたが、増減の値が小さかったり、、と少し検証内容は不十分だったのでは?と考えています。

絶えずリクエストを送ったり、VirtualServiceを使用して、サーキットブレーカーや割合によるリクエスト制御など様々な機能を利用した複雑なProxy設定を行った上でメトリクスを確認したら、より違った効果が見られたかもしれません。

Podを増やしていく上で、追加されたPodのistio-proxy、既に存在しているPodのistio-proxyどちらにも設定の更新が発生するためControl PlaneのIstiodにも負荷がかかってしまうということもわかりました。このあたりはPodの立ち上がりに影響が出てしまうかもしれないため別途検証してみたほうがよさそうです。

最後に

今回はなんとなく、Sidecarリソースを理解することができましたが、パフォーマンスに関する領域についてはもう少し確認したほうがいいことや、不十分なことが多く見られました。

また、時間を取って今回以上の検証ができればと思います。

もしくは、とても詳しい方がいたら教えてほしいですし、より詳しい記事があったら教えてほしいです。

参考

https://github.com/istio/istio/issues/19925

https://banzaicloud.com/blog/istio-sidecar/

Everything We Learned Running Istio In Production — Part 2 | by Craig Huber | HelloTech

envoyproxy - Reducing memory usage by ISTIO side car - Stack Overflow

Istio / Announcing Istio 1.1

https://linkerd.io/2020/12/03/why-linkerd-doesnt-use-envoy/

Potential memory leak on istio-proxy sidecar · Issue #29505 · istio/istio · GitHub

https://istio.io/latest/docs/concepts/traffic-management/#sidecars

High memory utilization of istio-proxy proportional to number of services · Issue #7912 · istio/istio · GitHub

https://tech.bigbasket.com/bigbaskets-experience-with-istio/

High memory usage with long HTTP/2 connections (GRPC) · Issue #9891 · envoyproxy/envoy · GitHub

Sidecar uses a large amount of memory · Issue #9367 · istio/istio · GitHub

https://github.com/istio/istio.io/issues/7700

https://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request