Istioのサイドカーインジェクションモデルを解き明かす

Istioが既存のデプロイメントにデータプレーンコンポーネントをどのようにプラグインしているかを解き明かします。

2019年1月31日 | 著者:Manish Chugtu

Istioサービスメッシュアーキテクチャの簡単な概要は、常にコントロールプレーンとデータプレーンの説明から始まります。

Istioのドキュメントより:

The overall architecture of an Istio-based application.
Istioアーキテクチャ

アプリケーションポッドへのサイドカーインジェクションは、手動インジェクションも可能ですが、自動的に行われることを理解することが重要です。トラフィックは、開発者がそれについて心配する必要なく、アプリケーションサービスとこれらのサイドカーとの間でやり取りされます。アプリケーションがIstioサービスメッシュに接続されると、開発者はサービスメッシュが提供するすべてのメリットを利用し始めることができます。ただし、データプレーンの配管はどのように行われるのか、また、それをシームレスに動作させるために実際に何が必要なのでしょうか。この記事では、サイドカーインジェクションの仕組みを明確に理解するために、サイドカーインジェクションモデルの詳細を深く掘り下げていきます。

サイドカーインジェクション

簡単に言えば、サイドカーインジェクションとは、ポッドテンプレートに追加のコンテナの構成を追加することです。Istioサービスメッシュに必要な追加のコンテナは次のとおりです。

istio-init このinitコンテナは、インバウンド/アウトバウンドトラフィックがサイドカープロキシを通過するようにiptablesルールを設定するために使用されます。initコンテナは、次の点でアプリコンテナとは異なります。

したがって、このタイプのコンテナが、実際のアプリケーションコンテナの一部である必要のないセットアップまたは初期化ジョブに最適であることがわかります。この場合、istio-initはまさにそれを行い、iptablesルールを設定します。

istio-proxy これは、実際のサイドカープロキシ(Envoyベース)です。

手動インジェクション

手動インジェクション方式では、istioctlを使用してポッドテンプレートを変更し、前述の2つのコンテナの構成を追加できます。手動インジェクションと自動インジェクションの両方で、Istioはistio-sidecar-injector構成マップ(configmap)とメッシュのistio configmapから構成を取得します。

istio-sidecar-injector configmapの構成を見て、実際に何が起こっているのかを把握しましょう。

$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}'
SNIPPET from the output:

policy: enabled
template: |-
  initContainers:
  - name: istio-init
    image: docker.io/istio/proxy_init:1.0.2
    args:
    - "-p"
    - [[ .MeshConfig.ProxyListenPort ]]
    - "-u"
    - 1337
    .....
    imagePullPolicy: IfNotPresent
    securityContext:
      capabilities:
        add:
        - NET_ADMIN
    restartPolicy: Always

  containers:
  - name: istio-proxy
    image: [[ if (isset .ObjectMeta.Annotations "sidecar.istio.io/proxyImage") -]]
    "[[ index .ObjectMeta.Annotations "sidecar.istio.io/proxyImage" ]]"
    [[ else -]]
    docker.io/istio/proxyv2:1.0.2
    [[ end -]]
    args:
    - proxy
    - sidecar
    .....
    env:
    .....
    - name: ISTIO_META_INTERCEPTION_MODE
      value: [[ or (index .ObjectMeta.Annotations "sidecar.istio.io/interceptionMode") .ProxyConfig.InterceptionMode.String ]]
    imagePullPolicy: IfNotPresent
    securityContext:
      readOnlyRootFilesystem: true
      [[ if eq (or (index .ObjectMeta.Annotations "sidecar.istio.io/interceptionMode") .ProxyConfig.InterceptionMode.String) "TPROXY" -]]
      capabilities:
        add:
        - NET_ADMIN
    restartPolicy: Always
    .....

ご覧のとおり、configmapにはistio-init initコンテナとistio-proxyプロキシコンテナの両方の構成が含まれています。構成には、コンテナイメージの名前と、インターセプトモード、機能などの引数が含まれます。

セキュリティの観点から、istio-initがポッドの名前空間内でiptablesを変更するためにNET_ADMIN機能が必要であり、istio-proxyTPROXYモードで構成されている場合は同様であることに注意することが重要です。これはポッドの名前空間に制限されているため、問題はないはずです。ただし、最近のOpenShiftバージョンでは問題が発生する可能性があり、回避策が必要になることに気付きました。その1つのオプションは、この投稿の最後に記載されています。

サイドカーインジェクションのために現在のポッドテンプレートを変更するには、次のようにすることができます。

$ istioctl kube-inject -f demo-red.yaml | kubectl apply -f -

または

変更されたconfigmapまたはローカルconfigmapを使用するには

