Docker 入門

Docker ネットワークモデル

ネットワーク (Network) は、独立したコンテナ (Container) 間、コンテナとホストマシン (Host) 間、およびコンテナと外部ネットワーク間の通信を可能にします。適切なネットワーク設定がなければ、コンテナは完全に隔離 (Isolate) された状態になり、大多数のマルチサービスアプリケーション (Multi-service Application) は機能しなくなります。

Docker がネットワークをどのように管理しているかを理解することは、堅牢でスケーラブル (Scalable) かつ安全なコンテナデプロイメント (Deployment) アーキテクチャ (Architecture) を設計するための鍵となります。これは、Web サーバーコンテナがデータベースコンテナとどのように通信 (Communicate) するか、アプリケーションがクライアントのリクエストにどのように応答するか、そして分散システム (Distributed System) 内のサービスがどのように互いをディスカバリー (Discovery) し、相互作用 (Interact) するかを決定します。

本章では、Docker のコアとなるネットワークモデルの謎を解き明かし、Bridge(ブリッジ)Host(ホスト)、および Overlay(オーバーレイ) ネットワークに焦点を当てて解説します。単一マシンの開発環境 (Development Environment) であっても、複雑なマルチノード (Multi-node) の本番環境 (Production Environment) であっても、これら3つのモードは不可欠なものです。

1. Docker ネットワークのコアコンセプト

Docker は、さまざまなユースケースやデプロイメントシナリオを満たすために、いくつかの異なる「ネットワークドライバ (Network Drivers)」を提供しています。各ドライバはコンテナ通信に独自の方法を提供し、隔離性、パフォーマンス、および複雑さの面でそれぞれトレードオフ (Trade-off) があります。これらの基本的なドライバを理解することは、コンテナ化アプリケーションの相互作用の方法をマスターするためのコアとなります。

1.1 Bridge ネットワーク (ブリッジモード)

Bridge ネットワークは、Docker コンテナで最も一般的であり、デフォルトのネットワークドライバでもあります。Docker をインストールすると、システムは自動的に bridge という名前のデフォルトブリッジネットワークを作成します。他のネットワークドライバを指定しない限り、新しく作成されたコンテナはすべて自動的にこのデフォルトネットワークに接続されます。

原理の解析:
Bridge ネットワークは、ホストマシン上の仮想スイッチ (Virtual Switch) のようなものです。Docker は仮想ブリッジ(Linux では通常 docker0 と呼ばれます)を作成し、これがソフトウェアブリッジとして機能し、そこに接続されたコンテナ間のネットワークトラフィック (Traffic) をルーティング (Routing) します。このブリッジネットワーク上の各コンテナは、このブリッジのサブネット (Subnet) 内で独自のプライベート IP アドレスを取得します。

同一のブリッジネットワーク上のコンテナは、IP アドレスまたはコンテナ名を使用して相互に通信できます(Docker は、カスタム Bridge ネットワークにおいて名前解決のための組み込み DNS サービスを提供しています)。外部の世界と通信するため(または外部トラフィックをコンテナにアクセスさせるため)に、Docker はネットワークアドレス変換 (NAT) とポートマッピング (Port Mapping) を使用します(前の章「ポートのエクスポーズ」で学んだ通りです)。

どのように動作するか:

  1. Docker の起動時、ホストマシン上に仮想ブリッジインターフェース (Interface)(例:docker0)が作成されます。
  2. bridge ネットワーク上でコンテナが起動するたびに、Docker は仮想イーサネットインターフェースのペア (veth pair) を作成します。このペアの一方の端はコンテナのネットワークネームスペース (Network Namespace) に接続され(コンテナ内では eth0 として見えます)、もう一方の端はホストマシンの docker0 ブリッジに接続されます。
  3. Docker は、ブリッジのサブネットからコンテナの eth0 インターフェースに IP アドレスを割り当てます。
  4. Docker はホストマシン上で iptables ルールを設定し、コンテナからの発信接続に対して NAT を有効にし、外部からの着信ポートマッピングを処理します。

