TT Lab
はじめる
学ぶ 学習パス コース

Istio深化 — なぜそう流れるのか

アプリは知らないのにすべての接続がプロキシを通る理由

TT Labで続きを見る

一言でいうと

サイドカーの注入は、Podにコンテナを2つ追加します。1回動いて終わるistio-initが、iptablesに番号(15001・15006・1337)を仕込み、長く生きるistio-proxyが、その番号で待ち受けます。アプリは何も変えていないのに、すべての接続がプロキシを通る理由が、この番号です。

なぜ必要なのか

サービスメッシュの約束は、「コードを直さずに、すべての呼び出しにmTLS・リトライ・観測を付ける」ことです。そのためには、アプリがhttp://reviews:9080へ送った接続を、アプリに気づかれずに、プロキシが受け取る必要があります。ライブラリを埋め込む方式は、言語ごとに別々に作らなければならず、アプリがそのライブラリを使ってくれる必要があります。そこでIstioは、カーネル側で横取りします。Podのネットワークネームスペースにiptablesのルールを入れて、出ていく接続は15001へ、入ってくる接続は15006へ振り向けます。

ここですぐに、2つの問題が生じます。1つ目は、プロキシが受け取ったリクエストを再び外へ送ると、その接続もルールに掛かって、15001へ戻ってくることです。無限ループです。2つ目は、ルールを書き換えるにはNET_ADMIN権限が必要ですが、その権限を常に動いているプロキシに与えると、プロキシが突破されたときに、Podのネットワークをまるごと変えられるようになることです。注入の出力物の形は、この2つの問題への答えです。

どう動くのか

istioctl kube-injectが作ったマニフェストを開くと、番号が散らばっています。まとめると、次のとおりです。

番号 書かれている場所 意味
15001 istio-init -p アプリが外へ送る接続がリダイレクトされて入ってくる場所(virtualOutbound)
15006 istio-init -z 外から入ってくる接続がリダイレクトされて入ってくる場所(virtualInbound)
1337 istio-init -u、istio-proxy runAsUser このユーザーが送ったパケットは横取りしない
15021 readinessProbe プロキシ自身の準備状態
15020 prometheus.io/portアノテーション アプリとプロキシのメトリクスを合わせて出力する場所
15090 コンテナポートhttp-envoy-prom Envoy自身のメトリクス

ループの問題は、-u 1337で解決します。プロキシコンテナをUID 1337で起動し、そのUIDが送ったパケットは、ルールから除きます。2か所の番号がずれると、プロキシが自分のリクエストをまた受け取って、CPUを使います。権限の問題は、コンテナを分けて解決します。istio-initは、rootでNET_ADMIN・NET_RAWを持ち、ルールだけを仕込んで終わり、istio-proxyはrootではなく、すべての権限を捨て、ファイルシステムも読み取り専用です。-d 15090,15021,15020は、入ってくる側の横取りから除くポートです。kubeletのヘルスチェックとPrometheusのスクレイプが、Envoyのルーティングを通ってはいけないからです。

Envoy側から見ると、15006はvirtualInboundリスナーで、元の宛先ポートを見て、inbound|<포트>||(プレースホルダーはポート番号です)クラスターへ渡します。15001はvirtualOutboundで、メッシュが知っている宛先なら、そのサービスのリスナーへ、知らなければPassthroughCluster(そのまま通す)かBlackHoleCluster(エンドポイントがなく503)へ送ります。どちらになるかは、メッシュ設定のoutboundTrafficPolicyが決めます。

現場での姿

注入後、アプリが起動する前にリクエストを送って失敗する場合。プロキシが準備できる前にアプリが先に起動して外へ接続すると、ルールはすでにあるのに、15001で受け取る相手がいません。holdApplicationUntilProxyStartsがある理由です。注入の出力物のコンテナの順序を見れば、その設定がどう反映されるかがわかります。

「外部APIだけが503になります」。ログにBlackHoleClusterが出ていれば、メッシュがREGISTRY_ONLYで、そのホストを知らせるServiceEntryがないという意味です。応答本文のno healthy upstreamは、アプリではなく、サイドカーが作ったものです。

アプリが1337で起動する場合。アプリコンテナを偶然UID 1337で起動すると、アプリの接続も横取りから外れて、mTLSなしの平文で出ていきます。何のエラーも出ないので、セキュリティ点検で初めて表に出ます。

公式ドキュメント: Application requirements・Debugging Envoy and Istiod

次のラボですること

オフラインで実際の注入の出力物を作り、番号が書かれた場所をyqで1つずつ取り出します。その後、virtualInboundとvirtualOutboundを手で立てて、アプリまで届くリクエストと、BlackHoleClusterに落ちるリクエストを、自分で見ます。