Istioのサイドカーインジェクションモデルを解き明かす
Istioが既存のデプロイメントにデータプレーンコンポーネントをどのようにプラグインしているかを解き明かします。
Istioサービスメッシュアーキテクチャの簡単な概要は、常にコントロールプレーンとデータプレーンの説明から始まります。
アプリケーションポッドへのサイドカーインジェクションは、手動インジェクションも可能ですが、自動的に行われることを理解することが重要です。トラフィックは、開発者がそれについて心配する必要なく、アプリケーションサービスとこれらのサイドカーとの間でやり取りされます。アプリケーションがIstioサービスメッシュに接続されると、開発者はサービスメッシュが提供するすべてのメリットを利用し始めることができます。ただし、データプレーンの配管はどのように行われるのか、また、それをシームレスに動作させるために実際に何が必要なのでしょうか。この記事では、サイドカーインジェクションの仕組みを明確に理解するために、サイドカーインジェクションモデルの詳細を深く掘り下げていきます。
サイドカーインジェクション
簡単に言えば、サイドカーインジェクションとは、ポッドテンプレートに追加のコンテナの構成を追加することです。Istioサービスメッシュに必要な追加のコンテナは次のとおりです。
istio-init
このinitコンテナは、インバウンド/アウトバウンドトラフィックがサイドカープロキシを通過するようにiptables
ルールを設定するために使用されます。initコンテナは、次の点でアプリコンテナとは異なります。
- アプリコンテナが開始される前に実行され、常に完了まで実行されます。
- 多くの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-proxy
もTPROXY
モードで構成されている場合は同様であることに注意することが重要です。これはポッドの名前空間に制限されているため、問題はないはずです。ただし、最近のOpenShiftバージョンでは問題が発生する可能性があり、回避策が必要になることに気付きました。その1つのオプションは、この投稿の最後に記載されています。
サイドカーインジェクションのために現在のポッドテンプレートを変更するには、次のようにすることができます。
$ istioctl kube-inject -f demo-red.yaml | kubectl apply -f -
または
変更されたconfigmapまたはローカルconfigmapを使用するには
configmapから
inject-config.yaml
およびmesh-config.yaml
を作成します。$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml $ kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
既存のポッドテンプレート(私の場合は
demo-red.yaml
)を変更します。$ istioctl kube-inject --injectConfigFile inject-config.yaml --meshConfigFile mesh-config.yaml --filename demo-red.yaml --output demo-red-injected.yaml
demo-red-injected.yaml
を適用します。$ kubectl apply -f demo-red-injected.yaml
上記のように、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
コンテナのState
はTerminated
で、Reason
はCompleted
です。実行されているコンテナは、メインアプリケーションの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アドミッションコントローラーを理解する必要があります。
自動サイドカーインジェクションの場合、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つに一致するリクエストを受信すると、apiserver
はclientConfig:
構成で指定された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、またはポッドで自動サイドカーインジェクションが制御されているかどうかによって、多くの変数があることを示しており、それらは次のとおりです。
- webhooks
namespaceSelector
(istio-injection: enabled
) - デフォルトポリシー(ConfigMap
istio-sidecar-injector
で構成) - ポッドごとのオーバーライドアノテーション(
sidecar.istio.io/inject
)
インジェクションステータステーブルは、上記の変数の値に基づいて最終的なインジェクションステータスを明確に示しています。
アプリケーションコンテナからサイドカープロキシへのトラフィックフロー
サイドカーコンテナと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
がリッスンしているポートであるポート15001
にREDIRECTED
されていることを明確に示しています。発信トラフィックについても同様です。
これで、この投稿は終わりです。Istioが既存のデプロイメントにサイドカープロキシをどのようにインジェクションし、Istioがトラフィックをプロキシにどのようにルーティングしているかを解き明かすのに役立てば幸いです。