リアルなビジネスシナリオ:

  • シナリオ 1:Web アプリケーションアーキテクチャ
    • 要件: フロントエンドの web_app コンテナ(Nginx が静的ファイルを提供し、リバースプロキシとして機能するなど)とバックエンドの api_server コンテナ(Python Flask が動的データを提供するなど)があり、これらが相互に通信する必要があります。
    • 実装: デフォルトでは、どちらも bridge ネットワークに接続されます。web_app は 80 番ポートをホストマシンにエクスポーズし、api_server は内部で 5000 番ポートを保持するだけで済みます。その後、web_app は内部 IP またはコンテナ名を使用してリクエストを api_server にプロキシ (Proxy) できます。
    • 利点: コンテナとホストマシン間の隔離を提供し、バックエンドを直接パブリックネットワークにエクスポーズすることなく、コンテナが安全に通信できるようにします。
  • シナリオ 2:開発環境の構築
    • 要件: 開発者はコード開発に合わせて、ローカルで特定バージョンの PostgreSQL データベースと Redis キャッシュを実行する必要があります。
    • 実装: postgresredis コンテナを起動します。これらはデフォルトで bridge ネットワーク上にあります。ホストマシン上のローカルコードは、ポートマッピング(例:localhost:5432)を通じてこれらに接続します。開発中のアプリケーションもコンテナとしてパッケージ化されている場合、それらは直接 postgres:5432 を介して内部接続を行うことができます。
    • 利点: ローカルマシンの他のサービスから隔離しつつ、すべての開発者環境の一貫性を確保します。

コード例 (デフォルトの Bridge ネットワークの調査):

# すべての Docker ネットワークのリストを表示
docker network ls

# 期待される出力には 'bridge' が含まれます
# NETWORK ID     NAME      DRIVER    SCOPE
# ...
# d6c9d5f0b12a   bridge    bridge    local
# ...

# デフォルトの 'bridge' ネットワークをインスペクト (Inspect) し、その設定と接続されているコンテナを確認
docker network inspect bridge

# 出力例 (簡潔にするためにトリミング済み、実際の出力は非常に長いです):
# [
#     {
#         "Name": "bridge",
#         "IPAM": {
#             "Config": [
#                 {
#                     "Subnet": "172.17.0.0/16", # Bridge のデフォルトサブネット
#                     "Gateway": "172.17.0.1"    # Bridge のゲートウェイ IP
#                 }
#             ]
#         },
#         "Containers": {}, # コンテナを実行する前はここは空です
#         "Options": {
#             "com.docker.network.bridge.name": "docker0" # 実際のホストマシンブリッジインターフェース名
#         }
#     }
# ]

# ここで、シンプルな Nginx コンテナを実行し、ホストマシンの 8080 をコンテナの 80 にマッピングします
docker run -d -p 8080:80 --name mynginx nginx

# 再度 'bridge' ネットワークをインスペクトし、マウントされたコンテナを確認します
docker network inspect bridge

# これで "Containers" の下に 'mynginx' コンテナが表示されます:
# ...
#         "Containers": {
#             "a1b2c...": {
#                 "Name": "mynginx",
#                 "IPv4Address": "172.17.0.2/16", # Nginx コンテナに割り当てられた内部 IP
#             }
#         },
# ...

1.2 Host ネットワーク (ホストモード)

名前が示すように、Host ネットワークドライバはコンテナと Docker ホストマシン間のネットワークの隔離を取り除きます。host ネットワークモードを使用すると、コンテナはホストマシンのネットワークネームスペースを直接共有します。これは、コンテナがホストマシンの IP アドレスとネットワークインターフェースを直接使用することを意味します。

原理の解析:
host ネットワークドライバで設定されたコンテナは、仮想インターフェースを持つ独自のプライベートネットワークスタック (Network Stack) を持ちません。代わりに、ホストマシンのインターフェース、IP アドレス、およびポート範囲を直接使用します。これは事実上、コンテナ内で実行されているサービスが、ホストマシンの IP とポートを通じて直接アクセス可能であり、NAT やポートマッピングを一切必要としないことを意味します。

