すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:サービスメッシュ環境でサーバーアプリケーションがクライアントの送信元 IP を取得する方法

最終更新日:Oct 17, 2025

このトピックでは、Service Mesh 環境でサービスにアクセスする際に、サーバーアプリケーションがクライアントの送信元 IP アドレスを取得して保持する方法について説明します。

前提条件

背景情報

送信元 IP アドレスは、次の典型的なユースケースを含む多くのシナリオで広く使用されています。

  • アプリケーションのアクセス制御: たとえば、多くのアプリケーションは、ユーザーが異なるリージョンからログインしたことを検出すると、追加の認証を強制します。これは、送信元 IP アドレスを取得することで実現できます。

  • シンプルなセッション維持: クライアントアドレスに応じて、送信元 IP アドレスに基づいてロードバランシングを実行できます。これにより、同じクライアントからのリクエストが同じサービスインスタンスに転送されます。

  • アクセスログとモニタリング統計: 送元 IP アドレスを含むアクセスログとメトリックは、開発者が統計を分析するのに役立ちます。

クラウドベースの Server Load Balancer も、クライアントの送信元 IP をバックエンドサービスに渡すことをサポートしています。Istio は、アプリケーションが送信元 IP アドレスを取得する機能を提供する必要があります。しかし、Istio を使用する場合、Sidecar プロキシが Pod に挿入された後、すべてのインバウンドトラフィックは Envoy からリダイレクトされます。現在、Envoy はローカルアドレス (127.0.0.1) にバインドされているアプリケーションにトラフィックを送信するため、アプリケーションは実際の送信元 IP アドレスを確認できません。

サンプルアプリケーションのデプロイ

  1. sleep アプリケーションをデプロイします。

    1. 次の内容を使用して sleep.yaml を作成します。

      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: sleep
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: sleep
        labels:
          app: sleep
          service: sleep
      spec:
        ports:
        - port: 80
          name: http
        selector:
          app: sleep
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: sleep
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: sleep
        template:
          metadata:
            labels:
              app: sleep
          spec:
            terminationGracePeriodSeconds: 0
            serviceAccountName: sleep
            containers:
            - name: sleep
              image: curlimages/curl
              command: ["/bin/sleep", "3650d"]
              imagePullPolicy: IfNotPresent
              volumeMounts:
              - mountPath: /etc/sleep/tls
                name: secret-volume
            volumes:
            - name: secret-volume
              secret:
                secretName: sleep-secret
                optional: true
    2. 次のコマンドを実行して sleep アプリケーションをデプロイします。

      kubectl -n default apply -f  sleep.yaml
  2. httpbin アプリケーションをデプロイします。

    1. 次の内容を使用して httpbin.yaml を作成します。

      apiVersion: v1
      kind: Service
      metadata:
        name: httpbin
        labels:
          app: httpbin
      spec:
        ports:
        - name: http
          port: 8000
        selector:
          app: httpbin
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: httpbin
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: httpbin
            version: v1
        template:
          metadata:
            labels:
              app: httpbin
              version: v1
          spec:
            containers:
            - image: docker.io/citizenstig/httpbin
              imagePullPolicy: IfNotPresent
              name: httpbin
              ports:
              - containerPort: 8000
    2. 次のコマンドを実行して httpbin アプリケーションをデプロイします。

      kubectl -n default apply -f  httpbin.yaml

シナリオ 1: 東西トラフィック

ステップ 1: デフォルト構成でサーバーに表示されるクライアントの送信元 IP アドレスの確認

