アプリケーションが localhost(127.0.0.1)ではなく 0.0.0.0 にバインドされている場合、クラスター内の他の Pod からそのアプリケーションにアクセスすることはできません。Kubernetes サービスが正しいポートを公開している場合でも同様です。本トピックでは、この現象の原因と、対応する 2 つのソリューションについて説明します。
問題の概要
クラスター内のアプリケーションが localhost でリスンしており、他の Pod が Kubernetes サービスを介してそのアプリケーションにアクセスできない状況です。
一般的な localhost バインドパターン:
| 言語 | コード |
|---|---|
| Go | net.Listen("tcp", "localhost:8080") |
| Node.js | http.createServer().listen(8080, "localhost") |
| Python | socket.socket().bind(("localhost", 8083)) |
原因
Kubernetes の各 Pod には独自の IP アドレスが割り当てられます。サービスはこの Pod の IP アドレスにトラフィックをルーティングしますが、Pod 内部の localhost にはルーティングしません。アプリケーションが localhost にバインドされている場合、ループバックインターフェース(127.0.0.1)のみで接続を受け付けるため、その範囲は Pod 固有のネットワーク名前空間に限定されます。Pod の IP アドレスに到達したトラフィックは、アプリケーションに届くことはありません。
ASM によって管理されるクラスターでは、Istio サイドカープロキシがさらに 1 つのレイヤーを追加します。すなわち、iptables ルールを介してインバウンドトラフィックをインターセプトし、アプリケーションに転送します。アプリケーションが localhost のみでリスンしている場合、サイドカープロキシはトラフィックをアプリケーションに配信できません。これは、転送先が Pod の IP アドレスであり、ループバックアドレスではないためです。
ソリューション
以下の 2 つのアプローチが利用可能です:
| アプローチ | 適用条件 |
|---|---|
| バインドアドレスの変更 | アプリケーションのソースコードを制御できる場合 |
| Sidecar リソースの構成 | アプリケーション コードは変更できません。 |
ソリューション 1:バインドアドレスの変更
アプリケーションのコードを更新し、0.0.0.0 でリスンするように変更します(localhost ではなく)。これにより、アプリケーションはすべてのネットワークインターフェース(Pod の IP を含む)で接続を受け付けるようになります。
| 言語 | 変更前(localhost) | 変更後(すべてのインターフェース) |
|---|---|---|
| Go | net.Listen("tcp", "localhost:8080") | net.Listen("tcp", "0.0.0.0:8080") |
| Node.js | http.createServer().listen(8080, "localhost") | http.createServer().listen(8080, "0.0.0.0") |
| Python | socket.socket().bind(("localhost", 8083)) | socket.socket().bind(("0.0.0.0", 8083)) |
更新後のコードをデプロイした後、他の Pod がサービスを介してアプリケーションに到達可能であることを確認してください。
ソリューション 2:Sidecar リソースの構成
アプリケーションのコードを変更できない場合は、インバウンドトラフィックを localhost アドレスに転送する Istio Sidecar リソースを作成します。サイドカープロキシはサービスのポートでトラフィックを受信し、Pod 内の 127.0.0.1:<container-port> へリダイレクトします。
操作手順
-
ASM コンソールにログインします。左側のナビゲーションウィンドウで、 を選択します。
-
[メッシュ管理] ページで、ASM インスタンスの名前をクリックします。 左側のナビゲーションウィンドウで、 を選択します。 表示されたページで、[YAML から作成] をクリックします。
YAML からの作成 をクリックします。
[作成] ページで、名前空間とテンプレートを選択し、次の YAML 構成を貼り付け、その後 [作成] をクリックします。
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: localhost-access
namespace: <namespace>
spec:
ingress:
- defaultEndpoint: '127.0.0.1:<container-port>'
port:
name: tcp
number: <service-port>
protocol: TCP
workloadSelector:
labels:
<label-key>: <label-value>以下のプレースホルダーを実際の値に置き換えてください:
| プレースホルダー | 説明 | 例 |
|---|---|---|
<namespace> | アプリケーションがデプロイされている名前空間 | default |
<container-port> | アプリケーションが localhost でリスンしているポート | 8080 |
<service-port> | Kubernetes サービスが公開しているポート | 80 |
<label-key>: <label-value> | ターゲットワークロードを識別する Pod のラベル | app: my-service |
defaultEndpoint フィールドは、サイドカープロキシがインバウンドトラフィックをどこに転送するかを指定します。127.0.0.1:<container-port> に設定することで、リクエストはアプリケーションが既にリスンしているループバックアドレスにルーティングされます。
修正の検証
いずれかのソリューションを適用した後、Pod 間の接続性をテストします:
# 別の Pod から、サービスを介してアプリケーションにリクエストを送信します
kubectl exec -it <test-pod> -- curl http://<service-name>.<namespace>.svc.cluster.local:<service-port>正常な応答が得られた場合、アプリケーションが他の Pod から到達可能であることが確認されます。
リクエストが失敗した場合は、サイドカープロキシのログを確認し、転送エラーがないかを調べます:
kubectl logs <pod-name> -c istio-proxy