どのように動作するか:

  • Docker は基本的に、ホストマシンのネットワークスタックを「借りる」ようにコンテナに指示します。
  • コンテナ内の localhost は、ホストマシンの localhost そのものです。
  • コンテナ内のアプリケーションが開くポートはすべて、ホストマシンのインターフェースに直接バインド (Bind) されます。ホストマシン上ですでに Web サーバーが 80 番ポートを占有しており、host モードを使用するコンテナも 80 番ポートをリスンしようとすると、ポートの競合 (Conflict) が発生します。

リアルなビジネスシナリオ:

  • シナリオ 1:高性能ネットワークアプリケーション
    • 要件: 特定のアプリケーションは、極めて低レイテンシ (Latency) のネットワーク通信を必要とするか、膨大なネットワークトラフィックを処理する必要があります(専用のネットワーク監視ツールや高性能プロキシなど)。
    • 実装: --network host を使用してコンテナを実行することで、Docker ネットワークスタックの仮想ブリッジのオーバーヘッドをバイパスし、物理ネットワークカードと直接通信できるようになり、ベアメタル (Bare Metal) に近いネットワークパフォーマンスを得ることができます。
  • シナリオ 2:ホストマシンにバインドされた特定のサービスへのアクセス
    • 要件: コンテナから、ホストマシンの 127.0.0.1 でのみリスンしているローカルサービス(ローカルのインメモリ Redis など)に接続したいとします。
    • 実装: host モードを使用すると、コンテナの localhost がホストマシンの localhost になるため、127.0.0.1:ポート番号 に直接接続できます。

コード例 (Host モードでのコンテナの実行):

# host ネットワークを使用して Nginx コンテナを実行します。
# コンテナの 80 番ポートはホストマシンの 80 番ポートそのものであるため、-p (ポートマッピング) は使用していません。
docker run -d --network host --name mynginx-host nginx

# ホストマシンの 80 番ポートがすでに占有されている場合、このコマンドは "port already in use" エラーをスローします。
# 実行に成功した場合、ホストマシンの IP または localhost 経由で直接 Nginx にアクセスできます。
# 例: curl http://localhost/

# コンテナ内からネットワーク設定をチェックできます:
docker run --network host --rm alpine/git ip a

# 期待される出力には、コンテナ自身の仮想ネットワークカードではなく、
# ホストマシンのすべての物理ネットワークカード (eth0、wlan0 など) と IP アドレスが直接リストされます。

重要なヒント(Mac/Windows ユーザー向け): ネイティブの Linux システム上では、--network host はコンテナがホストマシンのネットワークを完全に利用することを意味します。しかし、Docker Desktop (macOS/Windows) では、Docker は軽量の仮想マシン (VM) 内で実行されます。この場合、--network host は、コンテナが Mac/Windows ホストマシン自体の物理ネットワークではなく、Docker Desktop VM のネットワークスタックを共有することを意味します。これは非常に重要な違いです。

1.3 Overlay ネットワーク (オーバーレイモード)

Overlay ネットワークは、複数の Docker ホストマシンにまたがって実行される分散アプリケーション専用に設計されています。これにより、異なる Docker デーモン上で実行されているコンテナが、あたかも同じローカルエリアネットワーク (LAN) 上にいるかのようにシームレスに通信できるようになります。Overlay ネットワークは主に、Docker Swarm モードや Kubernetes などのコンテナオーケストレーション (Orchestration) ツールで使用されます。

原理の解析:
単一のホストマシンに制限される bridge ネットワークとは異なり、overlay ネットワークは複数のホストマシンにまたがります。これは、VXLAN (Virtual eXtensible Local Area Network) などのテクノロジーを使用してコンテナトラフィックをカプセル化 (Encapsulation) することで実現されます。ホスト A 上のコンテナがホスト B 上のコンテナと通信したい場合、overlay ネットワークドライバはトラフィックのルーティングとカプセル化を処理し、コンテナに直接接続されていると認識させます。