Istio では、東西サービスにアクセスすると、Sidecar の挿入により、サービスに出入りするすべてのトラフィックが Envoy によってインターセプトされ、プロキシされます。その後、Envoy はリクエストをアプリケーションに転送します。したがって、アプリケーションが受信するリクエストの送信元アドレスは、Envoy のアクセスアドレスである 127.0.0.6 になります。

  1. 次のコマンドを実行して Pod のステータスを確認します。

    kubectl -n default get pods -o wide

    期待される結果:

    NAME                            READY   STATUS        RESTARTS   AGE     IP             NODE                     NOMINATED NODE   READINESS GATES
    httpbin-c85bdb469-4ll2m         2/2     Running       0          3m22s   172.17.X.XXX   cn-hongkong.10.0.0.XX    <none>           <none>
    sleep-8f764df66-q7dr2           2/2     Running       0          3m9s    172.17.X.XXX   cn-hongkong.10.0.0.XX    <none>           <none>

    期待される結果から、sleep アプリケーションのアドレスが 172.17.X.XXX であることがわかります。

  2. 次のコマンドを実行して sleep コンテナーからリクエストを送信します。

    kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ip

    期待される結果:

    {
      "origin": "127.0.0.6"
    }

    期待される結果から、httpbin アプリケーションが受信したリクエストの送信元アドレスは、sleep アプリケーションのアドレスではなく、Envoy のアクセスアドレスである 127.0.0.6 であることがわかります。

  3. ソケット情報から送信元 IP アドレスが 127.0.0.6 であることを確認します。

    1. httpbin コンテナーにログインし、次のコマンドを実行して netstat をインストールします。

      apt update & apt install net-tools
    2. httpbin コンテナーを終了し、次のコマンドを実行してポート 80 に基づく実行情報を表示します。

      kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 80

      期待される結果:

      tcp        0      0 172.17.X.XXX:80         127.0.0.6:42691         TIME_WAIT   -

      期待される結果から、送信元 IP アドレスが 127.0.0.6 であることがわかります。

  4. httpbin Pod のプロキシログの内容を表示します。

    フォーマットされたログの例は次のとおりです。

    {
      "trace_id":null,
      "bytes_received":0,
      "upstream_host":"172.17.X.XXX:80",
      "authority":"httpbin:8000",
      "downstream_remote_address":"172.17.X.XXX:56160",
      "upstream_service_time":"1",
      "upstream_transport_failure_reason":null,
      "istio_policy_status":null,
      "path":"/ip",
      "bytes_sent":28,
      "request_id":"4501a50a-dab0-44c9-b52c-2a4f425a****",
      "protocol":"HTTP/1.1",
      "method":"GET",
      "duration":1,
      "start_time":"2022-11-22T16:09:30.394Z",
      "user_agent":"curl/7.86.0-DEV",
      "upstream_local_address":"127.0.0.6:42169",
      "response_flags":"-",
      "route_name":"default",
      "response_code":200,
      "upstream_cluster":"inbound|80||",
      "x_forwarded_for":null,
      "downstream_local_address":"172.17.X.XXX:80",
      "requested_server_name":"outbound_.8000_._.httpbin.default.svc.cluster.local"
    }

    ログから、次の情報を取得できます。

    • "downstream_remote_address":"172.17.X.XXX:56160": sleep のアドレス。

    • "downstream_local_address":"172.17.X.XXX:80": sleep がアクセスした宛先アドレス。

    • "upstream_local_address":"127.0.0.6:42169": httpbin に接続する httpbin Envoy のローカルアドレス (この時点で取得される送信元 IP アドレスは 127.0.0.6 です)。

    • "upstream_host":"172.17.X.XXX:80": httpbin Envoy がアクセスした宛先アドレス。

ステップ 2: サーバーが正しいクライアントの送信元 IP アドレスを取得できるようにサービスメッシュを構成する

方法 1: TPROXY 透明インターセプトモードを介してクライアントの送信元 IP アドレスを保持する (CentOS 以外のオペレーティングシステムに適用可能)