上記のように、sidecar-injectorとメッシュ構成を使用して新しいテンプレートを作成し、次にkubectlを使用してその新しいテンプレートを適用します。インジェクションされたYAMLファイルを見ると、上で説明したように、Istio固有のコンテナの構成が含まれています。インジェクションされたYAMLファイルを適用すると、2つのコンテナが実行されていることがわかります。1つは実際のアプリケーションコンテナであり、もう1つはistio-proxyサイドカーです。

$ kubectl get pods | grep demo-red
demo-red-pod-8b5df99cc-pgnl7   2/2       Running   0          3d

カウントが3ではないのは、istio-initコンテナが、ポッド内でiptableルールを設定するという本来の役割を果たした後に終了するinitタイプのコンテナであるためです。initコンテナの終了を確認するために、kubectl describeの出力を確認しましょう。

$ kubectl describe pod demo-red-pod-8b5df99cc-pgnl7
SNIPPET from the output:

Name:               demo-red-pod-8b5df99cc-pgnl7
Namespace:          default
.....
Labels:             app=demo-red
                    pod-template-hash=8b5df99cc
                    version=version-red
Annotations:        sidecar.istio.io/status={"version":"3c0b8d11844e85232bc77ad85365487638ee3134c91edda28def191c086dc23e","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs...
Status:             Running
IP:                 10.32.0.6
Controlled By:      ReplicaSet/demo-red-pod-8b5df99cc
Init Containers:
  istio-init:
    Container ID:  docker://bef731eae1eb3b6c9d926cacb497bb39a7d9796db49cd14a63014fc1a177d95b
    Image:         docker.io/istio/proxy_init:1.0.2
    Image ID:      docker-pullable://docker.io/istio/proxy_init@sha256:e16a0746f46cd45a9f63c27b9e09daff5432e33a2d80c8cc0956d7d63e2f9185
    .....
    State:          Terminated
      Reason:       Completed
    .....
    Ready:          True
Containers:
  demo-red:
    Container ID:   docker://8cd9957955ff7e534376eb6f28b56462099af6dfb8b9bc37aaf06e516175495e
    Image:          chugtum/blue-green-image:v3
    Image ID:       docker-pullable://docker.io/chugtum/blue-green-image@sha256:274756dbc215a6b2bd089c10de24fcece296f4c940067ac1a9b4aea67cf815db
    State:          Running
      Started:      Sun, 09 Dec 2018 18:12:31 -0800
    Ready:          True
  istio-proxy:
    Container ID:  docker://ca5d690be8cd6557419cc19ec4e76163c14aed2336eaad7ebf17dd46ca188b4a
    Image:         docker.io/istio/proxyv2:1.0.2
    Image ID:      docker-pullable://docker.io/istio/proxyv2@sha256:54e206530ba6ca9b3820254454e01b7592e9f986d27a5640b6c03704b3b68332
    Args:
      proxy
      sidecar
      .....
    State:          Running
      Started:      Sun, 09 Dec 2018 18:12:31 -0800
    Ready:          True
    .....

出力でわかるように、istio-initコンテナのStateTerminatedで、ReasonCompletedです。実行されているコンテナは、メインアプリケーションのdemo-redコンテナとistio-proxyコンテナの2つのみです。

自動インジェクション

ほとんどの場合、istioctlコマンドを使用してアプリケーションをデプロイするたびにサイドカーを手動でインジェクションしたくはないでしょうが、Istioが自動的にポッドにサイドカーをインジェクションすることを好むでしょう。これは推奨されるアプローチであり、これを機能させるために必要なのは、アプリをデプロイしている名前空間にistio-injection=enabledというラベルを付けることだけです。

ラベル付けされると、Istioはその名前空間にデプロイするすべてのポッドに自動的にサイドカーをインジェクションします。次の例では、istio-dev名前空間にデプロイされたポッドにサイドカーが自動的にインジェクションされます。

$ kubectl get namespaces --show-labels
NAME           STATUS    AGE       LABELS
default        Active    40d       <none>
istio-dev      Active    19d       istio-injection=enabled
istio-system   Active    24d       <none>
kube-public    Active    40d       <none>
kube-system    Active    40d       <none>

しかし、これはどのように機能するのでしょうか。これを理解するには、Kubernetesアドミッションコントローラーを理解する必要があります。

Kubernetesのドキュメントより

自動サイドカーインジェクションの場合、IstioはMutating Admission Webhookに依存しています。istio-sidecar-injectorミューテーティングWebhook構成の詳細を見てみましょう。

$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml
SNIPPET from the output:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"admissionregistration.k8s.io/v1beta1","kind":"MutatingWebhookConfiguration","metadata":{"annotations":{},"labels":{"app":"istio-sidecar-injector","chart":"sidecarInjectorWebhook-1.0.1","heritage":"Tiller","release":"istio-remote"},"name":"istio-sidecar-injector","namespace":""},"webhooks":[{"clientConfig":{"caBundle":"","service":{"name":"istio-sidecar-injector","namespace":"istio-system","path":"/inject"}},"failurePolicy":"Fail","name":"sidecar-injector.istio.io","namespaceSelector":{"matchLabels":{"istio-injection":"enabled"}},"rules":[{"apiGroups":[""],"apiVersions":["v1"],"operations":["CREATE"],"resources":["pods"]}]}]}
  creationTimestamp: 2018-12-10T08:40:15Z
  generation: 2
  labels:
    app: istio-sidecar-injector
    chart: sidecarInjectorWebhook-1.0.1
    heritage: Tiller
    release: istio-remote
  name: istio-sidecar-injector
  .....
