[Docker]0.0.0.0でサーバーを立てる理由(Python)
はじめに
Dockerコンテナ内でサーバーを立てる際、127.0.0.1(localhost)でlistenするとERR_EMPTY_RESPONSEとなってしまいますが、0.0.0.0でlistenするとアクセスできます.
Dockerを使っていないときは127.0.0.1(localhost)でlistenしていてもアクセスできたので、Dockerを使うとなぜ0.0.0.0でlistenしないといけないのか気になり、この記事を書くに至りました.
Demo
まず、前提としてlistenするアドレスとアクセスの可否についてまとめる.今回はPython(Django)でdemoを行いました.
今回、Demoの内容については結果をまとめるにとどめます.もし興味がある場合は次のリポジトリで確認できます.
Demo結果
行はアクセスするURL、列はlistenするアドレスごとに結果をまとめました.
| 127.0.0.1:8000 | 0.0.0.0:8000 | |
|---|---|---|
http://127.0.0.0.1:8000/ |
ERR_EMPTY_RESPONSE |
正常 |
http://0.0.0.0:8000/ |
ERR_EMPTY_RESPONSE |
正常 |
表からもわかるように、127.0.0.1でサーバーを立てた場合はアクセスできないが、0.0.0.0でサーバーを立てた場合はアクセスできるという結果となった.
ここで気になるのが
- なぜ
0.0.0.0でサーバーを立てないとアクセスできないのか -
0.0.0.0でサーバーを立てた時、http://127.0.0.0.1:8000/、http://0.0.0.0.0:8000/どちらでもアクセスできるのはなぜか(0.0.0.0とはなにを指すのか)
ということです.
本題
さて、本題に入りましょう.
127.0.0.1(localhost)でlistenするとなんでだめ?
今回、127.0.0.1でlistenしているのはDockerコンテナであり、ローカルマシンの127.0.0.1とは違います.そのため、ローカルマシンでhttp://127.0.0.0.1:8000/にアクセスしても、Dockerコンテナの127.0.0.1にはアクセスできないため、エラーとなってしまいます.
じゃあ0.0.0.0って?
サーバーでの0.0.0.0
0.0.0.0でサーバーを立てると、そのホストの全てのインターフェースでlistenします.また、127.0.0.1でサーバーを立てた場合と比較すると、0.0.0.0で起動した場合は同一ネットワーク内の別ホストからアクセス可能ですが、127.0.0.1でサーバーを立てた場合は他のホストからはアクセスできないという点が異なっています.今回の場合、ホストはDockerコンテナです.
※以下、サーバー側は0.0.0.0でlistenしているとします.
宛先としての0.0.0.0
宛先としての0.0.0.0とは今回で言うと、webページを見るためにアクセスするhttp://0.0.0.0:8000/の0.0.0.0にあたります.
では、この宛先が意味するところを順を追って確認していきましょう.
まず、http://0.0.0.0:8000/というのは、ざっくり言うとhttpというプロトコルを用いて、IPアドレスが0.0.0.0の8000番ポートにリクエストを投げてねという意味です.
しかし、RFC6890によると0.0.0.0は無効な宛先のアドレスとされているようです.つまり、今回のhttp://0.0.0.0:8000/では無効なアドレスに向かってリクエストを投げているということになります.
なぜ無効なアドレスであるはずの0.0.0.0にアクセスできるのか
前述の通り、0.0.0.0は無効なアドレスであるはずですがアクセスできています.その理由は、OSがよしなに書き換えてくれているからです.具体的には、宛先アドレスが0.0.0.0だった場合、送信元アドレスか127.0.0.1に書き換えてくれるようです.
まとめ
- Dockerコンテナ内で
127.0.0.1(localhost)でサーバーを立ててもローカルマシンの127.0.0.1(localhost)とは異なるため、アクセスできない. -
0.0.0.0でサーバーを立てるとそのホストの全てのインターフェースでlistenするため、ローカルマシンからもアクセスできる. - 宛先アドレスを
0.0.0.0にするとOSが送信元アドレスか127.0.0.1に書き換えてくれる.
Discussion