SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-07

Docker Composeのネットワーク設定と通信を理解する

Composeのデフォルトbridge、サービス名によるDNS、hostモードの使いどころ、複数プロジェクトでのネットワーク共有、UFWを迂回する公開ポートまで解説します。

アプリケーションの起動前に Compose が構築するもの

Docker Compose のネットワークは、1 つのルールから始まります。docker compose up はプロジェクト用のプライベートネットワークを作成し、すべてのサービスを接続し、サービス名で相互に通信できるようにします。そのために networks: の行を 1 行書く必要はありません。Compose のネットワークで混乱しやすいのは、このデフォルト設定が最初から存在することを把握していないためです。

小さなファイルを示します。ディレクトリ shop を作成し、その中に compose.yaml として保存してください。

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

起動して、Docker が作成したものを確認します。

docker compose up -d
docker network ls

一覧に shop_default というネットワークが追加されています。Compose はこれを <project>_default と命名し、プロジェクト名にはデフォルトでディレクトリ名の小文字表記を使用します。docker compose -p myproject up -d またはファイル内のトップレベルの name: myproject で上書きできます。ドライバーは bridge です。これはホスト内部の仮想スイッチです。各コンテナにはプライベートサブネット上のアドレスが割り当てられ、外向きのトラフィックは送信時にホストのアドレスへ変換されます。

docker compose down を実行すると、そのネットワークも削除されます。古いプロジェクトのコンテナがネットワークを使用中のまま残っていると、ネットワークを保持し続けるのはこのためです。Docker は error while removing network: network shop_default has active endpoints を返して削除を拒否します。対処するには、そのネットワークに接続されたコンテナを停止するか削除してください。

Compose を初めて使用する場合は、Compose ファイルの構成とライフサイクルコマンドを先に読むとよいでしょう。以降の説明は、プロジェクトを起動および停止できることを前提としています。

サービス名による DNS は、初心者が見落としやすい部分です

ユーザー定義ネットワークでは、Docker が組み込み DNS サーバーを実行します。各コンテナは 127.0.0.11 でこの DNS サーバーを参照できます。DNS はサービス名を現在のコンテナアドレスに解決します。そのため、web は設定なしで、ホスト名 db のデータベースへポート 5432 で接続できます。

docker compose exec web getent hosts db

172.18.0.2 db のような行が出力されます。何も出力されない場合、2 つのサービスは同じネットワーク上にありません。

ほとんどの人が一度は犯す間違いは、アプリケーション設定で localhost を使用することです。コンテナ内では、localhost はそのコンテナ自身を指します。ホストや別のサービスは指しません。Postgres クライアントはこれを明確に報告します。

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

接続文字列は postgresql://postgres:example@db:5432/postgres にします。ホスト部分にはサービス名を指定します。

後の作業時間を短縮できる点が 2 つあります。名前は現在実行中のコンテナに解決されるため、docker compose up -d --scale web=3 は 1 つの名前に 3 つのアドレスを返します。DNS を無期限にキャッシュするクライアントは、停止したコンテナに固定されます。また、--network を指定しない単純な docker run が使用する従来の bridge ネットワークでは、名前解決は行われません。そのため、2016 年のコンテナリンクに関する説明は、現在の動作と一致しません。

2 つのサービスを接続するために ports: は必要ありません

ports: は、コンテナのポートをホスト上に公開します。これは、Docker の外部から到着するネットワークトラフィックに使用します。サービス間の通信とは関係ありません。同じプロジェクトネットワーク上では、ポート範囲全体を使用してサービス間通信がすでに機能します。

そのため、多くの人がデータベースサービスに追加する ports: - "5432:5432" は役に立たないだけでなく、実害があります。サーバーのパブリックインターフェースで Postgres を公開してしまうためです。削除してください。移行のためにノート PC から接続する場合は、"127.0.0.1:5432:5432" で loopback にバインドし、SSH トンネル経由で接続してください。listening socket、公開ポート、ファイアウォールルールの違いについては、Linux でポートと待ち受けサービスが動作する仕組みで説明しています。

Compose における expose: は、ドキュメントとしてのみ機能します。同じネットワーク上のコンテナ間では通信が閉じられていないため、何も開きません。

network_mode host が適する場合と、その代償

Host モードでは、コンテナ独自のネットワーク名前空間を使用せず、プロセスがホストのインターフェースを直接使用します。

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