httpbin アプリケーションのデプロイメントを変更して、インバウンドトラフィックのインターセプトモードとして TPROXY を使用します。

  1. 次のコマンドを実行して、httpbin アプリケーションのデプロイメントを変更します。

    kubectl patch deployment -n default httpbin -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/interceptionMode":"TPROXY"}}}}}'                       
  2. 次のコマンドを実行して sleep コンテナーからリクエストを送信します。

    kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ip

    期待される結果:

    {
      "origin": "172.17.X.XXX"
    }

    期待される結果から、httpbin が sleep の実際の IP アドレスを取得できることがわかります。

  3. 次のコマンドを実行して、ポート 80 に基づく実行情報を表示します。

    説明

    Pod が再起動された後、netstat を再インストールする必要があります。

    kubectl -n default exec -it deploy/httpbin -c httpbin -- netstat -ntp | grep 80

    期待される結果:

    tcp        0      0 172.17.X.XXX:80         172.17.X.XXX:36728      ESTABLISHED -

    期待される結果から、送信元 IP アドレスが 172.17.X.XXX であることがわかります。

  4. httpbin Pod のプロキシログの内容を表示します。

    フォーマットされたログの例は次のとおりです。

    {
      "route_name":"default",
      "bytes_received":0,
      "trace_id":null,
      "request_id":"1ccabe60-63cf-469b-8565-99cac546****",
      "upstream_cluster":"inbound|80||",
      "response_flags":"-",
      "protocol":"HTTP/1.1",
      "upstream_transport_failure_reason":null,
      "requested_server_name":"outbound_.8000_._.httpbin.default.svc.cluster.local",
      "response_code":200,
      "user_agent":"curl/7.86.0-DEV",
      "start_time":"2022-11-22T16:03:32.803Z",
      "path":"/ip",
      "authority":"httpbin:8000",
      "bytes_sent":31,
      "downstream_remote_address":"172.17.X.XXX:39058",
      "upstream_service_time":"1",
      "method":"GET",
      "downstream_local_address":"172.17.X.XXX:80",
      "duration":1,
      "upstream_host":"172.17.X.XXX:80",
      "istio_policy_status":null,
      "upstream_local_address":"172.17.X.XXX:46129",
      "x_forwarded_for":null
    }

    ログから、次の情報を取得できます。

    • "downstream_remote_address":"172.17.X.XXX:39058": sleep のアドレス。

    • "downstream_local_address":"172.17.X.XXX:80": sleep がアクセスした宛先アドレス。

    • "upstream_local_address":"172.17.X.XXX:46129": httpbin に接続する httpbin Envoy のローカルアドレス (sleep の IP アドレス)。

    • "upstream_host":"172.17.X.XXX:80": httpbin Envoy がアクセスした宛先アドレス。

方法 2: XFF リクエストヘッダーを介してクライアントの送信元 IP アドレスを取得する

次の構成を使用して、サーバーアプリケーションのメッシュプロキシ Sidecar が、インバウンドリクエストをアプリケーションに転送する前に、リクエストに X-Forwarded-For リクエストヘッダーを追加するようにできます。リクエストヘッダーの値は、実際のクライアント IP アドレスに設定されます。この方法にはノードのオペレーティングシステムに関する制限はありませんが、アプリケーションが XFF リクエストヘッダーによって運ばれるクライアントの送信元 IP アドレスを識別して取得できる必要があります。

  1. Envoy フィルターテンプレートを使用して Envoy フィルターを作成する」を参照して、EnvoyFilter テンプレートを介して次の EnvoyFilter を ASM インスタンスに適用します。

    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: enable-xff-for-sidecar-inbound
      namespace: istio-system  # この名前空間をゲートウェイが配置されている名前空間に変更します
      labels:
        asm-system: "true"
        provider: "asm"
    spec:
      configPatches:
      - applyTo: NETWORK_FILTER
        match:
          proxy:
            proxyVersion: "^1.*"
          context: SIDECAR_INBOUND 
          listener:
            name: "virtualInbound"
            filterChain:
              filter:
                name: "envoy.filters.network.http_connection_manager"
        patch:
          operation: MERGE
          value:
            typed_config:
              "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
              use_remote_address: true
  2. 次のコマンドを実行して sleep コンテナーからリクエストを送信します。

    kubectl -n default exec -it deploy/sleep -c sleep -- curl http://httpbin:8000/ip
  3. 期待される結果:

    {
      "origin": "172.17.X.XXX"
    }

    期待される結果から、httpbin が sleep の実際の IP アドレスを取得できることがわかります。クライアントの送信元 IP アドレスは、Sidecar によって X-Forwarded-For リクエストヘッダーを介して httpbin アプリケーションに渡されることに注意してください。httpbin アプリケーションは X-Forwarded-For リクエストヘッダーからクライアントの送信元 IP アドレスを取得することをサポートしているため、クライアントの送信元 IP アドレスを正しく読み取ることができます。アプリケーションに X-Forwarded-For リクエストヘッダーから送信元 IP アドレスを取得する機能がない場合、この方法は適用できません。