webhooks:
- clientConfig:
    service:
      name: istio-sidecar-injector
      namespace: istio-system
      path: /inject
  name: sidecar-injector.istio.io
  namespaceSelector:
    matchLabels:
      istio-injection: enabled
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods

ここでは、サイドカーインジェクションとistio-injection: enabledラベルが一致するWebhook namespaceSelectorラベルを確認できます。この場合、ポッドが作成されたときにこれが行われる操作とリソースも表示されます。apiserverが、ルールの1つに一致するリクエストを受信すると、apiserverclientConfig:構成で指定されたWebhookサービスにname: istio-sidecar-injectorキーと値のペアを持つアドミッションレビューリクエストを送信します。このサービスがistio-system名前空間で実行されていることを確認できるはずです。

$ kubectl get svc --namespace=istio-system | grep sidecar-injector
istio-sidecar-injector   ClusterIP   10.102.70.184   <none>        443/TCP             24d

この構成は、最終的に手動インジェクションで見たものとほぼ同じことを行います。ポッドの作成中に自動的に実行されるだけであり、デプロイメントの変更は表示されません。kubectl describeを使用して、サイドカープロキシとinitプロキシを表示する必要があります。

自動サイドカーインジェクションは、WebhookのnamespaceSelectorメカニズムだけでなく、デフォルトのインジェクションポリシーとポッドごとのオーバーライドアノテーションにも依存しています。

istio-sidecar-injector ConfigMapを再度見ると、デフォルトのインジェクションポリシーが定義されています。この例では、デフォルトで有効になっています。

$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}'
SNIPPET from the output:

policy: enabled
template: |-
  initContainers:
  - name: istio-init
    image: "gcr.io/istio-release/proxy_init:1.0.2"
    args:
    - "-p"
    - [[ .MeshConfig.ProxyListenPort ]]

ポッドテンプレートでアノテーションsidecar.istio.io/injectを使用して、デフォルトポリシーをオーバーライドすることもできます。次の例では、Deployment内のポッドのサイドカーの自動インジェクションを無効にします。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: ignored
spec:
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"
    spec:
      containers:
      - name: ignored
        image: tutum/curl
        command: ["/bin/sleep","infinity"]

この例は、名前空間、ConfigMap、またはポッドで自動サイドカーインジェクションが制御されているかどうかによって、多くの変数があることを示しており、それらは次のとおりです。

インジェクションステータステーブルは、上記の変数の値に基づいて最終的なインジェクションステータスを明確に示しています。

アプリケーションコンテナからサイドカープロキシへのトラフィックフロー

サイドカーコンテナとinitコンテナがアプリケーションマニフェストにどのようにインジェクションされるかについては理解できましたが、サイドカープロキシはどのようにしてコンテナとの間のインバウンドおよびアウトバウンドトラフィックを取得するのでしょうか?ポッドの名前空間内でiptableルールを設定することによって行われることを簡単に説明しました。これはistio-initコンテナによって行われます。ここで、名前空間内で実際に何が更新されるかを確認する時が来ました。

前のセクションでデプロイしたアプリケーションポッドの名前空間に入り、構成済みのiptablesを確認しましょう。nsenterを使用した例を示します。または、特権モードでコンテナに入り、同じ情報を確認することもできます。ノードにアクセスできない場合は、execを使用してサイドカーに入り、iptablesを実行する方が実用的です。

$ docker inspect b8de099d3510 --format '{{ .State.Pid }}'
4125
$ nsenter -t 4215 -n iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N ISTIO_INBOUND
-N ISTIO_IN_REDIRECT
-N ISTIO_OUTPUT
-N ISTIO_REDIRECT
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 80 -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15001
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ISTIO_REDIRECT
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001

上記の出力は、red-demoアプリケーションがリッスンしているポートであるポート80へのすべての着信トラフィックが、Envoyプロキシであるistio-proxyがリッスンしているポートであるポート15001REDIRECTEDされていることを明確に示しています。発信トラフィックについても同様です。

これで、この投稿は終わりです。Istioが既存のデプロイメントにサイドカープロキシをどのようにインジェクションし、Istioがトラフィックをプロキシにどのようにルーティングしているかを解き明かすのに役立てば幸いです。

この投稿を共有する