これを必要とする明確な理由があります。ローカルネットワーク上のブロードキャストまたはマルチキャストトラフィックを確認する必要があるプロセスは、ブリッジの背後からでは確認できません。メディアサーバーのデバイス検出やホームオートメーションハブなどが該当します。ブリッジは、そのトラフィックをコンテナへ転送しないためです。ホストのインターフェースカウンターを読み取る監視エージェントにも、ホストのインターフェースが必要です。また、アドレス変換の処理を省略できます。これは、パケットレートが高い場合に重要です。

代償も明確です。

ports: は機能しません。Docker は、host network mode では公開ポートが破棄され、コンテナはプロセスがバインドするポートをそのまま使用すると警告します。Host モードの 2 つのコンテナが port 8080 を使用しようとすると競合し、後から起動したコンテナは bind: address already in use で終了します。

サービス名による名前解決は、両方向で利用できません。コンテナはプロジェクトネットワークに接続されないため、db を名前解決できず、他のサービスからもコンテナを名前解決できません。コンテナが他のサービスへ接続するには、ホスト上で公開されたポートを使用する必要があり、通常は 127.0.0.1 で接続します。

分離も失われます。Host モードのコンテナ内でプロセスが 0.0.0.0 にバインドすると、apt でインストールしたパッケージと同じように、サーバーのすべてのインターフェースで待ち受けます。パブリックインターフェースも含まれます。利点も 1 つあります。このトラフィックは通常の入力経路を通るため、UFW のルールが適用されます。公開ポートでは、この動作になりません。

Host モードは Linux Docker Engine の機能です。Docker Desktop では version 4.34 以降でのみサポートされ、事前に有効化する必要があります。さらに、コンテナはホストの IP アドレスにバインドできず、処理対象は TCP と UDP に限られます。チームの半数が Linux サーバーを使用し、残りの半数が Docker Desktop を使用する場合、同じファイルでも動作が異なることを想定してください。

ホストのインターフェースが必要な場合に Host モードを使用してください。接続問題を解決するために使用してはいけません。通常は、1 つの問題をより複雑な問題に置き換えるだけだからです。

Compose プロジェクト間を external network で接続する

一方のプロジェクトが作成したネットワークは、別のプロジェクトからは見えません。そのため、同じサーバー上でも proxy/compose.yaml のリバースプロキシから app/compose.yaml のアプリを参照できません。解決策は、どちらのプロジェクトも所有しないネットワークを用意することです。

手動で 1 回だけ作成します。

docker network create edge

次に、各プロジェクトで external として宣言します。プロキシ側は次のようにします。

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

アプリケーション側は次のようにします。

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true は、Compose に既存のネットワークへ接続させ、新しいネットワークを作成させない指定です。また、docker compose down 上にそのネットワークを残します。独立した name: キーも重要です。これがない場合、Compose は edge という名前と完全に一致するネットワークを探します。このキーを使うと、ファイル内の名前とホスト上の名前を別々に指定できます。

ネットワークが存在しない場合、Compose は起動を拒否し、そのネットワークが external として宣言されているものの見つからないと報告します。先にネットワークを作成してください。

アプリケーションのファイルで internal がどのように指定されているか確認してください。データベースはそのプロジェクト専用のネットワークにのみ接続されるため、プロキシからは到達できず、app だけが接続できます。ネットワークの下に internal: true を追加すると、外部ネットワークへのルートも完全に削除されます。これはデータベースの適切なデフォルト設定です。ただし、設定前に把握しておくべき注意点があります。internal ネットワーク上のコンテナは何もダウンロードできません。そのため、起動時に apt-get update または pip install を実行する entrypoint はハングし、最終的にタイムアウトで失敗します。

ルーティングルールと証明書を含む完全な構成例については、1 つの Traefik インスタンスの背後で複数のアプリを実行するを参照してください。

公開ポートは UFW を迂回します

Compose のネットワーク設定で、セキュリティインシデントにつながるのがこの部分です。ポートを公開し、UFW が有効で SSH 以外をすべて拒否していることを確認しても、そのサービスにはインターネットから接続できます。

sudo ufw status
curl http://203.0.113.10:8080

UFW はポートがブロックされていると示します。しかし、curl はページを返します。これは故障ではありません。Docker はアドレス変換と転送のルールを独自に iptables へ直接書き込みます。公開されたコンテナポート宛てのトラフィックはホストへ配信されず、コンテナへ転送されます。そのため、ホスト宛てのトラフィックを管理する UFW のチェーンを通過しません。Docker のルールは UFW のルールより先に照合されます。

