Istioのiptablesを理解したい
HUIT Advent Calendar 2023の14日目の記事です。
Istioを触ってて気になったことを書き残していきます。
初めに
IstioのEnvoyをサイドカーとして挿入する構造はわかりやすいですが、 目的地のアドレスを入力するだけで自動的にEnvoyを通す仕組みが気になり調べてみました。
Version
Istio: 1.20.1
Istioとは
雑に説明すると、Kubernetesで扱えるサービスメッシュ実装の1つ。
ここら辺はわかりやすい記事が沢山あるので貼っておきます。
新卒でもわかる!Istioとは? | SIOS Tech. Lab
Istioの構造
大まかにコントロールプレーンとデータプレーンの2つに分かれています。
コントロールプレーンではポリシーやプロキシ設定の管理を。
データプレーンでは各PodにEnvoyをプロキシーとして配置、サービス間の通信を制御する構成です。
全ての通信をEnvoyに通すために
アプリケーションからはProxyの存在を意識することなく、目的地のアドレスを指定して通信することができます。
ここで、その状態でProxyを通すにはどうしているのか気になりました。
調べてみると、Istioではサービスからの通信を、iptablesを用いて制御しているらしいです。
これはPod毎に指定されており、INPUT, OUTPUTそれぞれにおいてEnvoyに一度リダイレクトするように指定されています。
iptablesの設定を確認する
iptablesの設定を見ていきたいです。
手元のkindで簡単なnginxサーバーを構築してIstioをインストールしてみました。
Pod内のコンテナはネットワーク名前空間を共有しているので、iptablesやport,deviceなどは共有されているみたいです。(初めはコンテナが2つあるので、iptablesが別々に設定されるものかと思ってた)
下の実行結果から、nginxとenvoyのプロセスが同一のネットワーク名前空間に属していることがわかります。
# docker execでノード内に入り、lsnsコマンドで確認 (4026533805は名前空間のid)
$ lsns 4026533805
PID PPID USER COMMAND
1122 1102 65535 /pause
1252 1102 root nginx: master process nginx -g daemon off;
1297 1252 _rpc |-nginx: worker process
1298 1252 _rpc |-nginx: worker process
1299 1252 _rpc |-nginx: worker process
1300 1252 _rpc |-nginx: worker process
1301 1252 _rpc |-nginx: worker process
1302 1252 _rpc |-nginx: worker process
1304 1252 _rpc |-nginx: worker process
1305 1252 _rpc `-nginx: worker process
1314 1102 1337 /usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local --proxyLogLevel=warning --proxyComponentLogLevel=misc:error --log_output_level=default:info
1338 1314 1337 `-/usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json
それでnginxのコンテナに入り、iptablesコマンドで確認したものは以下になります。
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
ISTIO_INBOUND tcp -- anywhere anywhere
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ISTIO_OUTPUT tcp -- anywhere anywhere
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
Chain ISTIO_INBOUND (1 references)
target prot opt source destination
RETURN tcp -- anywhere anywhere tcp dpt:15008
RETURN tcp -- anywhere anywhere tcp dpt:15090
RETURN tcp -- anywhere anywhere tcp dpt:15021
RETURN tcp -- anywhere anywhere tcp dpt:15020
ISTIO_IN_REDIRECT tcp -- anywhere anywhere
Chain ISTIO_IN_REDIRECT (3 references)
target prot opt source destination
REDIRECT tcp -- anywhere anywhere redir ports 15006
Chain ISTIO_OUTPUT (1 references)
target prot opt source destination
RETURN all -- 127.0.0.6 anywhere
ISTIO_IN_REDIRECT tcp -- anywhere !localhost tcp dpt:!15008 owner UID match 1337
RETURN all -- anywhere anywhere ! owner UID match 1337
RETURN all -- anywhere anywhere owner UID match 1337
ISTIO_IN_REDIRECT tcp -- anywhere !localhost tcp dpt:!15008 owner GID match 1337
RETURN all -- anywhere anywhere ! owner GID match 1337
RETURN all -- anywhere anywhere owner GID match 1337
RETURN all -- anywhere localhost
ISTIO_REDIRECT all -- anywhere anywhere
Chain ISTIO_REDIRECT (1 references)
target prot opt source destination
REDIRECT tcp -- anywhere anywhere redir ports 15001
- UIDの1337はEnovyを実行しているユーザー
- Envoyの重要なポート番号
- 15001: Envoy outbound
- 15006: Envoy inbound
他のポート番号は公式ドキュメントを参照
https://istio.io/latest/docs/ops/deployment/requirements/
これだけでは理解が難しいので、入力と出力の2種類を分けて詳しくみていきます。
Outbound
アプリケーションからの出力では、Servcie -> Envoy
とEnvoy -> Service
の2通りに分けて考えることができます。
Service -> Envoy
このルートの目的は、アプリケーションからのリクエストをEnvoyの15001ポートにリダイレクトすることになります。
ルーティングテーブルを追うと、
OUTPUT
-> ISTIO_OUTPUT
-> ISTIO_REDIRECT
-> REDIRECT(15001)
となっていました。
基本的な全てのリクエストはISTIO_OUTPUT
の一番下のルールに引っ掛かり、ISTIO_REDIRECT
チェーンでport:15001
にリダイレクトしています。
ここでNATでパケットを書き換えることなく、リダイレクトすることで目的地のIPアドレスをそのままにするのがポイントになっていそう。
Envoy -> Service
このルートの目的は、Envoyから目的地にリクエストを投げることです。
ルーティングテーブルを追うと、
OUTPUT
->ISTIO_OUTPUT
-> RETURN
-> POSTROUTING
となっています。
UIDがEnvoyの1337からのリクエストなので、ISTIO_OUTPUT
の上から4番目のルールにヒットしRETURN
されます。
この仕組みでリクエストがループすることを防いでいます。
Inbound
外部ネットワークからの入力でも同様に、Servcie -> Envoy
とEnvoy -> Service
の2通りに分けて考えることができます。
Service -> Envoy
このルートの目的は、外部からの通信をEnvoyの15006ポートにリダイレクトすることです。
PREROUTING
-> ISTIO_INBOUND
-> ISTIO_IN_REDIRECT
-> REDIRECT(15006)
Istioの管理用ポートが予約されており、それは150~となります。それ以外のポートはISTIO_INBOUNDチェーン一番下のISTIO_IN_REDIRECT
にヒットするので、15006にリダイレクトされるようです。
Envoy -> Service
このルートの目的は、Envoyから目的地にリクエストを投げることです。
ルーティングテーブルを追うと、
OUTPUT
->ISTIO_OUTPUT
-> RETURN
-> POSTROUTING
inboundの時はEnvoyが送信元IPアドレスを127.0.0.6
に書き換えるので、一番上のルールに引っ掛かりRETURNされます。
最後に
コンテナの通信をあれこれ調べるの楽しい!
初心者が頑張って調べた程度なので、間違ってる箇所があれば教えてください〜。