複数のdocker-compose間で通信する
異なるdocker-composeで動く環境に接続したくなったので調べてみました。
準備
まず、以下のようなdocker-compose.ymlで定義された2つの環境があるとします。
.
├── app_a
│ ├── app/...
│ └── docker-compose.yml
└── app_b
├── app/...
└── docker-compose.yml
version: '3.7' | |
services: | |
app_a: | |
image: golang:latest | |
tty: true | |
db_a: | |
image: postgres:latest |
version: '3.7' | |
services: | |
app_b: | |
image: golang:latest | |
tty: true | |
db_b: | |
image: postgres:latest |
この状態で app_a のコンテナと app_b のコンテナで通信することを考えます。
そもそもdocker-composeのネットワークってどうなってるの?
先ほどのapp_aのdocker-composeを起動すると以下のようなログが出ます。
~/app_a$ docker-compose up -d
Creating network "app_a_default" with the default driver
Creating app_a_db_a_1 ... done
Creating app_a_app_a_1 ... done
コンテナを立ち上げる前にapp_a_default
というネットワークが作られています。このネットワークの詳細を見ると、以下のようになっています。
~/app_a$ docker network inspect app_a_default
[
{
"Name": "app_a_default",
"Id": "2c3cc9e7edaa5f54371d12299eaa6e26e710c45d5baca8196eeae67054e5fc53",
"Created": "2020-02-01T11:53:37.0174152Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "192.168.80.0/20",
"Gateway": "192.168.80.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"0688a9b74b6dab1b2b85113f0bc535c585cb7bd48b659f0f00081f3d183ee310": {
"Name": "app_a_db_a_1",
"EndpointID": "5442fa856c1c3e6c81bb2e10fcb424ae10c22287c43a198c3bc7b8fdf2dd2fa6",
"MacAddress": "02:42:c0:a8:50:03",
"IPv4Address": "192.168.80.3/20",
"IPv6Address": ""
},
"a7d712117e7c243437269eb29f83f0ea453c5ba9201639218d9993f45c06e1bb": {
"Name": "app_a_app_a_1",
"EndpointID": "8cb0d595d775ed385bd9a6ff30d988631261cd0f3e634f98b38ace7432af0afd",
"MacAddress": "02:42:c0:a8:50:02",
"IPv4Address": "192.168.80.2/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "app_a",
"com.docker.compose.version": "1.24.1"
}
}
]
"Containers"
という項目を見ると、2つのコンテナがネットワーク上にあるのが分かります。
このように、docker-compose up
で起動すると、ネットワークを自動で作り、そのネットワークにコンテナがアタッチされた状態になります。
そのため、docker-composeを使用していると、自分で何も設定しなくてもコンテナ間で通信ができます。
Dockerのネットワークに関しては以下の記事が参考になりました。
異なるdocker-compose間での通信
別のdocker-composeであろうと、同一のネットワーク上に存在するコンテナどうしなら通信ができます。
以下の記事を参考に設定をしてみました。
以下のようにdocker-compose.ymlを修正します。
version: '3.7' | |
services: | |
app_a: | |
image: golang:latest | |
tty: true | |
networks: | |
- default | |
- shared-network | |
db_a: | |
image: postgres:latest | |
networks: | |
shared-network: | |
external: true |
version: '3.7' | |
services: | |
app_b: | |
image: golang:latest | |
tty: true | |
networks: | |
- default | |
- shared-network | |
db_b: | |
image: postgres:latest | |
networks: | |
shared-network: | |
external: true |
それぞれのdocker-composeのapp_a,app_bの設定にnetworks
という項目を設定しています。これは各コンテナに接続するネットワークを指定できます。この項目を何も設定しなければ、自動でdefaultのネットワークにセットされます。
app_a,app_bのnetworks
に共通のネットワーク(shared-network
)を追加することで、コンテナ間の通信が可能になります。
shared-network
はdocker-composeで管理されていないので、networks
の設定が必要です(services
と同じ階層)。
external: true
はdocker-composeで管理されていない外部のネットワーク、という意味です。
そして、docker-compose起動前に以下のコマンドを実行し、ネットワークを作成しておきます。
docker network create shared-network
これでshared-netowork
というネットワークが作成されます。このネットワークを作成した状態で上の2つのdocker-composeを起動します。
~/app_a$ docker network create shared-network
1eb174ca4efc48e20e8210a443d7b9c371fedb7b18bb6fa20ce34234da1bf7df
~/app_a$ docker-compose up -d
Creating network "app_a_default" with the default driver
Creating app_a_app_a_1 ... done
Creating app_a_db_a_1 ... done~/app_a$ cd ../app_b/
~/app_b$ docker-compose up -d
Creating network "app_b_default" with the default driver
Creating app_b_app_b_1 ... done
Creating app_b_db_b_1 ... done
app_bのコンテナに入り、app_aに対してpingを打ってみます。
~/app_b$ docker-compose exec app_b bash
root@ec898649299c:/go# ping app_a
PING app_a (192.168.96.2) 56(84) bytes of data.
64 bytes from app_a_app_a_1.shared-network (192.168.96.2): icmp_seq=1 ttl=64 time=0.508 ms
64 bytes from app_a_app_a_1.shared-network (192.168.96.2): icmp_seq=2 ttl=64 time=0.209 ms
きちんと通信できています。
ちなみに、app_bとdb_aは別のネットワークなので通信はできません。
root@ec898649299c:/go# ping db_a
ping: db_a: Name or service not known
docker-compose外のネットワークの管理が面倒
これで通信自体はできますが、shared-network
を作成せずにdocker-compose up
するとエラーになったり、docker-compose down
してもshared-network
は消えないので残ったままになったりと、色々と面倒です。
そこで、Makefileにそのあたりの設定を追加することにしました。
(たまたま自分が今のプロジェクトでMakefileを使っていたのでMakefileでやりましたが、普通のシェルスクリプトでも良いと思います)
Makefileに以下を追加します。
NETWORK_NAME = shared-network | |
up: | |
@if [ -z "`docker network ls | grep $(NETWORK_NAME)`" ]; then docker network create $(NETWORK_NAME); fi | |
docker-compose up -d | |
down: | |
docker-compose down | |
@if [ -n "`docker network inspect $(NETWORK_NAME) | grep \"\\"Containers\\": {}\"`" ]; then docker network rm $(NETWORK_NAME); fi |
これで make up
を実行すると、shared-network
が作成されていなければ作成してからdocker-compose up
されます。
また、make down
を実行すると、docker-compose down
した後にshared-network
上のコンテナを確認し、1つもなければshared-network
を削除します。
`docker network inspect $(NETWORK\_NAME) | grep \"\\"Containers\\": {}\"`
のあたりが若干強引なので、何かいい方法があると良いのですが…
まとめ
以下のような構成だった場合、
.
├── app_a
│ ├── app/...
│ ├── Makefile
│ └── docker-compose.yml
└── app_b
├── app/...
├── Makefile
└── docker-compose.yml
以下のように設定すると、異なるdocker-composeのコンテナ間で通信ができます。
version: '3.7' | |
services: | |
app_a: | |
image: golang:latest | |
tty: true | |
networks: | |
- default | |
- shared-network | |
db_a: | |
image: postgres:latest | |
networks: | |
shared-network: | |
external: true |
version: '3.7' | |
services: | |
app_b: | |
image: golang:latest | |
tty: true | |
networks: | |
- default | |
- shared-network | |
db_b: | |
image: postgres:latest | |
networks: | |
shared-network: | |
external: true |
NETWORK_NAME = shared-network | |
up: | |
@if [ -z "`docker network ls | grep $(NETWORK_NAME)`" ]; then docker network create $(NETWORK_NAME); fi | |
docker-compose up -d | |
down: | |
docker-compose down | |
@if [ -n "`docker network inspect $(NETWORK_NAME) | grep \"\\"Containers\\": {}\"`" ]; then docker network rm $(NETWORK_NAME); fi |