シナリオ 2: 南北のトラフィック

南北のトラフィックの場合、クライアントはまず Server Load Balancer にリクエストを送信し、次にリクエストは Istio イングレスゲートウェイに転送され、その後バックエンドサービスに転送されます。中間にイングレスゲートウェイが追加されるため、クライアントの送信元 IP アドレスの取得はより複雑になります。以下のセクションでは、HTTP および HTTPS プロトコルリクエストの送信元 IP アドレスの保持を構成および検証する方法について説明します。

HTTP プロトコルリクエスト

送信元 IP の保持なし

  1. 次の内容を使用して http-demo.yaml を作成し、HTTP プロトコルを使用して httpbin にアクセスします。

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: httpbin-gw-httpprotocol
      namespace: default
    spec:
      selector:
        istio: ingressgateway
      servers:
        - hosts:
            - '*'
          port:
            name: http
            number: 80
            protocol: HTTP
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin
      namespace: default
    spec:
      gateways:
        - httpbin-gw-httpprotocol
      hosts:
        - '*'
      http:
        - route:
            - destination:
                host: httpbin
                port:
                  number: 8000
  2. 次のコマンドを実行して、ゲートウェイと仮想サービスをデプロイします。

    kubectl -n default apply -f  http-demo.yaml
  3. 次のコマンドを実行して、イングレスゲートウェイを介して httpbin にアクセスします。

    export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    curl http://$GATEWAY_URL:80/ip

    期待される結果:

    {
      "origin": "10.0.0.93"
    }

    期待される結果から、返された IP アドレスが Kubernetes クラスターのノードアドレスであることがわかります。

  4. イングレスゲートウェイのアクセスログを表示します。

    サンプルログ:

    {
      "upstream_service_time":"1",
      "response_code":200,
      "protocol":"HTTP/1.1",
      "bytes_sent":28,
      "upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local",
      "start_time":"2022-11-23T03:29:20.017Z",
      "istio_policy_status":null,
      "upstream_transport_failure_reason":null,
      "trace_id":null,
      "route_name":null,
      "request_id":"292903be-a889-4d5d-83a0-ab1f5d1a****",
      "method":"GET",
      "upstream_host":"172.17.X.XXX:80",
      "duration":1,
      "path":"/ip",
      "downstream_local_address":"172.17.X.XXX:80",
      "authority":"47.242.XXX.XX",
      "user_agent":"curl/7.79.1",
      "downstream_remote_address":"10.0.0.93:5899",
      "upstream_local_address":"172.17.X.XXX:54322",
      "requested_server_name":null,
      "x_forwarded_for":"10.0.0.93",
      "response_flags":"-",
      "bytes_received":0
    }

    ログから、次の情報を取得できます。

    • "downstream_remote_address":"10.0.0.93:5899": 実際のクライアントの送信元アドレスではありません。

    • "downstream_local_address":"172.17.X.XXX:80": イングレスゲートウェイ Pod のアドレス。

    • "upstream_local_address":"172.17.X.XXX:54322": イングレスゲートウェイ Pod のアドレスは保持されますが、ポートの値は変更されます。

    • "upstream_host":"172.17.X.XXX:80": httpbin Pod のアドレス。

