Open1

Docker環境で学ぶ 0.0.0.0 と 127.0.0.1 の違いと確認方法

tech_mwtech_mw

Docker上で開発をしていると、「ポートは開いているはずなのにブラウザからアクセスできない」という状況に遭遇することがあります。
原因のひとつとして「0.0.0.0 で待ち受けているか」「127.0.0.1 で待ち受けているか」の違いがあります。
本記事では /proc/net/tcp※ を例に、実際の確認方法を整理します。
(※ コマンドだけで手軽に確認したかったため採用しました)

環境

pc:MacBook Pro(2019)
os:macos Sequoia
Python:3.13.1

構成

.
├── docker-compose.yml
├── express
│   ├── Dockerfile
│   ├── package-lock.json
│   ├── package.json
│   └── server.js
├── README
└── vite
    ├── Dockerfile
    ├── index.html
    ├── package-lock.json
    └── package.json

各種ファイル

docker-compose.yml
version: "3.9"
services:
  express:
    build: ./express
    ports:
      - "3000:3000"

  vite-nohost:
    build: ./vite
    command: ["npm","run","dev"]
    ports:
      - "5173:5173"

  vite-host:
    build: ./vite
    command: ["npm","run","dev","--","--host","0.0.0.0"]
    ports:
      - "5174:5173"

express

express/Dockerfile
FROM node:20
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY server.js ./
EXPOSE 3000
CMD ["node","server.js"]
express/package.json(package-lock.json)
{
  "name": "express-min",
  "version": "1.0.0",
  "private": true,
  "main": "server.js",
  "scripts": { "start": "node server.js" },
  "dependencies": { "express": "^4.19.2" }
}
express/server.js
const express = require("express");
const app = express();
const PORT = 3000;
app.get("/", (_req, res) => res.send("Hello from Express!"));
app.listen(PORT);

vite

  • vite は開発環境で利用する検証用サーバー
vite/Dockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package.json index.html /app/
RUN npm install
# CMDは compose.yml 側で指定(--host 有無を切り替える)
vite
<!DOCTYPE html>
<html>
  <head><meta charset="utf-8"><title>Vite Host Check</title></head>
  <body>
    <h1>Hello from Vite !</h1>
  </body>
</html>
vite/package.json(package-lock.json)
{
  "name": "vite-host-check",
  "private": true,
  "scripts": {
    "dev": "vite --port 5173"
  },
  "devDependencies": {
    "vite": "^5.2.0"
  }
}

検証コマンド

まずはサービスを稼働します。

$ docker compose up -d

/proc/net/tcp の見方

  • docker ps の PORTS はホスト側の公開設定。コンテナ内の bind 先(0.0.0.0 / 127.0.0.1)は分からないため、/proc/net/tcp で確認します。

ポート番号は16進数で表記される

  • 0BB8 (hex) = 3000 (dec) → Express サーバーのポート
  • 1435 (hex) = 5173 (dec) → Vite dev サーバーのポート

(進数変換についての記事です↓)
https://zenn.dev/tech_mw/scraps/b70956b196fb58

アドレス部はリトルエンディアン(逆順に読んで変換)

  • 00000000 → 0.0.0.0(全IF待受)
  • 0100007F → 127.0.0.1(ローカル専用)

例:

  • 00000000:PORT = 0.0.0.0:PORT(外部からアクセス可)
  • 0100007F:PORT = 127.0.0.1:PORT(コンテナ内からのみアクセス可)

実際の確認

Express(3000番)

$ docker exec -it host-check_express_1 \
  sh -lc "grep -E ':(0BB8)' /proc/net/tcp /proc/net/tcp6 || true"
/proc/net/tcp6:   0: 00000000000000000000000000000000:0BB8 ...

→ 000...000:0BB8 は IPv6 全IF (:: :3000)。
実質 0.0.0.0:3000 → 全IF待受(外部アクセス可)

$ curl -i http://localhost:3000
HTTP/1.1 200 OK ...
Hello from Express!

→ OK

Vite(--host を付けない場合):

$ docker exec -it host-check_vite-nohost_1 \
  sh -lc "grep -E ':(1435)' /proc/net/tcp /proc/net/tcp6 || true"
/proc/net/tcp:   0: 0100007F:1435 ...

→ 0100007F:1435 = 127.0.0.1:5173。
→ コンテナ内だけで有効

$ curl -i http://localhost:5173
curl: (52) Empty reply from server

→ NG

Vite(--host 0.0.0.0 を指定した場合):

$ docker exec -it host-check_vite-host_1 \
  sh -lc "grep -E ':(1435)' /proc/net/tcp /proc/net/tcp6 || true"
/proc/net/tcp:   1: 00000000:1435 ...

→ 00000000:1435 = 0.0.0.0:5173。
→ 外部からもアクセス可能

$ curl -i http://localhost:5174
HTTP/1.1 200 OK ...

→ OK

  • Express はデフォルトで 0.0.0.0 にバインドされる(外部OK)
  • Vite はデフォルトで 127.0.0.1 にバインドされる(内部のみ)
  • --host 0.0.0.0 を付けると Vite もコンテナ外からもアクセス可になる
  • /proc/net/tcp を読むことで「実際にどこへバインドされているか」が正しく確認できる