コンテンツにスキップ

Istio@CNCF

はじめに

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


01. Istioの仕組み

サイドカープロキシメッシュ

▼ Istioのサイドカープロキシメッシュとは

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

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

▼ サイドカープロキシメッシュの仕組み

istio_sidecar-mesh_architecture

サイドカープロキシメッシュは、データプレーン、Isiodコントロールプレーン、といったコンポーネントから構成される。

サイドカープロキシを使用して、サービスメッシュを実装する。

サイドカーは、L4 (トランスポート層) のプロトコル (例:TCP、UDP、など) とL7 (アプリケーション層) のプロトコル (例:HTTP、HTTPS、など) を処理できる。


アンビエントメッシュ (Nodeエージェント型のサービスメッシュ)

▼ アンビエントメッシュとは

アンビエントメッシュは、Nodeエージェント型のサービスメッシュを実装したものである。

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

▼ アンビエントメッシュの仕組み

istio_ambient-mesh_architecture

アンビエントメッシュは、データプレーン、コントロールプレーンNode、といったコンポーネントから構成される。Node内の単一プロキシを使用して、サービスメッシュを実装する。

マイクロサービスアーキテクチャ固有のインフラ領域の問題 (例:サービスディスカバリーの必要性、マイクロサービス間通信の暗号化、テレメトリー作成、など) を解決する責務を持つ。

Node外からのインバウンド通信、またNode外へのアウトバウンド通信は、ztunnelのPodを経由して、一度waypoint-proxyのPodにリダイレクトされる。

サイドカープロキシメッシュを将来的に廃止するということはなく、好きな方を選べるようにするらしい。

ztunnelのPodを経由した段階でHTTPSプロトコルになる。

ハードウェアリソースの消費量の少ないL4プロトコルと、消費量の多いL7プロトコルのプロコトルの処理の責務が分離されているため、サイドカープロキシメッシュと比較して、L4プロトコルのみを処理する場合に、Nodeのハードウェアリソース消費量を節約できる。

サービスメッシュ内へのリクエストの経路は以下の通りである。

パブリックネットワーク
⬇⬆︎︎
リダイレクト
⬇⬆︎︎
# L4ロードバランサー
ztunnelのPod (L4) # DaemonSet配下のPodなので、Nodeごとにいる
⬇⬆︎︎
⬇⬆︎︎ # HBONE
⬇⬆︎︎
# L7ロードバランサー
waypoint-proxyのPod (L7) # Deployment配下のPodなので、任意のNodeにいる
⬇⬆︎︎
アプリコンテナのPod

サービスメッシュ内のリクエストの経路は以下の通りである。

アプリコンテナのPod # クライアント側
⬇⬆︎︎
# L4ロードバランサー
ztunnelのPod (L4) # DaemonSet配下のPodなので、Nodeごとにいる
⬇⬆︎︎
⬇⬆︎︎ # HBONE
⬇⬆︎︎
# L7ロードバランサー
waypoint-proxyのPod (L7) # Deployment配下のPodなので、任意のNodeにいる
⬇⬆︎︎
⬇⬆︎︎ # HBONE
⬇⬆︎︎
# L4ロードバランサー
ztunnelのPod (L4) # DaemonSet配下のPodなので、Nodeごとにいる
⬇⬆︎︎
アプリコンテナのPod # サーバー側

サービスメッシュ外へのリクエストの経路は以下の通りである。

パブリックネットワーク
⬆︎⬇
# L7ロードバランサー
waypoint-proxyのPod (L7) # Deployment配下なので、任意のNodeにいる
⬆︎⬇
⬆︎⬇ # HBONE
⬆︎⬇
# L4ロードバランサー
ztunnelのPod (L4) # DaemonSet配下なので、Nodeごとにいる
⬆︎⬇
リダイレクト
⬆︎⬇
アプリコンテナのPod

▼ ztunnel

ztunnelがL4 (トランスポート層) のプロトコル (例:TCP、UDP、など) を処理できる。

実体はDaemonSet配下のPodであり、Nodeごとにスケジューリングされている。

