コンテンツにスキップ

Istio@サービスメッシュ系ミドルウェア

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. Istioの仕組み

項目 サイドカーモード アンビエントモード
Nodeのハードウェアリソース消費量 × ⭕️
Nodeのストレージ使用量 ⭕️
Envoyの冗長性 ⭕️️
アプリごとのEnvoyの設定カスタマイズ ⭕️
単純性 × ⭕️
Istioのアップグレード インプレースアップグレード、カナリアアップグレード DaemonSetのローリングアップデート


01-02. 拡張性設計

コントロールプレーンのパフォーマンス設計

▼ CPU

  • デプロイ頻度
  • 設定変更頻度
  • istio-proxyコンテナ数
  • サービスメッシュのスコープ
  • コントロールプレーンの冗長化数


データプレーンのパフォーマンス設計

▼ CPUを消費する処理

メモリと同じように、以下の情報によって、データプレーンで必要なCPUが変わる。

  • istio-proxyコンテナ内のEnvoyプロセスのスレッド数。スレッドが多くなるほど、これに紐づくCPUが必要になる。
  • istio-proxyコンテナ内のEnvoyプロセスが作成するテレメトリー (ログ、メトリクス、分散トレース) のデータサイズ
  • リクエストやレスポンスのデータサイズ
  • 送信元の接続数
  • など...

▼ メモリを消費する処理

CPUと同じように、以下の情報によって、データプレーンで必要なメモリが変わる。

  • istio-proxyコンテナ内のEnvoyプロセスのスレッド数。スレッドが多くなるほど、これに紐づくCPUが必要になる。
  • istio-proxyコンテナ内のEnvoyプロセスが作成するテレメトリー (ログ、メトリクス、分散トレース) のデータサイズ
  • リクエストやレスポンスのデータサイズ
  • 送信元の接続数
  • など...

特に以下でメモリが必要になる。

  • istio-proxyコンテナ内のEnvoyプロセスが持つ宛先情報量

▼ サービスメッシュ有無による違い

サービスメッシュ有無によって、ハードウェアリソース消費量に違いがある。

Istioのドキュメントでは、以下のハードウェアリソースを消費することが記載されている。

1000 rps/sの場合である。

CPU メモリ
istio-proxyコンテナ 0.2 vCPU 60 Mi
waypoint-proxyのコンテナ 0.25 vCPU 60 Mi
ztunnelのコンテナ 0.06 vCPU 12 Mi

istio-proxyコンテナをインジェクションすることにより、 Podあたりで以下のハードウェアリソースが増える調査結果が出ている。

  • CPU:0.0002 vCPU 〜0.0003 vCPU
  • メモリ:40 Mi 〜 50 Mi
Pod CPU (導入前) CPU (導入後) メモリ (導入前) メモリ (導入後)
Nginx 0 vCPU 0.0003 vCPU 2 Mi 47 Mi
Database 0.0001 vCPU 0.0003 vCPU 29 Mi 76 Mi
サービスA 0.0001 vCPU 0.0004 vCPU 237 Mi 220 Mi
サービスB 0.0002 vCPU 0.0004 vCPU 219 Mi 288 Mi
サービスC 0.0002 vCPU 0.0004 vCPU 253 Mi 270 Mi
サービスD 0.0002 vCPU 0.0004 vCPU 28 Mi 73 Mi
サービスE 0.0004 vCPU 0.0007 vCPU 35 Mi 78 Mi
サービスF 0.0002 vCPU 0.0004 vCPU 230 Mi 270 Mi
サービスG 0.0003 vCPU 0.0006 vCPU 30 Mi 75 Mi
サービスH 0.0002 vCPU 0.0004 vCPU 393 Mi 311 Mi
サービスI 0.0001 vCPU 0.0004 vCPU 322 Mi 411 Mi
合計 0.0020 vCPU 0.0047 vCPU 1778 Mi 2119 Mi