送信元 IP の保持あり

  1. 外部トラフィックポリシーを Local に設定します。(Terway ネットワークモードのクラスターでは、このステップをスキップしてください。)

    1. ASM コンソールにログインします。左側のナビゲーションウィンドウで、[Service Mesh] > [メッシュ管理] を選択します。

    2. [メッシュ管理] ページで、ASM インスタンスの名前をクリックします。左側のナビゲーションウィンドウで、[ASM ゲートウェイ] > [イングレスゲートウェイ] を選択します。

    3. [イングレスゲートウェイ] ページで、対象のゲートウェイの右側にある [YAML の表示] をクリックします。

    4. [編集] ダイアログボックスで、spec フィールドの下にある externalTrafficPolicy フィールドを Local に設定し、[OK] をクリックします。设置外部流量策略为Local

  2. 次のコマンドを実行して、イングレスゲートウェイを介して httpbin にアクセスします。

    curl http://$GATEWAY_URL:80/ip

    期待される結果:

    {
      "origin": "120.244.xxx.xxx"
    }

    期待される結果から、返された IP アドレスが実際のクライアントの送信元 IP アドレスであることがわかります。

  3. イングレスゲートウェイのアクセスログを表示します。

    サンプルログ:

    {
      "istio_policy_status":null,
      "upstream_transport_failure_reason":null,
      "path":"/ip",
      "x_forwarded_for":"120.244.XXX.XXX",
      "route_name":null,
      "method":"GET",
      "duration":2,
      "downstream_remote_address":"120.244.XXX.XXX:28504",
      "bytes_received":0,
      "upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local",
      "bytes_sent":34,
      "protocol":"HTTP/1.1",
      "response_flags":"-",
      "upstream_local_address":"172.17.X.XXX:57498",
      "upstream_service_time":"2",
      "request_id":"9c0295d4-e77f-4a3a-b292-e5c58d92****",
      "start_time":"2022-11-23T03:24:04.413Z",
      "response_code":200,
      "trace_id":null,
      "authority":"47.242.XXX.XX",
      "user_agent":"curl/7.79.1",
      "downstream_local_address":"172.17.X.XXX:80",
      "upstream_host":"172.17.X.XXX:80",
      "requested_server_name":null
    }

    ログから、次の情報を取得できます。

    • "downstream_remote_address":"120.244.XXX.XXX:28504": 期待どおりのクライアントの送信元アドレス。

    • "downstream_local_address":"172.17.X.XXX:80": イングレスゲートウェイ Pod のアドレス。

    • "upstream_local_address":"172.17.X.XXX:57498": イングレスゲートウェイ Pod のアドレスは保持されますが、ポートの値は変更されます。

    • "upstream_host":"172.17.X.XXX:80": httpbin Pod のアドレス。

HTTPS プロトコルリクエスト

前のセクションでは、HTTP プロトコルリクエストの送信元 IP 保持を設定する前後の比較について詳しく説明しました。したがって、このセクションでは、HTTPS プロトコルリクエストの送信元 IP 保持を構成および検証する方法についてのみ説明します。

  1. 送信元 IP の保持を設定します

  2. 次の内容を使用して https-demo を作成し、HTTPS プロトコルを使用して httpbin にアクセスします。

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: httpbin-gw-https
      namespace: default
    spec:
      selector:
        istio: ingressgateway
      servers:
        - hosts:
            - '*'
          port:
            name: https
            number: 443
            protocol: HTTPS
          tls:
            credentialName: myexample-credential
            mode: SIMPLE
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin-https
      namespace: default
    spec:
      gateways:
        - httpbin-gw-https
      hosts:
        - '*'
      http:
        - route:
            - destination:
                host: httpbin
                port:
                  number: 8000
  3. 次のコマンドを実行して、ゲートウェイと仮想サービスをデプロイします。

    kubectl -n default apply -f  https-demo.yaml
  4. 次のコマンドを実行して、イングレスゲートウェイを介して httpbin にアクセスします。

    export GATEWAY_URL=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    curl -k https://$GATEWAY_URL:443/ip

    期待される結果:

    {
      "origin": "120.244.XXX.XXX"
    }

    期待される結果から、返された IP アドレスが実際のクライアントの送信元 IP アドレスであることがわかります。