コンテンツにスキップ

コントロールプレーン@Istio

はじめに

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


01. コントロールプレーン (Istiodコントロールプレーン) とは

サイドカー型の場合

サイドカー型のIstiodコントロールプレーンは、istiod-serviceを介して、各種ポート番号でistio-proxyコンテナからのリモートプロシージャーコールを待ち受ける。

語尾の『d』は、デーモンの意味であるが、Istiodコントロールプレーンの実体は、Deploymentである。

istio_control-plane_ports


アンビエンドメッシュの場合

記入中...


01-02. マニフェスト

マニフェストの種類

Istiodコントロールプレーンは、Deployment、Service、MutatingWebhookConfigurationなどのマニフェストから構成される。


Deployment

▼ istiod

コントロールプレーンのPodの可用性を高めるため、これを冗長化する。

Deployment配下のPodは、Istiodコントロールプレーンの実体である。

Pod内ではdiscoveryコンテナが稼働している。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: istiod
    istio.io/rev: <リビジョン番号>
    release: istiod
  name: istiod-<リビジョン番号>
  namespace: istio-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: istiod
      istio.io/rev: <リビジョン番号>
  strategy:
    rollingUpdate:
      maxSurge: 100%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: istiod
        istio.io/rev: <リビジョン番号>
    spec:
      containers:
        - args:
            - discovery
            # 15014番ポートの開放
            - --monitoringAddr=:15014
            - --log_output_level=default:info
            - --domain
            - cluster.local
            - --keepaliveMaxServerConnectionAge
            - 30m
          # pilotイメージ
          image: docker.io/istio/pilot:<リビジョン番号>
          imagePullPolicy: IfNotPresent
          # discoveryコンテナ
          name: discovery
          # 待ち受けるポート番号の仕様
          ports:
            # 8080番ポートの開放
            - containerPort: 8080
              protocol: TCP
            # 15010番ポートの開放
            - containerPort: 15010
              protocol: TCP
            # 15017番ポートの開放
            - containerPort: 15017
              protocol: TCP
          env:
            # 15012番ポートの開放
            - name: ISTIOD_ADDR
              value: istiod-<リビジョン番号>.istio-system.svc:15012 # 15012番ポートの開放

          ...

# 重要なところ以外を省略しているので、全体像はその都度確認すること。

Dockerfileとしては、最後にpilot-discoveryプロセスを実行している。

ENTRYPOINT ["/usr/local/bin/pilot-discovery"]

そのため、pilot-discoveryプロセスの実体は、GitHubのpilot-discoveryディレクトリ配下のmain.goファイルで実行されるGoのバイナリファイルである。


HorizontalPodAutoscaler

Deployment配下のPodには、HorizontalPodAutoscalerが設定されている。

コントロールプレーンの可用性を高められる。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: istiod-<リビジョン番号>
  namespace: istio-system
  labels:
    app: istiod
    istio.io/rev: <リビジョン番号>
    release: istiod
spec:
  maxReplicas: 5
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: istiod-<リビジョン番号>
  targetCPUUtilizationPercentage: 80


Service

▼ istiod

istio-proxyコンテナからのリクエストを、Istiodコントロールプレーン (Deployment配下のPod) にポートフォワーディングする。

apiVersion: v1
kind: Service
metadata:
  name: istiod-<リビジョン番号>
  namespace: istio-system
  labels:
    app: istiod
    istio: pilot
    istio.io/rev: <リビジョン番号>
    release: istiod
spec:
  ports:
    # webhookサーバーに対するリクエストを待ち受ける。
    - name: https-webhook
      port: 443
      protocol: TCP
      targetPort: 15017
    # xDSサーバーに対するリクエストを待ち受ける。
    - name: grpc-xds
      port: 15010
      protocol: TCP
      targetPort: 15010
    # SSL証明書に関するリクエストを待ち受ける。
    - name: https-dns
      port: 15012
      protocol: TCP
      targetPort: 15012
    # メトリクス収集に関するリクエストを待ち受ける。
    - name: http-monitoring
      port: 15014
      protocol: TCP
      targetPort: 15014
  selector:
    # ルーティング先のistiodコントールプレーン (Deployment配下のPod)
    app: istiod
    istio.io/rev: <リビジョン番号>