レイテンシー (≒レスポンスタイム) の大きさ

▼ レイテンシーを大きくする処理

以下により、レイテンシーは大きくなる。

  • istio-proxyコンテナ、waypoint-proxyのコンテナ、ztunnelのコンテナの経由
  • AuthorizationPolicyによるアクセストークンの署名検証
  • PeerAuthenticationによる相互TLS

▼ サービスメッシュ有無による違い

p99、1000 rps/s、240 秒間の負荷の場合である。

条件 レイテンシー
both (送信元/宛先 istio-proxyコンテナの両方) 約 28 ms
serveronly (宛先 istio-proxyコンテナのみ) 約 13 ms
baseline (istio-proxyコンテナなし) 約 3 ms

istio_sidecar-mode_latency

▼ モードによる違い


02. サイドカーモード

Istioのサイドカーモードとは

サイドカーモードは、サイドカープロキシ型のサービスメッシュを実装したものである。

各PodにサイドカーとしてEnvoyを稼働させ、これが各マイクロサービスのインフラ領域の責務をに担う。


03. アンビエントモード (サイドカーレスパターン)

アンビエントモードとは

アンビエントモードは、サイドカーレスパターンのサービスメッシュを実装したものである。

各Node上にエージェントとしてEnvoyを稼働させ、これが各マイクロサービスのインフラ領域の責務をに担う。


03. トラフィック管理

パケット処理の仕組み

  1. istio-proxyコンテナにて、リスナーでリクエストを受信する。
  2. EnvoyFilterがあれば、これをリスナーフィルターとしてEnvoyに適用する。
  3. ルートでリクエストを受け取る。
  4. クラスターでリクエストを受け取る。
  5. クラスター配下のエンドポイントにリクエストをプロキシする。


サービスメッシュ内ではkube-proxyは不要

実は、サービスメッシュ内のPod間通信では、kube-proxyは使用しない。

istio-initコンテナは、istio-iptablesコマンドを実行し、iptablesのルールを書き換える。

これにより、送信元Podから宛先Podに直接通信できるようになる。


03-02. サービスメッシュ外へのリクエスト送信

安全な通信方式

▼ 任意の外部システムに送信できるようにする

サービスメッシュ内のマイクロサービスから、istio-proxyコンテナ (マイクロサービスのサイドカーとIstio EgressGatewayの両方) を経由して、任意の外部システムにリクエストを送信できるようにする。

外部システムは識別できない。

▼ 登録した外部システムに送信できるようにする

サービスメッシュ内のマイクロサービスから、istio-proxyコンテナ (マイクロサービスのサイドカーとIstio EgressGatewayの両方) を経由して、ServiceEntryで登録した外部システムにリクエストを送信できるようにする。

外部システムを識別できる。


安全ではない通信方式

▼ 登録した外部システムに送信できるようにする

サービスメッシュ内のマイクロサービスから、istio-proxyコンテナ (マイクロサービスのサイドカーのみ) を経由して、任意の外部システムにリクエストを送信できるようにする。

外部システムは識別できない。

istio-proxyコンテナを経由せずに送信できるようにする

サービスメッシュ内のマイクロサービスから、istio-proxyコンテナを経由せずに、外部システムにリクエストを送信できるようにする。


外部システムの種類

PassthroughCluster

IPアドレスを指定した送信できる宛先のこと。

Istio v1.3以降で、デフォルトで全てのサービスメッシュ外へのリクエストのポリシーが ALLOW_ANYとなり、PassthroughClusterとして扱うようになった。

サービスメッシュ外にDBを配置する場合、メッシュ内のアプリからDBへ通信ではDBのエンドポイントを指定することになる。

そのため、サービスメッシュ外へのリクエストは PassthroughClusterに属する。

注意点として、REGISTRY_ONLYモードを有効化すると、ServiceEntryで登録された宛先以外へのサービスメッシュ外への全通信が BlackHoleClusterになってしまう

