投稿

VM のサーバーに接続できない?localhost・127.0.0.1・0.0.0.0 の違い

VM 内のサーバーが起動しているのにホストから接続できない原因を、localhost、127.0.0.1、0.0.0.0、NAT、Vagrant、Docker の仕組みから解説します。

VM のサーバーに接続できない?localhost・127.0.0.1・0.0.0.0 の違い

以前から私は、自分の PC 上に VM を作り、その中で開発するようにしています。主な目的は、開発環境とリソース構成をそろえることです。それぞれの VM に同程度の CPU とメモリを割り当てておけば、開発者ごとの PC 性能の違いによる影響を抑えられます。

しかし、そこで何度も同じ問題に遭遇しました。VM 内ではサーバーが正常に起動しているのに、ホスト側のブラウザからどうしても接続できないのです。

その後、ようやく重要な違いを理解しました。ホストの localhost は VM の localhost ではなく、ホストにとっての「自分自身」は VM にとっての「自分自身」でもありません。

この問題を Google で調べると、多くの場合は次の答えにたどり着きます。

127.0.0.10.0.0.0 に変更すれば接続できる。

確かにこの方法で解決することは多いのですが、なぜアドレスを 1 つ変更するだけで接続できるのでしょうか。この記事では基本から順に、localhost127.0.0.10.0.0.0 の違いと、VM や Docker の中でそれぞれがどのような役割を持つのかを説明します。

先に結論

名前種類意味主な用途
localhostHostnameローカルマシンのホスト名人が扱いやすい名前でローカルマシンを表す
127.0.0.1IP AddressIPv4 のループバックアドレス同じネットワーク環境内からのアクセスだけを受け付ける
0.0.0.0Special 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)

サーバーは loeth0 など、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 は、まさにこの構成に当たります。次の設定では、ホストの 8080 Port を VM の 5000 Port へ転送します。また、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 とホストのファイアウォールが 5000 Port を許可しているか。

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 がホストからの接続をアプリケーションへ転送できるようになります。

接続できないときの確認手順

接続に失敗した場合は、次の順番で確認できます。

  1. アプリケーションは本当に起動しているか? VM または Container の内部で curl http://127.0.0.1:5000 を実行して確認します。
  2. アプリケーションはどこを Listen しているか? ss -lntp を使い、127.0.0.1:50000.0.0.0:5000 のどちらを Listen しているか確認します。
  3. VM の IP アドレスは何か? ip addr を使って、ホストから到達できる IP アドレスを探します。
  4. VM のネットワークモードは正しいか? NAT では通常 Port Forwarding が必要です。Host-Only または Bridged では、対応する VM の IP アドレスを使用します。
  5. ファイアウォールが通信を許可しているか? VM、ホスト、またはクラウド環境で対象の Port がブロックされていないか確認します。
  6. 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.10.0.0.0 に変更する」とだけ覚える必要はなくなります。

この投稿は投稿者によって CC BY 4.0 の下でライセンスされています。

トレンドのタグ