Docker ネットワーク管理
コンテナ化 (Containerized) アプリケーションの世界において、単一のコンテナ (Container) は多くの場合、巨大なシステムにおける一つのコンポーネント (Component) に過ぎず、完全な機能を発揮するには相互に通信 (Communicate) する必要があります。前の章で、外部からのコンテナへのアクセスを許可する「ポートのエクスポーズ」について探求しましたが、真に堅牢で安全なマルチコンテナアプリケーションを実現するためには、より高度な内部通信メカニズムが必要です。
まさにここで、Docker の強力なネットワーク機能が真価を発揮します。これにより、サービスに対して隔離 (Isolate) され、柔軟かつ効率的な通信チャネル (Channel) を定義できます。これらのネットワークを作成および管理する方法を理解することは、複雑な分散アプリケーション (Distributed Application) をオーケストレーション (Orchestrate) するための基礎となります。これは、内部ポートを不必要にホストマシン (Host) や外部ネットワークに公開することなく、サービスが互いに確実に見つけ合い (Discovery)、相互作用 (Interact) することを保証します。本章では、Docker ネットワーク管理の完全なマスターを目指します。
1. Docker のネットワーク管理システムをマスターする
Docker は複雑なネットワークスタック (Network Stack) を提供しており、コンテナ間、コンテナとホストマシン間、およびコンテナと外部世界との間の通信を可能にします。コンテナは公開されたポートを介して通信できますが、専用ネットワークを介してこれらの接続を管理することで、マルチサービスアプリケーションに優れた隔離性、優れた名前解決 (DNS)、および高い柔軟性を提供します。
1.1 振り返り:Docker のデフォルトネットワークドライバ
カスタムネットワークの作成を深く学ぶ前に、前の章で紹介した Docker のコアとなるネットワークドライバ (Network Driver) を簡単に振り返りましょう。これらのドライバは、ネットワークの基盤となる実装方法と、それに接続されるコンテナの振る舞いを決定します:
- Bridge (ブリッジネットワーク): 他のネットワークが指定されていない場合、新しく作成されたコンテナのデフォルトネットワークになります。同じブリッジネットワーク上のコンテナは、IP アドレス、またはより便利にコンテナ名(Docker 組み込みの DNS サービスのおかげです)を介して相互に通信できます。ホストマシンはポートマッピング (Port Mapping) を通じてブリッジネットワーク上のコンテナにアクセスでき、コンテナも外部世界にアクセスできます。デフォルトの bridge ネットワークと比較して、ユーザー定義のブリッジネットワーク (User-defined bridge) は、より優れた隔離性と DNS 解決機能を提供します。
- Host (ホストネットワーク): host ネットワークを使用するコンテナは、ホストマシンのネットワークネームスペース (Network Namespace) を直接共有します。これは、コンテナのネットワークスタックがホストマシンと完全に一致し、独自の独立した IP を取得するのではなく、ホストマシンのサービスとポートを直接使用することを意味します。ネットワークアドレス変換 (NAT) を回避することで高いパフォーマンスをもたらしますが、ネットワークの隔離度は低下します。
- None (ネットワークなし): none ネットワークに接続されたコンテナは完全に隔離されます。ローカルループバックインターフェース (loopback) を除き、設定されたネットワークインターフェースはありません。手動でネットワークカード (NIC) を追加しない限り、他のコンテナや外部世界と通信することはできません。これは、ネットワーク接続を必要としない純粋な計算タスクや、極めて高度なカスタムネットワーク設定に適用されます。
- Overlay (オーバーレイネットワーク): Docker Swarm(コンテナオーケストレーションツール)専用に設計されています。Overlay ネットワークを使用すると、異なる Docker ホストマシン上で実行されているコンテナが、あたかも同じマシン上にいるかのようにシームレスに通信できます。
1.2 docker network コマンドセット
Docker は、ネットワークを管理するための専用コマンドラインインターフェース docker network を提供しています。このコマンドファミリーを使用すると、ネットワークのリスト表示 (list)、検査 (inspect)、作成 (create)、接続 (connect)、切断 (disconnect)、および削除 (remove) が可能になり、コンテナの通信アーキテクチャ (Architecture) に対するきめ細かい制御権が与えられます。
1.3 Docker ネットワークの表示と検査
新しいネットワークを作成する前に、Docker デーモン (Daemon) にすでにどのネットワークが存在しているかを確認することは非常に役立ちます。
既存のネットワークの表示
docker network ls コマンドを使用して、Docker デーモンが管理するすべてのネットワークのリストを表示できます。
docker network lsこのコマンドは、以下のようなテーブルを出力します:
NETWORK ID NAME DRIVER SCOPE
178f5a11d044 bridge bridge local
095388c3a7f8 host host local
f05ee0d98453 none null localNETWORK ID: ネットワークの一意の識別子。NAME: 人間が読めるネットワーク名。DRIVER: 使用されているネットワークドライバ(例:bridge,host,null)。SCOPE: ネットワークがローカルのlocal(現在の Docker ホストマシンでのみ利用可能)か、クラスターレベルのswarm(Swarm クラスターの複数ホスト間で利用可能)かを示します。
ネットワークの詳細の検査
特定のネットワークの詳細情報(サブネット (Subnet)、ゲートウェイ (Gateway)、現在接続されているコンテナなど)を取得するには、docker network inspect コマンドを使用します。
docker network inspect bridgeこのコマンドは、包括的な詳細情報を含む JSON 形式の出力を返します。例えば、デフォルトの bridge ネットワークの場合、その IPAM(IP アドレス管理)設定、ゲートウェイ、および接続されているすべてのコンテナに関する情報が表示されます。
[
{
"Name": "bridge",
"Id": "178f5a11d044...",
"Created": "2023-10-26T10:00:00.000000000Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
],
"Options": null
},
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.name": "docker0"
},
"Labels": {}
}
]その中で Containers セクションは特に有用であり、このネットワークに接続されているすべてのコンテナの ID、名称、IP アドレス、および MAC アドレスが表示されます。これにより、接続のトラブルシューティングやネットワーク設定の検証を迅速に行うことができます。
2. カスタムブリッジネットワークの作成
Docker デフォルトの bridge ネットワークはシンプルなシナリオには対応できますが、実際の開発においては、アプリケーションのために「カスタムブリッジネットワーク」を作成することを強く推奨します。カスタムブリッジネットワークには以下の大きな利点があります:
- 自動 DNS 解決: カスタムネットワーク上のコンテナは、(動的に変化する IP アドレスを記憶することなく)名前を介して互いに直接解決できます(例:直接
ping my-db-containerを実行)。デフォルトのbridgeネットワークは、このコンテナ名から IP への自動解決機能を提供していません。 - 優れた隔離性: 各カスタムネットワークは隔離されたネットワークネームスペースです。これにより、ネットワーク A 上のコンテナは、明示的に接続しない限り、ネットワーク B 上のコンテナと直接通信することが絶対にできなくなります。
- ポータビリティ (Portability): 特定のアプリケーションのために定義されたネットワークは、異なる環境で簡単に再作成でき、デプロイの一貫性が向上します。
2.1 基本的なネットワークの作成
新しいカスタムブリッジネットワークを作成するには、docker network create コマンドに続けてネットワーク名を指定します。
docker network create my-app-networkこのコマンドは新しいブリッジネットワークを作成し、Docker は自動的にランダムなサブネットとゲートウェイを割り当てます。
2.2 カスタムサブネットとゲートウェイ
IP アドレスの範囲をより正確に制御する必要がある場合は、--subnet および --gateway パラメータを使用して明示的に指定できます。これは、Docker ネットワークを既存の企業インフラストラクチャ (Infrastructure) と統合する必要がある場合や、IP の競合を回避したい場合に非常に便利です。
docker network create --driver bridge --subnet 172.20.0.0/16 --gateway 172.20.0.1 custom-app-network--driver bridge: bridge ドライバの使用を明示的に指定します。これはdocker network createのデフォルトの動作ですが、記述することでより明確になります。--subnet 172.20.0.0/16: このネットワークの IP アドレス範囲を定義します。このネットワークに接続されたすべてのコンテナは、この範囲内の IP を取得します。--gateway 172.20.0.1: このネットワークのゲートウェイ IP アドレスを設定します。
リアルなビジネスシナリオ: 異なるマイクロサービスを開発している複数の開発チームがあるとします。IP の競合を防ぎ、境界を明確にするために、各チームは一意のサブネットを持つ専用の Docker ネットワークを作成できます(例えば、チーム A は 172.20.0.0/16 を使用し、チーム B は 172.21.0.0/16 を使用する)。これは、Docker コンテナが特定の IP ホワイトリスト (Whitelist) で設定された外部リソースにアクセスする必要がある場合に特に有用です。
想定シナリオ: ある会社には、特定の IP セグメント(例:10.0.1.0/24)で稼働する古いレガシー (Legacy) システムがあり、このネットワークセグメントを簡単に変更することはできません。ここで、彼らは新しいアプリケーションをコンテナ化したいと考えており、この新しいアプリケーションは古いシステムと通信する必要があります。(古いシステムのネットワークセグメントを避けた)カスタムサブネットの Docker ネットワークを作成することで、コンテナがホストネットワークを介して古いシステムにアクセスしようとしたときに IP ルーティングの競合が発生しないことを保証できます。
3. コンテナをネットワークに接続する
コンテナを作成する瞬間にネットワークに接続することも、コンテナの実行後にいつでも接続することもできます。
3.1 コンテナ作成時の接続
最も一般的な方法は、docker run コマンドの実行時に --network パラメータを使用することです。
docker run -d --name my-web-server --network my-app-network nginx:latestこの例では、my-web-server コンテナは起動後すぐに my-app-network ネットワークに接続され、そのネットワークのサブネットから IP アドレスを取得します。
3.2 既存のコンテナの接続
実行中のコンテナがあり、当初の起動時にカスタムネットワークが指定されていなかった場合(例えばデフォルトの bridge にぶら下がっている場合)、docker network connect を使用して新しいネットワークに接続できます。
# デフォルトネットワークにぶら下がるコンテナを起動する
docker run -d --name legacy-app alpine/git
# それをカスタムネットワークに接続する
docker network connect my-app-network legacy-appこれで、legacy-app コンテナはデフォルトの bridge ネットワークと my-app-network ネットワークの両方に同時に接続されました。1つのコンテナが複数のネットワークに同時に接続できることは、アプリケーションの異なるレイヤーに「橋を架ける」のに非常に適しています(例えば、ゲートウェイとして機能するコンテナは、外部ネットワークに公開されたネットワークと内部のバックエンドサービス専用のネットワークの両方に同時に接続する必要があるかもしれません)。
4. コンテナの切断とネットワークの削除
いつでもネットワークに接続できるのと同様に、不要になった場合は接続を切断し、ネットワークをクリーンアップ (Cleanup) することもできます。
4.1 コンテナの切断
コンテナを特定のネットワークから外すには、docker network disconnect コマンドを使用します。
docker network disconnect my-app-network my-web-serverこれにより、my-web-server が my-app-network から切り離されます。そのコンテナが他のネットワークにも接続している場合、それらの接続は影響を受けません。
4.2 カスタムネットワークの削除
ユーザー定義のネットワークを削除するには、docker network rm コマンドを使用します。
docker network rm my-app-network重要な注意: ネットワークにまだコンテナが1つでも接続されている場合、そのネットワークを削除することはできません。削除を試みる前に、すべてのコンテナの接続を切断する(またはそれらのコンテナを削除する)必要があります。強制的に削除しようとすると、Docker はエラーを返し、先にそれらを切断するように促します。
5. 総合実践演習
一般的な実践シナリオを通じて全プロセスを体験してみましょう:Web フロントエンド(Nginx)とバックエンドサービス(シンプルなメッセージを返す)を含む、極めてシンプルな Web アプリケーションを構築します。カスタムネットワークを使用して、それら間の内部通信を実現します。
シナリオの説明:バックエンドを持つシンプルな Web サービス
私たちのアプリケーションは2つの部分で構成されています:
- バックエンドサービス (Backend):
nginxdemos/helloイメージを実行するコンテナで、シンプルな "Hello from Nginx" ページを出力します。これには内部でのみアクセスします。 - フロントエンド Web サーバー (Frontend): リバースプロキシ (Reverse Proxy) として機能し、リクエストをバックエンドサービスに転送する Nginx コンテナです。
ステップ 1:カスタムネットワークを作成する
まず、私たちのアプリケーション専用のブリッジネットワークを作成します。
# 'web-tier-network' という名前の新しいブリッジネットワークを作成
docker network create web-tier-network作成が成功したか確認します:
docker network lsリストの中に web-tier-network が見えるはずです。
ステップ 2:バックエンドサービスを実行する
次に、バックエンドサービスを実行し、先ほど作成したネットワークに接続します。このバックエンドのポートはホストマシンに一切エクスポーズしないことに注意してください。フロントエンドからアクセスできれば十分だからです。
# バックエンドサービスを実行し、'web-tier-network' に接続する
# '--name' で指定された名前は、フロントエンドが DNS 解決を行うために使用されます
docker run -d --name backend-service --network web-tier-network nginxdemos/helloコンテナが実行され、正しく接続されているか検証します:
docker ps
docker network inspect web-tier-networkinspect 出力の Containers ブロック内で、backend-service に web-tier-network 内の IP が割り当てられていることが確認できるはずです。
ステップ 3:フロントエンド Web サーバーを実行する (Nginx リバースプロキシ)
続いて、フロントエンドの Nginx を起動します。これをリバースプロキシとして設定し、80 番ポートへのアクセスリクエストを内部ネットワークで backend-service に転送します。同時に、このフロントエンド Nginx の 80 番ポートをホストマシンの 8080 番ポートにエクスポーズする必要があります。
Nginx を設定するために、現在のディレクトリに nginx.conf という名前の設定ファイルを作成し、以下の内容を記述します:
# nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
# コンテナ名 'backend-service' を使用して内部 DNS 解決を行う
# Docker 内部の DNS は 'backend-service' を 'web-tier-network' 上の IP に解決します
proxy_pass http://backend-service:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}これで、この設定ファイルをマウント (Mount) し、ネットワークに接続して、フロントエンドの Nginx コンテナを実行します:
# フロントエンド Nginx を実行し、記述した設定をマウントし、
# 'web-tier-network' に接続し、ホストマシンの 8080 をそれにマッピングする
docker run -d --name frontend-nginx --network web-tier-network \
-p 8080:80 \
-v "$(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro" \
nginx:latestステップ 4:アプリケーションをテストする
ブラウザを開くか、curl を使用してフロントエンドが公開しているポートにアクセスします:
curl http://localhost:8080backend-service からの出力コンテンツが表示されるはずです:
Hostname: <コンテナIDの一部>
IP: 127.0.0.1
...この実践演習は完璧に証明しています:web-tier-network ネットワーク上のフロントエンド Nginx は、バックエンドのコンテナ名のみを介して、見事にバックエンドサービスを見つけ出し、コンテンツを取得しました。バックエンドの動的な IP を知る必要も、バックエンドのポートを外部にエクスポーズする必要もありませんでした。
ステップ 5:環境のクリーンアップ
使い終わったらリソースをクリーンアップする良い習慣を身につけましょう。
# コンテナの停止と削除
docker stop frontend-nginx backend-service
docker rm frontend-nginx backend-service
# ネットワークの削除
# すべてのコンテナが切断/削除された場合にのみ、このステップは成功します
docker network rm web-tier-network