BlackHoleCluster

IPアドレスを指定して送信できない宛先のこと。

基本的に、サービスメッシュ外へのリクエストは失敗し、502ステータスになる (502 Bad Gateway)。


04. 回復性の管理

フォールトインジェクション

▼ フォールトインジェクションとは

ランダムな障害を意図的にインジェクションし、サービスメッシュの動作を検証する。

▼ テストの種類

テスト名 内容
Delayインジェクション マイクロサービスに対するインバウンド通信にて、意図的に通信の遅延を発生させる。<br>https://istio.io/latest/docs/tasks/traffic-management/fault-injection/#injecting-an-http-delay-fault
Abortインジェクション マイクロサービスに対するインバウンド通信にて、意図的に通信の中止を発生させる。<br>https://istio.io/latest/docs/tasks/traffic-management/fault-injection/#injecting-an-http-abort-fault


ヘルスチェック

▼ アクティブヘルスチェック

istio-proxyコンテナは、マイクロサービスに対するkubeletのヘルスチェックを受信し、マイクロサービスに転送する。

▼ サーキットブレイカー

istio-proxyコンテナでサーキットブレイカーを実現する。

ただし、サーキットブレイカー後のフォールバック処理はistoi-proxyコンテナでは実装できず、アプリケーションで実装する必要がある。

istio-proxyコンテナは、送信可能な宛先がなくなると503ステータスを返信する。

Envoyは以下の機能を持っている。

  • Envoyでは、コネクションプールの上限を条件として、サーキットブレイカーを発動する
  • Envoyでは、ステータスコードの外れ値を条件として、ロードバランシングで異常なホストを排出する

Istioでは、外れ値の排出率を100%とすることで、ステータスコードもサーキットブレイカーの条件にできる。

  • Istioでは、コネクションプールの上限を条件として、サーキットブレイカーを発動する
  • Istioでは、ステータスコードの外れ値を条件を100%とすることにより、サーキットブレイカーを発動する


05. 通信の認証/認可

通信の認証

▼ 仕組み

Pod間通信時に、正しい送信元Envoyの通信であることを認証する。

▼ 相互TLS認証

相互TLS認証を実施し、送信元Podの通信を認証する。

▼ JWTによるBearer認証 (IDプロバイダーに認証フェーズを委譲)

JWTによるBearer認証を実施し、送信元Podの通信を認証する。

この場合、認証フェーズをIDプロバイダー (例:Auth0、AWS Cognito、GitHub、Google Cloud Auth、Keycloak、Zitadel) に委譲することになる。

JWT仕様トークンの取得方法として、例えば以下の方法がある。

  • 送信元PodがIDプロバイダーからJWTを直接取得する。
  • 送信元/宛先の間に認証プロキシ (例:OAuth2 Proxy、Dexなど) を配置し、認証プロキシでIDプロバイダーからJWTを取得する。

▼ アプリケーションの認証について

アプリ側の認証については、Istioの管理外である。


通信の認可

▼ 仕組み

Pod間通信時に、AuthorizationPolicyを使用して、スコープに含まれる認証済みEnvoyの通信のみを認可する。

istio_authorization-policy

▼ 通信の認可の委譲

AuthorizationPolicyで認可プロバイダー (例:Keycloak、OpenPolicy Agent) を指定し、認可フェーズを委譲できる。

▼ アプリケーションの認可について

アプリ側の認可については、Istioの管理外である。


06. アプリケーションデータの暗号化

相互TLS認証

▼ 相互TLS認証とは

相互TLS認証を実施し、L7のアプリケーションデータを暗号化/復号化する。

暗号スイート

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_AES_256_GCM_SHA384
  • TLS_AES_128_GCM_SHA256

▼ TLSタイムアウト

アウトバウンド時、istio-proxyコンテナは宛先にHTTPSリクエストを送信する。

この時、実際はタイムアウトであっても、TLS handshake timeoutというエラーなってしまう。