MutatingWebhookConfiguration

▼ istio-revision-tag-default

Podの作成/更新時にwebhookサーバーにリクエストを送信できるように、MutatingWebhookConfigurationでMutatingAdmissionWebhookプラグインを設定する。

.webhooks.failurePolicyキーで設定している通り、webhookサーバーのコールに失敗した場合は、Podの作成のためのkube-apiserverのコール自体がエラーとなる。

そのため、Istioが起動に失敗し続けると、サイドカーコンテナのインジェクションを有効しているPodがいつまでも作成されないことになる。

apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: istio-revision-tag-default
  labels:
    app: sidecar-injector
    istio.io/rev: <リビジョン番号>
    istio.io/tag: <エイリアス>
webhooks:
  - name: rev.namespace.sidecar-injector.istio.io
    # mutating-admissionステップ発火条件を登録する。
    rules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
        scope: "*"
    # WebhookのダウンストリームにあるServiceの情報を登録する。
    clientConfig:
      service:
        name: istiod-<リビジョン番号>
        namespace: istio-system
        # エンドポイント
        path: "/inject"
        port: 443
      caBundle: Ci0tLS0tQk...
    # webhookサーバーのコールに失敗した場合の処理を設定する。
    failurePolicy: Fail
    matchPolicy: Equivalent
    # 適用するNamespaceを設定する。
    namespaceSelector:
      matchExpressions:
        - key: istio.io/rev
          operator: In
          values:
            - <エイリアス>


02. discoveryコンテナ

discoveryコンテナの仕組み

Istio (v1.1) のdiscoveryコンテナは、Config Ingestionレイヤー、Core Data Modelレイヤー、Proxy Servingレイヤー、インメモリストレージ、といった要素からなる。

istio_control-plane_architecture


Config Ingestionレイヤー

▼ Config Ingestionレイヤーとは

Clusterで作成されたIstioリソースの状態を取得する。


Config translationレイヤー

▼ Config translationレイヤーとは

取得したカスタムリソースの状態をEnvoyの設定値に変換する。

▼ リスナーの場合

IstioリソースをEnvoyのリスナーに変換する。

▼ ルートの場合

IstioリソースをEnvoyのルートに変換する。

▼ クラスターの場合

IstioリソースをEnvoyのクラスターに変換する。

▼ エンドポイントの場合

IstioリソースをEnvoyのエンドポイントに変換する。


Config servingレイヤー

▼ Config servingレイヤーとは

Envoyの設定値に基づいて、istio-proxyコンテナをPodに提供する。

▼ XDS-API

pilot-agentを介して、Envoyとの間で定期的にリモートプロシージャーコールを双方向で実行し、宛先情報を送信する。

▼ XDS-APIの実装

package xds

...

// ADS-APIからEnvoyに宛先情報をリモートプロシージャーコールする。
func (s *DiscoveryServer) StreamAggregatedResources(stream DiscoveryStream) error {
    return s.Stream(stream)
}

func (s *DiscoveryServer) Stream(stream DiscoveryStream) error {

    ...

    for {

        select {

        // 先に終了したcaseに条件分岐する
        case req, ok := <-con.reqChan:
            if ok {
                // pilot-agentからリクエストを受信する。
                // 受信内容に応じて、送信内容を作成する。
                if err := s.processRequest(req, con); err != nil {
                    return err
                }
            } else {
                return <-con.errorChan
            }

        case pushEv := <-con.pushChannel:

            // pilot-agentにリクエストを送信する。
            err := s.pushConnection(con, pushEv)
            pushEv.done()
            if err != nil {
                return err
            }
        case <-con.stop:
            return nil
        }
    }
}

実装が移行途中のため、xds-proxyにも、Envoyからのリモートプロシージャーコールを処理する同名のメソッドがある。


インメモリストレージ

記入中...


02-02. 待ち受けるポート番号

ポート番号の確認