簡単な対処は、必要な場所にだけ公開することです。

    ports:
      - "127.0.0.1:8080:80"

これによりホスト側は loopback にバインドされます。ポートにはサーバー自身から接続でき、SSH トンネル経由でも接続できますが、それ以外の場所からは接続できません。公開入口は、80 と 443 を意図的に公開するリバースプロキシの背後に置いてください。公開ポートをフィルタリングする必要がある場合の DOCKER-USER チェーンを含む詳しい説明は、Docker が UFW を直接迂回して公開する理由とその修正方法にあります。

4 つのコマンドでデバッグする方法

まず、各コンテナが実際にどのネットワークに接続されているかを確認します。

docker network inspect shop_default

Containers ブロックには、接続されているすべてのコンテナとそのアドレスが表示されます。この一覧にないサービスは、別のネットワーク上にあるか、host モードで動作しているか、起動していません。

同じネットワークに接続した一時コンテナから名前解決をテストします。これにより、自分のイメージ内にツールを用意する必要がありません。

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

nslookup が失敗する場合は、名前解決またはネットワークへの接続に問題があります。nslookup が成功し、nc が失敗する場合、サービスは動作していますが、そのポートで待ち受けていないか、コンテナ内の 127.0.0.1 で待ち受けていて、0.0.0.0 では待ち受けていません。後者は開発用サーバーでよく発生します。修正箇所は Docker ではなく、アプリケーションの bind アドレスです。

Docker のバグに見える別の障害もあります。コンテナ間では通信できるのに、オフィスネットワークや VPN ネットワーク上のマシンへ接続できない場合、Docker のサブネットがそのネットワークと重複している可能性があります。Docker はデフォルトで 172.17.0.0/16 からアドレスプールを割り当てます。/etc/docker/daemon.json でプールを変更します。

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

次に sudo systemctl restart docker を実行し、影響を受けるネットワークを再作成します。既存のネットワークには、作成時に割り当てられたサブネットが保持されるためです。

FAQ

コンテナ間でサービス名を使って通信できないのはなぜですか?

同じネットワーク上にありません。Compose はすべてのサービスを自動的に <project>_default に接続します。ただし、サービスに networks: リストを追加すると、そのリストがそのサービスに設定されるネットワークの完全な一覧になり、デフォルトネットワークは暗黙的に追加されなくなります。docker network inspect <network> を実行し、両方のコンテナが Containers ブロックに表示されることを確認してください。また、どちらのサービスも network_mode: host を使用していないことを確認してください。host モードのコンテナは Docker ネットワークに接続されないため、サービス名を解決できません。

あるサービスから別のサービスへ接続する場合、ポートを公開する必要がありますか?

いいえ。Compose ネットワーク上では、各コンテナのすべてのポートに、そのネットワーク上の他のコンテナから到達できます。ports: は、Docker の外部からのネットワークトラフィックにコンテナを公開する場合にだけ使用します。expose: はドキュメントとしての指定です。データベースのポートを公開するのは、よくあるうえに危険な運用です。データベースがサーバーの公開インターフェースで待ち受けることになるためです。

bridge と host のネットワーク機能の違いは何ですか?

bridge では、コンテナに独自のネットワーク名前空間と仮想スイッチ上のアドレスが割り当てられます。コンテナ間の名前解決と、送信トラフィックの自動変換も行われます。host では、コンテナがホストのネットワークスタックを直接使用します。そのため、個別のアドレス、サービス名による名前解決、ポート公開、ホスト上の他の待ち受けとの分離はありません。デフォルトは bridge であり、プロセスがホストのインターフェースを必要とする場合を除き、bridge を使用するのが適切です。

異なる Compose ファイルに定義されたコンテナを接続するにはどうすればよいですか?

docker network create edge で共有ネットワークを作成し、両方のファイルで external: true を指定して、通信が必要なサービスをそのネットワークに接続します。Compose はそのネットワークを作成も削除もしません。作成手順を省略すると、Compose は起動を拒否し、外部ネットワークとして宣言されているものの見つからないと報告します。

UFW でポートをブロックしているのに、コンテナがインターネットから到達可能なのはなぜですか?

公開ポートは、Docker が iptables に追加する転送ルールによって処理されるためです。これらのルールは UFW のルールより先に評価されます。また、転送トラフィックは UFW がフィルタリングするチェーンを通過しません。"127.0.0.1:8080:80" でホスト側を loopback にバインドし、外部公開するサービスはポート 80 と 443 のリバースプロキシの背後に配置してください。