コンテンツにスキップ

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

はじめに

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


01. Istioの仕組み

01-02 サイドカーモード

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

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

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


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

アンビエントモードとは

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

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


01-04. パターンの比較

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


02. トラフィック管理

パケット処理の仕組み

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


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

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

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

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


02-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)。


03. 復旧性の管理

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

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

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

▼ テストの種類

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


サーキットブレイカー

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

なお、アプリケーションで同様の実装をしても良い。


04. 通信の認証/認可

通信の認証

▼ 仕組み

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

▼ 相互TLS認証

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

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

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

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

JWTの取得方法として、例えば以下の方法がある。

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

▼ アプリの認証について

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


通信の認可

▼ 仕組み

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

istio_authorization-policy

▼ 通信の認可の委譲

AuthorizationPolicyでIDプロバイダー (例:Auth0、GitHub、Keycloak、Zitadel、AWS Cognito、Google Cloud Auth) を指定し、認可フェーズを委譲できる。

▼ アプリの認可について

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


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

相互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証明書を作成する。


06. テレメトリーの作成

他のOSSとの連携

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

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


06-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コンテナが受信した総リクエスト数を表す。メトリクスの名前空間に対してさまざまなディメンションを設定できる。
https://blog.christianposta.com/understanding-istio-telemetry-v2/
istio_request_duration_milliseconds カウント istio-proxyコンテナが受信したリクエストに関して、処理の所要時間を表す。
istio_request_messages_total カウント istio-proxyコンテナが受信したgRPCによる総HTTPリクエスト数を表す。
istio_response_messages_total カウント istio-proxyコンテナが受信した総gRPCレスポンス数を表す。
envoy_cluster_upstream_rq_retry カウント istio-proxyコンテナの他のPodへのリクエストに関する再試行数を表す。
envoy_cluster_upstream_rq_retry_success カウント istio-proxyコンテナが他のPodへのリクエストに関する再試行成功数を表す。

▼ メトリクスのラベル

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

ラベル 説明
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コンテナ)
source (IngressGateway)
response_flags Envoyの%RESPONSE_FLAGS%変数を表す。 - (値なし)
response_code istio-proxyコンテナが返信したレスポンスコードの値を表す。 2004040 (クライアントが切断した場合)
source_app 送信元のコンテナ名を表す。 foo-container
source_cluster 送信元のKubernetes Cluster名を表す。 Kubernetes
source_workload 送信元のDeployment名を表す。 foo-deployment


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

ログの監視

▼ ログの出力

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

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

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

# istio-proxyコンテナのアクセスログ
{
  # 相互TLSの場合の宛先コンテナ名
  "authority": "foo-downstream:<ポート番号>",
  "bytes_received": 158,
  "bytes_sent": 224,
  "connection_termination_details": null,
  "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",
  "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) に送信する。


06-04. 分散トレース

分散トレースの監視

▼ スパンの作成

istio_distributed_tracing

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

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

istio-proxy アプリ

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

アプリコンテナ間でスパンが持つコンテキストを伝播しないため、コンテキストを伝播させる実装が必要になる。

▼ スパンの送信

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

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