$ kubectl exec foo-istiod -n istio-system -- netstat -tulpn

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:9876          0.0.0.0:*               LISTEN      1/pilot-discovery
tcp6       0      0 :::15017                :::*                    LISTEN      1/pilot-discovery
tcp6       0      0 :::8080                 :::*                    LISTEN      1/pilot-discovery
tcp6       0      0 :::15010                :::*                    LISTEN      1/pilot-discovery
tcp6       0      0 :::15012                :::*                    LISTEN      1/pilot-discovery
tcp6       0      0 :::15014                :::*                    LISTEN      1/pilot-discovery

8080

discoveryコンテナの8080番ポートでは、コントロールプレーンのデバッグエンドポイントに対するリクエストを待ち受ける。

discoveryコンテナの15014番ポートにポートフォワーディングしながら、別にgo tool pprofコマンドを実行することにより、Istioを実装するパッケージのリソース使用量を可視化できる。

# ポートフォワーディングを実行する。
$ kubectl port-forward svc/istiod-<リビジョン番号> 15014 -n istio-system

$ go tool pprof -http=:8080 127.0.0.1:15014/debug/pprof/heap

Fetching profile over HTTP from http://127.0.0.1:15014/debug/pprof/heap
Saved profile in /Users/hiroki-hasegawa/pprof/pprof.pilot-discovery.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz
Serving web UI on http://127.0.0.1:8080

# どのパッケージでどのくらいハードウェアリソースを消費しているか
$ curl http://127.0.0.1:8080/ui/flamegraph?si=alloc_objects


9876

discoveryコンテナの9876番ポートでは、ControlZダッシュボードに対するリクエストを待ち受ける。

ControlZダッシュボードでは、istiodコントロールプレーンの設定値を変更できる。


15010

istio_control-plane_service-discovery

discoveryコンテナの15010番ポートでは、istio-proxyコンテナからのxDSサーバーに対するリモートプロシージャーコールを待ち受け、discoveryコンテナ内のプロセスに渡す。

コールの内容に応じて、他のサービス (Pod、Node)の宛先情報を含むレスポンスを返信する。

istio-proxyコンテナはこれを受信し、pilot-agentがEnvoyの宛先情報設定を動的に変更する (サービスディスカバリー) 。

istio_service-registry

Istiodコントロールプレーンは、サービスレジストリ (例:etcd、ZooKeeper、consul catalog、nocos、cloud foundry) に登録された情報や、コンフィグストレージに永続化されたマニフェストの宣言 (ServiceEntry、WorkloadEntry) から、他のサービス (Pod、Node) の宛先情報を取得する。

discoveryコンテナは、取得した宛先情報を自身に保管する。


15012

discoveryコンテナの15012番ポートでは、アプリコンテナ間で相互TLSによるHTTPSプロトコルを使用する場合に、istio-proxyコンテナからのSSL証明書に関するリクエストを待ち受け、discoveryコンテナ内のプロセスに渡す。

リクエストの内容に応じて、SSL証明書と秘密鍵を含むレスポンスを返信する。

istio-proxyコンテナはこれを受信し、pilot-agentはEnvoyにこれらを紐付ける。

また、SSL証明書の期限が切れれば、istio-proxyコンテナからのリクエストに応じて、新しいSSL証明書と秘密鍵を作成する。

istio_control-plane_certificate


15014

コンテナの15014番ポートでは、Istiodコントロールプレーンのメトリクスを監視するツールからのリクエストを待ち受け、discoveryコンテナ内のプロセスに渡す。

リクエストの内容に応じて、データポイントを含むレスポンスを返信する。

# ポートフォワーディングを実行する。
$ kubectl port-forward svc/istiod-<リビジョン番号> 15014 -n istio-system

# デバッグダッシュボードにリクエストを送信する。
$ curl http://127.0.0.1:15014/debug


15017

discoveryコンテナの15017番ポートでは、Istioのistiod-<リビジョン番号>というServiceからのポートフォワーディングを待ち受け、discoveryコンテナ内のプロセスに渡す。AdmissionReviewを含むレスポンスを返信する。