クライアント証明書/SSL証明書発行

▼ Istiodコントロールプレーン (discoveryコンテナ) をルート認証局として使用する場合

デフォルトでは、Istiodコントロールプレーンがルート認証局として働く。

クライアント証明書/SSL証明書を提供しつつ、これを定期的に自動更新する。

  1. Istiodコントロールプレーンは、istio-ca-secret (Secret) を自己署名する。
  2. Istiodコントロールプレーンは、istio-proxyコンテナから送信された秘密鍵と証明書署名要求で署名されたクライアント証明書/SSL証明書を作成する。特に設定しなければ、istio-proxyコンテナのpilot-agentプロセスが、秘密鍵と証明書署名要求を自動で作成してくれる。
  3. istio-proxyコンテナからのリクエストに応じて、IstiodのSDS-APIがクライアント証明書/SSL証明書を istio-proxyコンテナに配布する。
  4. Istiodコントロールプレーンは、CA証明書を持つ istio-ca-root-cert (ConfigMap) を自動的に作成する。これは、istio-proxyコンテナにマウントされ、証明書を検証するために使用する。
  5. istio-proxyコンテナ間で相互TLS認証できるようになる。
  6. 証明書が失効すると、istio-proxyコンテナの証明書が自動的に差し代わる。Podの再起動は不要である。

istio_istio-ca-root-cert

▼ 外部ツールをルート認証局として使用する場合

Istiodコントロールプレーン (discoveryコンテナ) を中間認証局として使用し、ルート認証局をIstio以外 (例:HashiCorp Vaultなど) に委譲できる。

外部のルート認証局は、istio-proxyコンテナから送信された秘密鍵と証明書署名要求で署名されたSSL証明書を作成する。


07. テレメトリーの作成

他のOSSとの連携

Istio上のEnvoyは、テレメトリーを作成する。

各監視ツールは、プル型 (ツールがIstiodから収集) やプッシュ型 (Istiodがツールに送信) でこのテレメトリーを収集する。


07-02. メトリクス

メトリクスの作成と送信

Istio上のEnvoyはメトリクスを作成し、Istiodコントロールプレーン (discoveryコンテナ) に送信する。

Prometheusは、discoveryコンテナの /stats/prometheusエンドポイント (15090番ポート) からメトリクスのデータポイントを収集する。

なお、istio-proxyコンテナにも /stats/prometheusエンドポイントはある。


セットアップ

▼ Prometheusの設定ファイル

Prometheusの設定ファイルとして定義することもできる。

scrape_configs:
  # Istiodの監視
  - job_name: istiod
    kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
            - istio-system
    relabel_configs:
      - source_labels:
          - __meta_kubernetes_service_name
          - __meta_kubernetes_endpoint_port_name
        action: keep
        regex: istiod;http-monitoring
  # istio-proxyの監視
  - job_name: istio-proxy
    metrics_path: /stats/prometheus
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels:
          - __meta_kubernetes_pod_container_port_name
        action: keep
        regex: .*-envoy-prom

▼ カスタムリソースの場合

Prometheusが discoveryコンテナからデータポイントを取得するためには、discoveryコンテナのPodを監視するためのServiceMonitorが必要である。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: istiod-service-monitor
  namespace: istio-system
spec:
  jobLabel: istio
  targetLabels:
    - app
  selector:
    matchExpressions:
      - key: istio
        operator: In
        values:
          - pilot
  namespaceSelector:
    matchNames:
      - istio-system
  endpoints:
    - port: http-monitoring
      interval: 15s

また、istio-proxyコンテナの監視には、PodMonitorが必要である。

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: istio-proxy-service-monitor
  namespace: istio-system
