AWS App MeshはどのようにしてEnvoy Proxyを経由するのか

こんにちは、以前「サービスメッシュと AWS App Mesh に入門した」という記事を書きました。

AWS App Mesh では、メッシュ上にデプロイされているアプリケーションのトラフィックは、すべてサイドカーとしてデプロイされている Envoy Proxy を経由します。 これは Pod 内から Pod 外への通信と、Pod 外から Pod 内のアプリケーションの両方です。

AWS App Meshのネットワーク。重みつきロードバランシングの例。

しかし既存のサービスに AWS App Mesh を導入するときは、クライアントやサーバーの指し先を localhost に書き換えることはありません。 この記事では、AWS App Mesh がどのようにして、各 Pod が Envoy Proxy をプロキシするのかを解決します。

カスタムコントローラによる注入

前回の記事で説明したとおり、Amazon EKS 上ならカスタムコントローラによって、Pod に Envoy Proxy サイドカーが注入されます。 カスタムコントローラは Pod に対して、以下の 2 つのコンテナを注入します。

  1. ネットワークを初期化する init コンテナ
  2. サイドカーコンテナとなる Envoy Proxy

同じ Pod 内のコンテナは同じネットワークを利用します。 init コンテナがネットワークを設定することで、サービスが利用するネットワークを初期化できます。

init コンテナの処理

App Mesh 上にデプロイする Pod は、Egress(Pod 外への通信)と Ingress(Pod への通信)の両方がサイドカーコンテナの Envoy Proxy を経由します。 init コンテナは、Pod 外への通信を Envoy Proxy の Egress ポートに流し、Pod への通信を Envoy Proxy の Ingress ポートに流すよう初期化します。

このコンテナの内部ではシェルスクリプトで、 iptables コマンドによって Pod 内のネットワークを設定します。 スクリプト全体はこちらから確認できます。 このスクリプトは主に 3 つの処理をします。

  • initialize() ... ネットワーク設定の初期化
  • enable_egress_routing() Egress ネットワークの設定
  • enable_ingress_redirect_routing() ... Ingress ネットワークの設定

ネットワーク設定の初期化

まずはネットワーク設定の初期化です。 これはスクリプトとの initialize() に記述されています。

まずはネットワークルールをする前に、 iptables コマンドで新たなチェインを作成します。 チェインとはiptables のルールまとめた単位で、チェインに対してルールを追加したり、条件を元に利用するチェインを選択できます。

if [ ! -z "$APPMESH_APP_PORTS" ]; then
    iptables -t nat -N APPMESH_INGRESS
fi
iptables -t nat -N APPMESH_EGRESS

$APPMESH_APP_PORTS は Pod 内のサービスが待ち受けるポート番号で、サービスがリクエストを受け付けるなら Ingress ネットワークのチェインを作成します。 (スクリプトには他にも mangle テーブルを設定していますが、現在は利用されていないようです

Egress ネットワークの設定

Pod 内から Pod 外へのネットワーク設定は enable_egress_routing() に記述されています。 Pod 内からの通信を全て、Envoy Proxy の $APPMESH_ENVOY_EGRESS_PORT (15001)に転送するよう設定します。 ただし Envoy Proxy 自身は直接 Pod 外に通信する必要があります。

# (1) Envoy Proxy自身のUID/GIDからの通信はEnvoy Proxyを経由しない
[ ! -z "$APPMESH_IGNORE_UID" ] && \
    iptables -t nat -A APPMESH_EGRESS \
    -m owner --uid-owner $APPMESH_IGNORE_UID \
    -j RETURN

[ ! -z "$APPMESH_IGNORE_GID" ] && \
    iptables -t nat -A APPMESH_EGRESS \
    -m owner --gid-owner $APPMESH_IGNORE_GID \
    -j RETURN

# (2) 特定のポート、アドレスもEnvoy Proxyを経由しない
[ ! -z "$APPMESH_EGRESS_IGNORED_PORTS" ] && \
    iptables -t nat -A APPMESH_EGRESS \
    -p tcp \
    -m multiport --dports "$APPMESH_EGRESS_IGNORED_PORTS" \
    -j RETURN

[ ! -z "$APPMESH_EGRESS_IGNORED_IP" ] && \
    iptables -t nat -A APPMESH_EGRESS \
    -p tcp \
    -d "$APPMESH_EGRESS_IGNORED_IP" \
    -j RETURN

# (3) それ以外の通信はEnvoy ProxyのEgressポートにリダイレクト
iptables -t nat -A APPMESH_EGRESS \
    -p tcp \
    -j REDIRECT --to $APPMESH_ENVOY_EGRESS_PORT

# (4) ローカル宛以外の全ての通信にAPPMESH_EGRESSチェインを使う
iptables -t nat -A OUTPUT \
    -p tcp \
    -m addrtype ! --dst-type LOCAL \
    -j APPMESH_EGRESS
  1. Envoy Proxy の UID(1337) と、GID(1337) からのパケットは、RETURN ターゲットを指定します。
  2. $APPMESH_ENVOY_INGRESS_PORT (22、SSH ポート)宛と、 $APPMESH_EGRESS_IGNORED_IP (169.254.169.254、EC2 インスタンスのメタデータ)宛の TCP パケットは、 RETURN ターゲットを指定します。
  3. それ以外の TCP パケットは REDIRECT ターゲットで APPMESH_ENVOY_EGRESS_PORT(15001)に転送します。
  4. ローカル宛以外の全ての TCP パケットに、APPMESH_EGRESS チェインを適用します。

RETURN ターゲットは、以降のパケットの評価をやめます。 これにより一部のパケットを、Envoy Proxy ではなく Pod 外と直接やりとりできます。

Ingress ネットワークの設定

Pod 外から Pod 内へのネットワーク設定は enable_ingress_redirect_routing() に記述されています。 本来アプリケーションが listen しているポート ($APPMESH_APP_PORTS)宛のパケットを、 Envoy Proxy の $APPMESH_ENVOY_INGRESS_PORT ポート(15000)に転送するよう設定します。

# (1) 全ての$APPMESH_APP_PORTS宛のパケットをEnvoy ProxyのIngressポートにリダイレクト
iptables -t nat -A APPMESH_INGRESS \
    -p tcp \
    -m multiport --dports "$APPMESH_APP_PORTS" \
    -j REDIRECT --to-port "$APPMESH_ENVOY_INGRESS_PORT"

# (2) ローカル以外からの全ての通信にAPPMESH_INGRESSチェインを使う
iptables -t nat -A PREROUTING \
    -p tcp \
    -m addrtype ! --src-type LOCAL \
    -j APPMESH_INGRESS
  1. 全ての$APPMESH_APP_PORTS宛のパケットを Envoy Proxy の Ingress ポートにリダイレクトします。
  2. ローカル以外からの全ての通信に APPMESH_INGRESS チェインを適用します。

まとめ

以上が Kubernetes マニフェストをほぼ修正することなく、App Mesh を導入できる仕組みです。 透過的にサービスメッシュが使える背景には、トラディッショナルな方法でネットワークを設定しているんですね。 App Mesh が動く仕組みがわかると、トラブル時のデバッグやネットワークの知識が深まりますね。


Profile picture

Shin'ya Ueoka

B2B向けSaaSを提供する会社の、元Webエンジニア。今はエンジニアリング組織のマネジメントをしている。