dockerは「1コンテナ=1サービス」で動作させる前提で設計されているため、一般的なWebサービスを構築したいケースでは複数のコンテナをネットワーク接続することが事実上必須になります。
たとえばRuby on Railsのアプリの場合、標準的な機能範囲でもrubyコンテナに加えて、MySQL/PostgreSQL、nginx/Apacheの計3つのコンテナが必要になります。
これらのサービスを1コンテナに詰め込もうとするとトリッキーなイメージを構築する必要があり、のちに何かを追加したくなった際に行き詰まる可能性があります。
基本的には開発用PCの環境を構築する段階からdockerのネットワーク構成に対応しておくことが重要です。
dockerネットワーク設定の流れ
dockerのネットワーク設定のおおまかなステップは、以下の3段階に分かれます。
- ネットワークを作成する
- 任意の名前をつけることができ、この同一ネットワーク内でコンテナ群が相互に名前解決できます
- コンテナをネットワークに参加させる
- コンテナ起動後の変更も可能です
- クライアントサービスの接続設定を行う
- コンテナ名を指定するだけで接続できます
dockerのネットワーク機能はDNSに相当する名前解決を提供していて、docker run --name
オプションで指定するコンテナ名がホスト名となります。
(起動時にコンテナ名を指定しなかった場合、dockerが自動でコンテナ名をつけます。これはdocker ps
で確認できます)
適切に設定されたネットワーク内では、たとえばMySQLクライアントの設定ファイルの接続ホストとして mysql_container:3306 のように指定することでMySQLコンテナに接続できます。
ネットワーク作成
dockerのネットワーク機能には、bridgeやoverlayといったいくつかのモードがあります。
1台のハード上で複数コンテナを動かすシンプルな構成の場合は、デフォルトのbridgeで簡単に構成できます。
docker network create my_docker_network
のように任意のネットワーク名を指定して作成するだけです。
このネットワーク作成前には、docker0というデフォルトのネットワークが用意されていますが、docker0は後方互換性のため、コンテナ間を明示的にlinkしないと名前解決しない仕様になっています。
作成済のネットワークの一覧はdocker network ls
コマンドで確認できます。
コンテナをネットワークに追加する
ネットワークへの参加は起動時であればdocker run -it <b>--net=my_docker_network</b>
、すでに起動しているコンテナをネットワーク接続するには<b>docker network connect</b> my_docker_network mysql_container
で行ないます。
ネットワーク内にあるコンテナの一覧はdocker network inspect my_docker_network
で確認できます。
また、docker network
の詳細な使い方や複数ハード間のネットワーク構成などについては、
Work with network commandsに解説があります。
docker-composeのネットワーク設定
自分で定義したネットワークにdocker-composeでコンテナを追加するには、YAML構成ファイルに以下のようにnetworksセクションを指定することで対応できます。
networks:
default:
external:
name: my_docker_network
ここで指定するネットワークは、コンテナ起動前にdocker network create
しておく必要があります。内部ネットワークが存在しない状態でdocker-compose up
すると、以下のようなエラーに遭遇します。
ERROR: Network dockernet declared as external, but could not be found. Please create the network manually using `docker network create my_docker_network` and try again.
Docker Composeのネットワーク設定については、 Networking in Composeに詳細な解説があります。
接続ポートに注意
dockerにはホストのポートにマッピングするポート変換機能があり、docker run -p 8080:80
のように「外部ポート:内部ポート」を指定して起動するケースがあります。
今回のようにユーザーが定義したネットワーク内で接続する場合のポート番号は内部ポート番号(各サーバープロセスがLISTENしているポートそのもの)を利用します。
他のコンテナから外部ポート番号に接続しようとするとconnection refusedのようなエラーになります。
接続できない場合の切り分け
コンテナ間の接続がエラーになる場合、
docker execでコンテナ内のシェルに入り、ping some_container
で疎通確認する方法が手軽です。
pingで応答がとれている場合はアプリケーションの設定ファイルにミスがある可能性が高く、pingが通らない場合にはdockerネットワークの構成に失敗しています。
また、ネットワーク構成が設定済みであっても、コンテナを停止するとネットワーク内からホスト設定が削除される挙動になっています。このため、停止中のコンテナに対してはpingが不通となります。
たとえばnginxはサービス起動時に転送先のホストへアクセスし、利用できない場合は”[emerg] 1#1: host not found in upstream”といったエラーを出力して異常終了します。
nginxに登録したコンテナをすべて起動しておく必要があるため、多数のサービスを単一のnginxに統合する際には注意が必要です。
nginxのupstream先のサービスをすべて起動させているにも関わらず、同様のエラーが出る場合は起動タイミングの問題の可能性もあります。
前提のサービスよりnginxの方が先に起動してしまうと、そのサービスが存在していないのと同じ挙動となります。
このようなケースに対応するため、docker-composeの設定ファイルではdepends_onディレクティブに前提サービスを列記することで、それらが起動した状態を保証できます。
Chuma Takahiro