spec:
  selector:
    matchExpressions:
      - key: istio-prometheus-ignore
        operator: DoesNotExist
  namespaceSelector:
    # istio-proxyをインジェクションしているNamespaceを網羅できるようにする
    any: true
  jobLabel: envoy-stats
  podMetricsEndpoints:
    # istio-proxyコンテナが公開しているメトリクス収集用のエンドポイントを指定する
    - path: /stats/prometheus
      interval: 15s
      relabelings:
        - action: keep
          sourceLabels:
            - __meta_kubernetes_pod_container_name
          regex: "istio-proxy"
        - action: keep
          sourceLabels:
            - __meta_kubernetes_pod_annotationpresent_prometheus_io_scrape
        - action: replace
          regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})
          replacement: "[$2]:$1"
          sourceLabels:
            - __meta_kubernetes_pod_annotation_prometheus_io_port
            - __meta_kubernetes_pod_ip
          targetLabel: __address__
        - action: replace
          regex: (\d+);((([0-9]+?)(\.|$)){4})
          replacement: $2:$1
          sourceLabels:
            - __meta_kubernetes_pod_annotation_prometheus_io_port
            - __meta_kubernetes_pod_ip
          targetLabel: __address__
        - action: labeldrop
          regex: "__meta_kubernetes_pod_label_(.+)"
        - sourceLabels:
            - __meta_kubernetes_namespace
          action: replace
          targetLabel: namespace
        - sourceLabels:
            - __meta_kubernetes_pod_name
          action: replace
          targetLabel: pod_name


メトリクスの種類

▼ Istiod全体に関するメトリクス

メトリクス名 単位 説明
istio_build カウント Istioの各コンポーネントの情報を表す。istio_build{component="pilot"}とすることで、Istiodコントロールプレーンの情報を取得できる。

istio-proxyコンテナに関するメトリクス

Prometheus上でメトリクスをクエリすると、Istiodコントロールプレーン (discoveryコンテナ) から収集したデータポイントを取得できる。

メトリクス名 単位 説明
istio_requests_total カウント istio-proxyコンテナが受信した総リクエスト数を表す。メトリクスの名前空間に対してさまざまなディメンションを設定できる。<br>https://blog.christianposta.com/understanding-istio-telemetry-v2/
istio_request_duration_milliseconds カウント istio-proxyコンテナが受信したリクエストに関して、処理の所要時間を表す。
istio_request_messages_total カウント istio-proxyコンテナが受信したgRPCによる総HTTPリクエスト数を表す。
istio_request_messages_total カウント istio-proxyコンテナが受信したgRPCによる総HTTPリクエスト数を表す。

| istio_request_duration_milliseconds_sum | カウント | istio-proxyコンテナが起動以降の全てのリクエスト期間の合計 | | envoy_cluster_upstream_rq_retry | カウント | istio-proxyコンテナの他のPodへのリクエストに関するリトライ数を表す。 | | envoy_cluster_upstream_rq_retry_success | カウント | istio-proxyコンテナが他のPodへのリクエストに関するリトライ成功数を表す。 | | envoy_cluster_upstream_rq_retry_backoff_expotential | カウント | 記入中... | | envoy_cluster_upstream_rq_retry_limit_exceeded | カウント | 記入中... |

▼ メトリクスのラベル

メトリクスをフィルタリングできるように、Istioでは任意のメトリクスにデフォルトでラベルがついている。

送信元と宛先を表すメトリクスがあり、Kialiと組み合わせることにより、リクエストの送信元Podを特定できる。

Istioを使わないと送信元IPアドレスで特定する必要があるが、プロキシによって書き換えられてしまうため、実際はかなり無理がある。

