Windows上でWSLとDockerを併用しながら、Stable DiffusionやLLMなど生成AIの環境や、将棋AIの環境を構築するにあたって、Dockerコンテナとのネットワークがようやく理解できてきたので、解説したいと思います。
Docker Networkについて、本家のDocsのネットワークに関する解説や、Qiitaの記事等も参考にさせて頂きましたが、いずれもLinux環境における話のようで、Windows環境では食い違うことがあり、仕方がないのでLinuxとWindowsを比較し、実際にアプリケーションと通信させながら確認をしていきました。
ポイント
Windowsでは、docker0というネットワークはできません。
Dockerのネットワークと、ホストのネットワークはIPルーティングできません。すべてIPアドレス変換(IP Masquerade)されます。
外部からのアクセスには、ポートフォワーディングの設定が必須です。
ネットワーク構成図
以下の図は、特別な設定をせずに、Dockerをインストールしてコンテナを起動した場合のネットワーク構成になります。
点線が1台のWindows PCで、それがルーターを介してインターネットに接続されています。物理的にはそれだけの構成です。
PCの中に、仮想的にWindowsアプリケーションが動作するホストOS、WSL2で動作するUbuntu、そして、Docker上のコンテナが存在しています。
Virtual RouterやLocalhost Networkというのは私が仮に置いたもので、こう考えると分かりやすいので加えました。
青線がIPのサブネットとなっているのですが、一番上の物理ネットワーク(192.168.1.0/24)と、WSL2の仮想ネットワーク(172.20.64.0/20)は、Windowsを介してルーティング可能となっています。
しかし、コンテナのサブネット(172.17.0.0/16)は、物理ネットワークともWSL2ともつながっておらず、孤立したネットワークとなっています。
しかし、コンテナを単に起動した場合、コンテナからインターネットへの通信は可能です。
これは、左下のVirtual Routerが、アドレス変換を行って、PCの物理ポートのIPアドレスからの通信に変換することにより、外部に出ることができるようになっています。
これは、家庭内からインターネットにアクセスする際と同じ仕組みです。
逆に、外部のどのネットワークからも、コンテナのサブネットにはアクセスすることは当然できません。しかも、外部だけではなく、ホストOSのWindowsやUbuntuからもアクセスすることができません。
では、どうやってコンテナのサービスにアクセスするのか。ここで、コンテナ起動オプションの-pを使用します。
-p ホスト側のポート番号:コンテナ側のポート番号
とすることで、ポートフォワーディングの設定が行われます。
例えば、図の構成では、Container 1の起動オプションとして、-p 8080:80 と付加すると、2つのマッピングが行われます。
一つは、左下のVirtual Routerにおいて、外部からの送信先192:168.1.23:8080は、172.17.0.2:80に変換されます。また、内部からの送信元172.17.0.2:80は、192:168.1.23:8080に変換されます。
もう一つは、ホストOSのlocalhost:8080への通信は、コンテナの0.0.0.0:80に変換されます。
ですので、Container 1で、172.17.0.2:80でLitsenしておけば、外部からそのサーバーへのアクセスが可能になります。
同じく、0.0.0.0:80でLitsenしておけば、localhost:8080としてアクセスが可能となります。
-pの設定には、IPアドレスを含めることができます。
ネットワークが複雑になった場合でも、上記の考え方を応用すれば、どうすれば良いのかが分かってきます。
考察
Linux環境でも同じことをしてみましたが、Linuxの方がネットワーク的にすっきりしています。ただ、結局必要な設定は同じです。コンテナの内部ネットワークは、外部からの通信を遮断しており、ポートフォワーディングのような設定を行わなければ、ホストとの通信もできません。開発環境としては不便ですが、実運用の環境としてはそれがベターだと思われます。
私が一番はまったのは、ポートフォワーディングの設定をしたにも関わらず、コンテナ側でStable diffusion webuiが起動したWebサーバーにlocalhost:7860で全くアクセスできず、nginxのデフォルトのコンテナにはさくっとアクセスできるという状況に陥った時です。最初はLinuxのセキュリティの設定かと思い、一つ一つ潰していきましたが、ゲストOSの設定に違いはないという結論に至るまで数日掛かりました。結局、LitsenしているIPアドレスが違っているということが分かって、なんで?と思いました。だれか、なぜこんなことになっているのか、理由が分かる人はいないでしょうか?
参考
Docker コンテナ・ネットワークの理解
Docker network 概論
外部ネットワーク側からDockerコンテナに通信できる環境を作成する