🐳

どうしてもDockerコンテナ間をlocalhost:xxxxで通信したい

2025/02/20に公開

結論

リバースプロキシを通しました

経緯

docker composeでwebアプリケーションが2つ起動する環境を構築しました
それぞれApp1, App2とします
App1はlocalhost:8080, App2はlocalhost:8081でアクセスできます
App1からApp2が提供するAPIをリクエストします
また、App1を開いている時にApp2へのリダイレクトが発生します

compose.ymlのイメージ
services:
  app1:
    ports:
      - 8080:80
  app2:
    ports:
      - 8081:80

App1からApp2へのAPI通信

App2のAPIエンドポイント/api/testをApp1からリクエストするためには
localhost:8081/api/test では出来ません。
同じネットワーク内なのでURIをApp2/api/testとしたり、
ホスト側にポートを公開してるので host.docker.internal:8081/api/testにリクエストする必要があります

App2へのリダイレクト

ホストマシンからみたApp2はlocalhost:8081です
App2でもhost.docker.internal:8081でも繋がりません

App1がURIを使い分けたらよいけども

できればAPIとwebブラウザからアクセスするURIは同じものに統一したいです

解決策

App1がlocalhost:8081にアクセスしたらhost.docker.internal:8081にポートフォーワーディングできればよさそう

nginxで解決

App1のコンテナ内にwebサーバ(nginx)が同居してる環境であれば、nginxのリバースプロキシを使うことで解決できそうでした

(省略)
server {
    listen 8081;
    server_name localhost;

    location / {
        proxy_pass http://host.docker.internal:8081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

socatで解決

今回扱った環境ではApp1はApp1のnginxとApp1のアプリケーション(PHP)にコンテナが分かれていたため、nginxのリバースプロキシは使えませんでした。

そこでsocatをインストールすることにしました
alpine linuxなのでapkコマンドでインストール(ubuntuならapt install socatですね)

apk add socat

以下をコンテナ起動時に実行

socat TCP-LISTEN:8081,fork TCP:host.docker.internal:8081

これでlocalhostによる通信が出来ました

脚注

通常、こんなことが必要なケースは滅多にないかと思います

このケースでは、独自のOAuthサーバ、クライアントを開発する環境を作ることになり、
OAuthライブラリは改造せずに使いたいという理由があったので発生しました
(OAuthサーバにlocalhostを設定すると、ログイン時のリダイレクトもアクセストークン取得APIもlocalhostが使われてしまい分けることが出来なかった)

Discussion