ラベル 説明 注意点
connection_security_policy Pod値の通信方法を表す。 mutual_tls (相互TLS認証)
destination_app リクエストの宛先のコンテナ名を表す。 foo-container
destination_cluster リクエストの宛先のKubernetes Cluster名を表す。 Kubernetes
destination_service リクエストの宛先のService名を表す。 foo-service
destination_workload リクエストの宛先のDeployment名を表す。 `foo-deployment
destination_workload_namespace 送信元のNamespace名を表す。
reporter メトリクスの収集者を表す。istio-proxyコンテナかIngressGatewayのいずれかである。 destination (宛先の istio-proxyコンテナ)<br>source (送信元のIngressGatewayまたは istio-proxyコンテナ)
response_flags Envoyの %RESPONSE_FLAGS%変数を表す。 - (値なし)
response_code istio-proxyコンテナが返信したレスポンスコードの値を表す。 2004040 reporter="source"の場合、送信元istio-proxyコンテナに対して、宛先 istio-proxyコンテナがアプリから受信したステータスコードを集計する。reporter="destination"の場合、送信元istio-proxyコンテナに対して、宛先 istio-proxyコンテナがアプリから受信したステータスコードを集計する。
source_app 送信元のコンテナ名を表す。 foo-container
source_cluster 送信元のKubernetes Cluster名を表す。 Kubernetes
source_workload 送信元のDeployment名を表す。 foo-deployment


07-03. ログ (アクセスログのみ)

ログの監視

▼ ログの出力

Istio上のEnvoyは、マイクロサービスへのアクセスログ (インバウンド通信とアウトバウンド通信の両方) を作成し、標準出力に出力する。

アクセスログにデフォルトで役立つ値が出力される。

ログ収集ツール (例:FluentBit、Fluentdなど) をDaemonSetパターンやサイドカーモードで配置し、NodeやPod内コンテナの標準出力に出力されたログを監視バックエンドに送信できるようにする必要がある。

# istio-proxyコンテナのアクセスログ
{
  # 相互TLSの場合の宛先コンテナ名
  "authority": "foo-downstream:<ポート番号>",
  "bytes_received": 158,
  "bytes_sent": 224,
  "connection_termination_details": null,
  # istio-proxyコンテナにとっての送信元
  "downstream_local_address": "*.*.*.*:50010",
  "downstream_remote_address": "*.*.*.*:50011",
  # 送信元から宛先へリクエストをプロキシし、レスポンスを処理し終えるまでにかかった時間
  # 送信元で設定したタイムアウトになった場合は、Envoyはその時間の直前にプロキシをやめるため、Durationはタイムアウトとおおよそ同じになる
  "duration": 12,
  "method": null,
  "path": null,
  "protocol": null,
  "request_id": null,
  "requested_server_name": null,
  # 宛先からのレスポンスのステータスコード
  "response_code": 200,
  "response_code_details": null,
  # ステータスコードの補足情報
  "response_flags": "-",
  "route_name": null,
  "start_time": "2023-04-12T06:11:46.996Z",
  # istio-proxyコンテナにとっての宛先
  "upstream_cluster": "outbound|50000||foo-pod.foo-namespace.svc.cluster.local",
  "upstream_host": "*.*.*.*:50000",
  "upstream_local_address": "*.*.*.*:50001",
  "upstream_service_time": null,
  "upstream_transport_failure_reason": null,
  "user_agent": null,
  "x_forwarded_for": null,
}

▼ ログの送信

Istio上のEnvoyは、アクセスログをログ収集ツール (例:OpenTelemetry Collector) に送信する。


07-04. 分散トレース

分散トレースの監視

▼ スパンの作成

istio_distributed_tracing

Istio上のEnvoyは、スパンを作成する。

スパンの作成場所としては、いくつか種類がある。

istio-proxy アプリ

スパンの作成場所が多いほど、各コンテナの処理時間が細分化された分散トレースを収集できる。

マイクロサービス間でスパンが持つコンテキストを伝播しないため、コンテキストを伝播させる実装が必要になる。

▼ スパンの送信

Istio上のEnvoyは、スパンを分散トレース収集ツール (例:Jaeger Collector、OpenTelemetry Collectorなど) に送信する。

Envoyでは宛先としてサポートしていても、Istio上のEnvoyでは使用できない場合がある。(例:X-Rayデーモン)