▼ waypoint-proxy

waypoint-proxyがL7 (アプリケーション層) のプロトコル (例:HTTP、HTTPS、SMTP、DNS、POP3、など) を処理できる。

実体は、Gateway-APIで作成されたenvoyコンテナを含むPodであり、任意のNodeにスケジューリングされている。

$ istioctl experimental waypoint generate
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: namespace
spec:
  gatewayClassName: istio-waypoint
  listeners:
    - name: mesh
      port: 15008
      protocol: HBONE

▼ Envoy

(たぶん) Envoyの設定値は以下のように機能している。

送信元ztunnelのEnvoyのL4処理で

  1. 前半のListenerとCluster:宛先マイクロサービスを決める
  2. 後半のListenerとCluster:宛先waypoint-proxyを決める

waypoint-proxyのEnvoyのL7処理で

  1. inbound_CONNECT_terminate Listener:HBORNを介したリクエストを受信する
  2. Internal Inbound VIP Cluster:Inbound VIP Listenerにルーティングする
  3. Inbound VIP Listener:VirtualServiceのルーティングポリシーを適用する
  4. Inbound VIP Cluster:Inbound Pod Listenerにロードバランシングする
  5. Inbound Pod Listener:HBORNのメタデータをセットアップする
  6. Inbound Pod Cluster
  7. inbound_CONNECT_originate Listener
  8. inbound_CONNECT_originate Cluster:宛先ztunnelを決める

宛先ztunnelのEnvoyのL4処理で

  1. ListenerとCluster:宛先マイクロサービスを決める


展開パターンの比較

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


01-02. Istioを採用するのか否か

設定方法の対応関係

KubernetesとIstioには重複する能力がいくつか (例:サービスディスカバリー) ある。全てのPodのistio-proxyコンテナをインジェクションする場合、kube-proxyとServiceによるサービスメッシュは不要になる。

ただし、実際の運用場面ではこれを実行することはなく、アプリコンテナの稼働するPodのみでこれを行えばよい。

そのため、istio-proxyコンテナをインジェクションしないPodでは、Istioではなく、従来のkube-proxyとServiceによるサービスディスカバリーを使用することになる。

能力 Istio + Kubernetes + Envoy Kubernetes + Envoy Kubernetesのみ
サービスメッシュコントロールプレーン Istiodコントロールプレーン (discoveryコンテナ) go-control-plane なし
サービスディスカバリーでのルーティング先設定 DestinationRule routeキー kube-proxy + Service
サービスディスカバリーでのリスナー EnvoyFilter + EndpointSlice listenerキー kube-proxy + Service
サービスディスカバリーでの追加サービス設定 ServiceEntry + EndpointSlice clusterキー EndpointSlice
Cluster外Nodeに対するサービスディスカバリー WorkloadEntry endpointキー Egress
サービスレジストリ etcd etcd etcd
Node外からのインバウンド通信のルーティング ・VirtualService + Gateway (内部的には、NodePort ServiceまたはLoadBalancer Serviceが作成され、これらはNode外からのインバウンド通信を待ち受けられるため、Ingressは不要である)
・Ingress + Istio Ingressコントローラー + ClusterIP Service
routeキー + listenerキー Ingress + Ingressコントローラー + ClusterIP Service


Istioのメリット/デメリット

▼ メリット

▼ デメリット

項目 説明
Nodeのハードウェアリソースの消費量増加 IstioのPod間通信では、Kubernetesと比べて、通信に必要なコンポーネント (例:Istiodコントロールプレーン、istio-proxyコンテナ) が増える。そのため、Nodeのハードウェアリソースの消費量が増え、また宛先Podからのレスポンス速度が低くなる。
学習コストの増加 Istioが多機能であり、学習コストが増加する。


02. トラフィック管理

パケット処理の仕組み

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


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


サーキットブレイカー

記入中...


04. 通信の認証/認可

通信の認証

▼ 仕組み

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

▼ 相互TLS認証

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

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

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

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

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

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

▼ アプリの認証について

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


通信の認可

