dockerのネットワーク設定

dockerは「1コンテナ=1サービス」で動作させる前提で設計されているため、一般的なWebサービスを構築したいケースでは複数のコンテナをネットワーク接続することが事実上必須になります。

たとえばRuby on Railsのアプリの場合、標準的な機能範囲でもrubyコンテナに加えて、MySQL/PostgreSQL、nginx/Apacheの計3つのコンテナが必要になります。

これらのサービスを1コンテナに詰め込もうとするとトリッキーなイメージを構築する必要があり、のちに何かを追加したくなった際に行き詰まる可能性があります。

基本的には開発用PCの環境を構築する段階からdockerのネットワーク構成に対応しておくことが重要です。

dockerネットワーク設定の流れ

dockerのネットワーク設定のおおまかなステップは、以下の3段階に分かれます。

  1. ネットワークを作成する
    • 任意の名前をつけることができ、この同一ネットワーク内でコンテナ群が相互に名前解決できます
  2. コンテナをネットワークに参加させる
    • コンテナ起動後の変更も可能です
  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