VM のサーバーに接続できない?localhost・127.0.0.1・0.0.0.0 の違い
VM 内のサーバーが起動しているのにホストから接続できない原因を、localhost、127.0.0.1、0.0.0.0、NAT、Vagrant、Docker の仕組みから解説します。
以前から私は、自分の PC 上に VM を作り、その中で開発するようにしています。主な目的は、開発環境とリソース構成をそろえることです。それぞれの VM に同程度の CPU とメモリを割り当てておけば、開発者ごとの PC 性能の違いによる影響を抑えられます。
しかし、そこで何度も同じ問題に遭遇しました。VM 内ではサーバーが正常に起動しているのに、ホスト側のブラウザからどうしても接続できないのです。
その後、ようやく重要な違いを理解しました。ホストの localhost は VM の localhost ではなく、ホストにとっての「自分自身」は VM にとっての「自分自身」でもありません。
この問題を Google で調べると、多くの場合は次の答えにたどり着きます。
127.0.0.1を0.0.0.0に変更すれば接続できる。
確かにこの方法で解決することは多いのですが、なぜアドレスを 1 つ変更するだけで接続できるのでしょうか。この記事では基本から順に、localhost、127.0.0.1、0.0.0.0 の違いと、VM や Docker の中でそれぞれがどのような役割を持つのかを説明します。
先に結論
| 名前 | 種類 | 意味 | 主な用途 |
|---|---|---|---|
localhost | Hostname | ローカルマシンのホスト名 | 人が扱いやすい名前でローカルマシンを表す |
127.0.0.1 | IP Address | IPv4 のループバックアドレス | 同じネットワーク環境内からのアクセスだけを受け付ける |
0.0.0.0 | Special Address | 利用可能なすべての IPv4 ネットワークインターフェース | 複数のインターフェースからの接続を受け付ける |
最も重要なポイントは次のとおりです。
127.0.0.1が表すのは、現在のネットワーク環境における「自分自身」だけであり、ホストではありません。
localhost:IP アドレスではなく名前
localhost は IP アドレスではなく、Hostname(ホスト名)です。多くのシステムでは、hosts ファイルに次のような対応関係が定義されています。
1
127.0.0.1 localhost
そのため、localhost は通常 127.0.0.1 に名前解決されます。システムによっては、IPv6 の ::1 が優先される場合もあります。
簡単にまとめると、次のようになります。
localhostは名前。127.0.0.1は実際に使用される IPv4 のループバックアドレス。
実際の利用では置き換えられることが多いものの、概念としては同じではありません。
127.0.0.1:自分自身からの接続だけを受け付ける
127.0.0.1 は IPv4 の Loopback Address(ループバックアドレス)です。このアドレス宛てのパケットは物理ネットワークインターフェースを通らず、現在のネットワーク環境へそのまま戻されます。
たとえば、アプリケーションを次のように設定したとします。
1
app.run(host="127.0.0.1", port=5000)
この設定では、サーバーはループバックインターフェースだけを Listen します。つまり、同じマシン、Container、または VM の内部からの接続しか受け付けません。
この PC の LAN 内の IP アドレスが 192.168.1.100 だとすると、ローカルでは次の URL に接続できます。
1
2
http://127.0.0.1:5000
http://localhost:5000
しかし、ほかのデバイスから次の URL には接続できません。
1
http://192.168.1.100:5000
サーバーが 192.168.1.100 のネットワークインターフェースを Listen していないためです。
0.0.0.0:すべてのネットワークインターフェースを Listen する
サーバーの Bind 設定における 0.0.0.0 は、利用可能なすべての IPv4 ネットワークインターフェースを Listen するという意味です。実際に接続先として使用する特定の IP アドレスではありません。
1 台の PC が、同時に次のような複数のアドレスを持つことがあります。
1
2
3
127.0.0.1
192.168.1.100
10.0.0.5
アプリケーションを次のように設定するとします。
1
app.run(host="0.0.0.0", port=5000)
サーバーは利用可能なすべてのインターフェースで 5000 Port を Listen するため、次の各アドレスから接続できる可能性があります。
1
2
3
127.0.0.1:5000
192.168.1.100:5000
10.0.0.5:5000
ただし、0.0.0.0 はサーバー側の待ち受け設定であり、通常はブラウザに入力する接続先ではありません。ブラウザでは、ホストや VM が実際に持っていて、接続元から到達できる IP アドレスを使用します。
0.0.0.0に Bind すると、同じネットワーク上の別のデバイスからもサービスへ接続できる場合があります。開発時には便利ですが、意図せずサービスを公開しないよう、ファイアウォール、認証、適切なネットワーク制限も組み合わせる必要があります。
VM が 127.0.0.1 に Bind すると、なぜホストから接続できないのか?
ネットワークの観点では、VM は独立した別のコンピューターとして動作します。ホストと VM は、それぞれ独自のループバックインターフェース、IP アドレス、ネットワークインターフェースを持っています。
1
2
3
4
5
6
7
Host
├── localhost / 127.0.0.1
└── 192.168.56.1
VM
├── localhost / 127.0.0.1
└── 192.168.56.101
VM 内のアプリケーションを次のように設定した場合を考えます。
1
app.run(host="127.0.0.1", port=5000)
このアプリケーションは、VM 自身のループバックインターフェースだけを Listen します。
1
2
lo 127.0.0.1 ← Listen している
eth0 192.168.56.101 ← Listen していない
そのため、VM 内で http://127.0.0.1:5000 を開けば接続できますが、ホストから http://192.168.56.101:5000 へアクセスすることはできません。
次のように変更すると、状況が変わります。
1
app.run(host="0.0.0.0", port=5000)
サーバーは lo、eth0 など、VM が持つ各インターフェースを Listen します。ホストから VM のネットワークへ到達できれば、次の URL で接続できます。
1
http://192.168.56.101:5000
0.0.0.0 に変更しても接続できないのはなぜか?
「サービスが Listen しているか」と「ネットワークとして到達できるか」は、別の階層の問題だからです。
- Bind:サーバーは、そのネットワークインターフェースからの接続を受け付けているか。
- Routing:ホストから VM までパケットを届けられるか。
- Firewall:OS やクラウドのファイアウォールルールが、その Port の通信を許可しているか。
0.0.0.0 で解決できるのは Bind の問題だけです。ホストから VM へ到達できるかどうかは、VM のネットワークモードやファイアウォール設定にも左右されます。
NAT
NAT は、よく使用されるデフォルトのネットワークモードです。
1
Host → Virtual NAT → VM → Internet
通常、VM から Internet へは接続できますが、外部から VM へ直接接続を開始することはできません。ホストから VM 内のサービスへ接続するには、ホストの 5000 Port を VM の 5000 Port へ転送するような Port Forwarding の設定が必要です。
Vagrant の
forwarded_portは、まさにこの構成に当たります。次の設定では、ホストの8080Port を VM の5000Port へ転送します。また、host_ipを指定することで、ホスト自身からだけ接続できるように制限しています。
1 2 3 4 config.vm.network "forwarded_port", guest: 5000, host: 8080, host_ip: "127.0.0.1"このときの接続経路は、
Host の 127.0.0.1:8080 → Vagrant NAT Port Forwarding → VM の 5000 Port → Serverです。そのため、VM 内のサーバーは引き続き0.0.0.0:5000を Listen する必要があります。VM 自身の127.0.0.1:5000だけを Listen している場合、VM のネットワークインターフェースへ転送された通信はサーバーまで届きません。設定後にvagrant reloadを実行すれば、ホストからhttp://127.0.0.1:8080でサービスへ接続できます。
Host-Only
Host-Only は、ホストと VM の通信だけに使う仮想ネットワークを作成します。
1
2
3
Host 192.168.56.1
↕
VM 192.168.56.101
サービスが VM の対応するインターフェースを Listen していれば、ホストから次の URL で接続できます。
1
http://192.168.56.101:5000
Bridged
Bridged モードでは、VM がホストと同じ物理ネットワークへ直接参加します。
1
2
3
Router
├── Host 192.168.1.100
└── VM 192.168.1.101
この場合、VM は LAN 上のもう 1 台のコンピューターとほぼ同じです。ネットワークとファイアウォールが許可していれば、ホストやスマートフォンから次の URL へ接続できます。
1
http://192.168.1.101:5000
VM のどの IP アドレスへ接続すればよいのか?
VM には、複数のネットワークインターフェースが同時に存在する場合があります。
1
2
3
4
lo 127.0.0.1
ens33 10.0.2.15
ens34 192.168.56.101
docker0 172.17.0.1
サーバーが 0.0.0.0:5000 を Listen していても、ブラウザに入力する IP アドレスは、次の条件によって決まります。
- ホストからそのサブネットへ到達できるか。
- VM が NAT、Host-Only、Bridged のどのモードを使用しているか。
- VM とホストのファイアウォールが
5000Port を許可しているか。
Linux VM では、次のコマンドで IP アドレスを確認できます。
1
ip addr
アプリケーションが実際に Listen しているアドレスと Port は、次のコマンドで確認できます。
1
ss -lntp
Docker でも 0.0.0.0 に Bind する必要があるのはなぜか?
Container にも独立したネットワーク環境があります。Container 内のアプリケーションが、次のアドレスだけを Listen しているとします。
1
app.run(host="127.0.0.1", port=5000)
Container の起動時に Port Mapping を設定していても、
1
docker run -p 5000:5000 my-app
ホストから接続できない場合があります。アプリケーションが Container 内部のループバックインターフェースからの接続しか受け付けていないためです。
アプリケーションを次のように変更します。
1
app.run(host="0.0.0.0", port=5000)
これでサーバーは Container のネットワークインターフェースも Listen するようになり、Docker の Port Mapping がホストからの接続をアプリケーションへ転送できるようになります。
接続できないときの確認手順
接続に失敗した場合は、次の順番で確認できます。
- アプリケーションは本当に起動しているか? VM または Container の内部で
curl http://127.0.0.1:5000を実行して確認します。 - アプリケーションはどこを Listen しているか?
ss -lntpを使い、127.0.0.1:5000と0.0.0.0:5000のどちらを Listen しているか確認します。 - VM の IP アドレスは何か?
ip addrを使って、ホストから到達できる IP アドレスを探します。 - VM のネットワークモードは正しいか? NAT では通常 Port Forwarding が必要です。Host-Only または Bridged では、対応する VM の IP アドレスを使用します。
- ファイアウォールが通信を許可しているか? VM、ホスト、またはクラウド環境で対象の Port がブロックされていないか確認します。
- Docker を使用している場合、Port は公開されているか?
docker psに想定どおりの Port Mapping が表示されているか確認します。
まとめ
localhostはホスト名であり、通常は127.0.0.1または::1に名前解決されます。127.0.0.1はループバックアドレスであり、現在のネットワーク環境における自分自身だけを表します。0.0.0.0は、サーバーにすべての IPv4 ネットワークインターフェースを Listen させるための設定であり、ブラウザが接続先として使用する実際の IP アドレスではありません。- ホスト、VM、Container は、それぞれ独自のネットワーク環境と
localhostを持っています。 - サービスが Listen していても、必ずネットワークから到達できるとは限りません。Routing、VM のネットワークモード、ファイアウォールも確認する必要があります。
これらの概念を理解すれば、VM や Docker だけでなく、Kubernetes、クラウド環境、物理サーバーでネットワークの問題が発生したときにも、どの階層に原因があるのかをすばやく判断できます。単に「127.0.0.1 を 0.0.0.0 に変更する」とだけ覚える必要はなくなります。