▼ 仕組み

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

istio_authorization-policy

▼ 通信の認可の委譲

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

▼ アプリの認可について

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


05. パケットペイロードの暗号化

相互TLS認証

▼ 相互TLS認証とは

相互TLS認証を実施し、L7のパケットペイロードを暗号化/復号化する。

▼ TLSタイムアウト

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

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


SSL証明書の自動更新

▼ Istiodコントロールプレーン (discoveryコンテナ) による中間認証局を使用する場合

デフォルトでは、discoveryコンテナが中間認証局として働く。

ルート認証局を使用して、discoveryコンテナの中間CA証明書を署名しておく必要がある。

discoveryコンテナは、秘密鍵と証明書署名要求に基づいてSSL証明書を作成する。

KubernetesリソースにSSL証明書を提供しつつ、これを定期的に自動更新する。

▼ 外部の中間認証局を使用する場合

Istiodコントロールプレーン (discoveryコンテナ) を使用する代わりに、外部の中間認証局 (例:cert-manager、自前のcustom-controller) を使用する

ルート認証局を使用して、外部中間認証局の中間CA証明書を署名しておく必要がある。

外部中間認証局は、秘密鍵と証明書署名要求に基づいてSSL証明書を作成する。

KubernetesリソースにSSL証明書を提供しつつ、これを定期的に自動更新する。


06. テレメトリーの作成

他のOSSとの連携

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

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


06-02. メトリクス

メトリクスの監視

▼ メトリクスの作成

Istio上のEnvoyは、メトリクスを作成する。

▼ メトリクスの送信

Istio上のEnvoyは、Istiodコントロールプレーン (discoveryコンテナ) に送信する。

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

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

▼ セットアップ

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

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  namespace: istio-system
  name: istiod-monitor
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:
  namespace: istio-system
  name: istio-proxy-monitor
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 リクエストの宛先の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 リクエストの送信元の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上のEnvoyは、スパンを作成する。

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

istio-proxy アプリ

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

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

▼ スパンの送信

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

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


07. マルチClusterメッシュ

マルチClusterメッシュとは

複数のClusterのネットワークを横断的に管理するサービスメッシュ。

Istiodコントロールプレーンを持つプライマリCluster、サービスメッシュに参加するClusterのリモートCluster、からなる。


異なるCluster内コンテナのデータプレーン内管理

▼ 同じプライベートネットワーク内の場合

異なるClusterが同じプライベートネットワーク内に所属している場合に、ClusterのコントロールプレーンNode間でデータプレーンを管理し合う。

これにより、この時、IngressGatewayを使用せずに、異なるClusterのコンテナが直接的に通信できる。

istio_multi-service-mesh_cluster_same-network

▼ 異なるプライベートネットワーク内の場合

異なるClusterが異なるプライベートネットワーク内に所属している場合に、ClusterのコントロールプレーンNode間でデータプレーンを管理し合う。

これにより、この時、IngressGatewayを経由して、異なるClusterのコンテナが間接的に通信できる。

istio_multi-service-mesh_cluster_difficult-network


仮想サーバー内コンテナのデータプレーン内管理

▼ 同じプライベートネットワーク内の場合

仮想サーバーがコントロールプレーンNodeと同じプライベートネットワーク内に所属している場合に、この仮想サーバーにistio-proxyコンテナをインジェクションする。

これにより、データプレーン内で仮想サーバーを管理できるようになる。

この時、IngressGatewayを使用せずに、Kubernetes上のコンテナと仮想サーバー上のコンテナが直接的に通信できる。

istio_multi-service-mesh_vm_same-network

▼ 異なるプライベートネットワーク内の場合

仮想サーバーがコントロールプレーンNodeと異なるプライベートネットワーク内に所属している場合に、この仮想サーバーにistio-proxyコンテナをインジェクションする。

これにより、データプレーン内で管理できるようになる。

この時、IngressGatewayを経由して、Kubernetes上のコンテナと仮想サーバー上のコンテナが間接的に通信できる。

istio_multi-service-mesh_vm_difficult-network