はじめに
異なる二つのサービス(例えば、サービスAとサービスB)でそれぞれdocker-compose.yml
ファイルがあり、それぞれのサービスでapp
とdb
があるシナリオについて説明する。
ここでの目標は、サービスAのapp
がサービスBのdb
にアクセスできるようにすること。この例では、両方のサービスがmy-custom-network
という共通のネットワークを使用することを想定している。
既存のDockerネットワークの作成
まず、手動で既存のDockerネットワークを作成する必要がある。以下のコマンドをターミナルで使用して、my-custom-network
という名前のネットワークを作成する。
docker network create my-custom-network
このネットワークは、異なるdocker-compose.yml
ファイル間で共有されることを意図している。
docker-compose.yml
ファイルを使用してサービスを立ち上げる前に、ターミナルで個別に実行する必要がある。これにより、異なるdocker-compose.yml
ファイルから起動されるコンテナが同じネットワーク内で互いに通信できるようになる。
docker-compose.ymlでのexternal: true設定の利用
次に、external: true
設定を含むdocker-compose.yml
ファイルを作成する。
この設定は、サービスが既に作成されたmy-custom-network
に接続するために使用される。
分かりやすさのために、今回サービスAはデータベースとしてmysql(デフォルトポート3306):サービスBはデータベースとしてPostgreSQL(デフォルトポート5432)を使用するものとする。
サービスAのdocker-compose.yml
version: '3'
services:
app-a:
container_name: app_a_container
image: app-a:latest
ports:
- "8000:8000"
db-a:
container_name: db_a_container
image: mysql:8.0
environment:
MYSQL_DATABASE: db_a_database
MYSQL_ROOT_PASSWORD: password
ports:
- "33060:3306"
networks:
my-custom-net:
external: true
name: my-custom-network
サービスBのdocker-compose.yml
version: '3'
services:
app-b:
container_name: app_b_container
image: app-b:latest
ports:
- "8100:8100"
db-b:
container_name: db_b_container
image: postgres:latest
environment:
POSTGRES_DB: db_b_database
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "54320:5432"
networks:
my-custom-net:
external: true
name: my-custom-network
docker psの出力状態の例
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
<id> app-a:latest "docker-entrypoint..." 12 seconds ago Up 11 seconds 0.0.0.0:8000->8000/tcp app_a_container
<id> mysql:8.0 "docker-entrypoint..." 13 seconds ago Up 12 seconds 0.0.0.0:33060->3306/tcp db_a_container
<id> app-b:latest "docker-entrypoint..." 10 seconds ago Up 9 seconds 0.0.0.0:8100->8100/tcp app_b_container
<id> postgres:latest "docker-entrypoint..." 11 seconds ago Up 10 seconds 0.0.0.0:54320->5432/tcp db_b_container
アプリケーションにアクセスするURL
- サービスAのアプリケーション:
http://localhost:8000
- サービスBのアプリケーション:
http://localhost:8100
これらのURLは、docker-compose.yml
ファイルで指定されたポートマッピングに基づいている。ホストマシンのポート(左側)がコンテナ内のポート(右側)にフォワードされている。
Dockerポートマッピングに関しては、以前こちらの記事で記載。
サービスAからサービスBのデータベースへアクセスするには?
サービスAのアプリケーションからサービスBのpostgreSQLデータベースに接続するには、以下の接続情報を使用する
- ホスト名:
db_b_container
(サービスBのデータベースコンテナ名) - ポート番号:
5432
(サービスBがリッスンしている内部ポート:postgreSQLのデフォルトポート) - データベース名:
db_b_database
- ユーザー名:
user
(または環境変数で設定されたpostgreSQLのユーザー名) - パスワード:
password
(環境変数で設定されたパスワード)
コンテナ間での通信には、外部ネットワークmy-custom-network
を介しているため、db_b_container
の名前を直接ホスト名として使用できる。そして、コンテナ間通信ではホストのポートマッピングは関係なく、内部ポートを直接使用する。
データベースの接続情報は次のようになる
DATABASE_URL = "postgresql://user:password@db_b_container:5432/db_b_database"
Dockerコンテナ内でのlocalhostの挙動
ちなみにここで、DATABASE_URL=postgresql://root:password@localhost:5432/db_b_database
と設定してもサービスBのデータベースには接続できない。
Dockerコンテナを使用してアプリケーション(サービスA)とデータベース(サービスB)が実行されている状況では、localhost
はその特定のコンテナ内部のループバックアドレス(つまり、そのコンテナ自体)を指す。
サービスAのアプリケーションがDockerコンテナ内で実行されているときにlocalhost
を使って何かに接続しようとすると、それはサービスAのアプリケーションが動作しているその同じコンテナ内部に接続しようとするので、サービスBはもちろんのこと、サービスAのデータベースにも接続できない。
サービスBからサービスAのデータベースへの接続
サービスBのアプリケーションからサービスAのMySQLデータベースに接続する場合も、同様にサービスAのデータベースコンテナ名(db_a_container
)をホスト名として、そしてデータベースがリッスンしている内部ポート(3306
)を使用する。
サービスAのデータベース接続情報は次のようになる:
DATABASE_URL=mysql://root:password@db_a_container:3306/db_a_database
おわりに
Dockerコンテナ間での通信には、コンテナ名ホスト名として使用し、適切な内部ポート番号を指定する。これは、Dockerの内部DNSが自動的にコンテナ名をそのコンテナのIPアドレスに解決するため。
docker-compose.yml
で定義されたポートマッピング(例えば、54320:3306
)は、コンテナの外部(ホストマシンなど)からアクセスするために使用される。コンテナ間通信では、この外部ポート(54320)ではなく、サービスの内部ポートを直接使用する。
【Docker備忘録】DockerFile・docker-compose.yml・Dockerコマンドに関しては、こちら。
コメント