どのように動作するか(概念レベル):

  • 分散コントロールプレーン (Control Plane): マルチホスト環境(Docker Swarm など)において、分散キーバリューストア (Key-Value Store) がすべてのノード (Node) 間のネットワーク設定と状態の管理を担当します。
  • VXLAN トンネル (Tunnel): 異なるホストマシン上のコンテナが通信する場合、そのトラフィックは VXLAN パケット (Packet) にパッケージ化されます。これらのパケットはその後、基盤となる物理ネットワークを通じて Docker ホストマシン間を転送されます。
  • カプセル化と非カプセル化 (Decapsulation): 各 Docker ホストは VXLAN トンネルエンドポイントとして機能します。送信されるトラフィックを VXLAN パケットにカプセル化して宛先ホストに送信し、宛先ホストはそれを受信して非カプセル化し、元のトラフィックを宛先コンテナに配信します。

リアルなビジネスシナリオ:

  • シナリオ:マイクロサービス (Microservices) アーキテクチャとクラスタのスケーリング (Scaling)
    • 要件: 会社に複数のマイクロサービス(認証サービス、製品カタログサービス、注文処理サービスなど)があるとします。高可用性 (High Availability) を実現するため、これらは複数の物理サーバーに分散してデプロイされています。
    • 実装: overlay ネットワークを作成し、すべてのサービスをそこに接続します。ホスト A 上の注文サービスは名前で直接ホスト B 上の製品サービスを呼び出すことができ、overlay ネットワークがシームレスな通信を保証します。
    • 利点: 分散マイクロサービスがクラスタ全体で透過的に通信できるようになり、基盤となる物理ネットワークのトポロジ (Topology) を完全に抽象化します。

2. 実践演習とデモンストレーション

コンテナを実行して、これらのネットワークの動作を実際に観察してみましょう。

2.1 デモ:デフォルト Bridge ネットワークの通信

デフォルトのブリッジネットワーク上で2つのコンテナを実行し、それらがどのように通信するかを確認します。

1. 実行を維持する alpine コンテナ (送信側) を実行します:

docker run -d --name alpine-sender alpine sleep 3600

2. その IP アドレスを検索します:

docker network inspect bridge

"Containers" セクションで alpine-sender の IP を見つけます(ここでは 172.17.0.2 と仮定します)。

3. 2つ目のコンテナ (受信側) を実行し、1つ目のコンテナに Ping を送信します:

# 172.17.0.2 を実際に調べた IP に置き換えてください
docker run --rm --name alpine-receiver alpine ping -c 3 172.17.0.2

成功した ping 応答が表示されるはずです。これは、同一 bridge 上のコンテナが IP を介して相互に通信できることを証明しています。

4. クリーンアップ:

docker rm -f alpine-sender alpine-receiver

2.2 デモ:Host ネットワークの動作

host ネットワークを使用して Web サーバーを実行し、その直接のアクセス可能性を検証します。

1. ローカルマシンの 80 番ポートが空いていることを確認します。

2. host モードを使用して Nginx を実行します:

docker run -d --network host --name mynginx-host-demo nginx

-p パラメータがないことに注意してください)

3. アクセスの検証: ブラウザを開いて http://localhost にアクセスすると、Nginx のウェルカムページが表示されるはずです。

4. クリーンアップ:docker rm -f mynginx-host-demo

2.3 概念の推演:Overlay ネットワークはどのように機能するか

2つの Docker ホストがあると仮定します:node1node2

  • node1frontend(フロントエンドコンテナ)を実行します
  • node2backend(バックエンドコンテナ)を実行します

Overlay がない場合: frontend は内部ネットワーク IP を介して別のマシンの backend に直接アクセスすることはできません。

Overlay がある場合 (Swarm クラスタ内):

  1. overlay ネットワークを作成します:docker network create -d overlay my-overlay
  2. 両方のサービスをこのネットワークに接続します。
  3. node1 上の frontend は、直接 http://backend:8080 にリクエストを送信できます。Docker 組み込み DNS が backend を解決し、VXLAN トンネルがリクエストを安全に node2 に届けます。backend を3つのインスタンス (Instance) にスケールアウトした場合でも、ネットワークは自動的にロードバランシング (Load Balancing) を処理します!