フロントエンドとバックエンドが別々のDocker環境で管理されている場合など、異なるDocker環境どうしでアクセスをしたい時があります。
Dockerのネットワーク機能を利用することでコンテナ間の通信をカスタマイズできます。
今回はDockerネットワークを利用した、異なるDocker環境のコンテナ間通信の実装方法について紹介します。
(追記)
本記事で紹介する方法でも異なるDocker環境のコンテナ間通信は実現できますが、もう少しシンプルな実装方法を別記事で書きました。
詳細についてはDockerネットワークとコンテナ名で実現する複数Docker Compose間の接続方法をご覧になってください。
目次
今回実現したいDocker環境について
異なるリポジトリで管理されたフロントエンドとバックエンドのDocker環境があります。
上記のDocker環境を連携させ、コンテナ間でAPI通信をできるようにすることが今回実現したいことです。
Docker Compose外へ通信するコンテナに対してexternalなDockerネットワークを設定する
フロントエンド環境のwebコンテナとバックエンド環境のapiコンテナを連携する場合は以下のようにします。
webコンテナとapiコンテナはexternal-api
というDockerネットワーク経由で通信することになります。
frontend/docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails6_mysql8
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
+ networks:
+ - external-api
db:
image: mysql:8.0.21
volumes:
- mysql_data_web:/var/lib/mysql
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'rails6_mysql8_development'
volumes:
mysql_data_web:
+ networks:
+ external-api:
+ external: true
backend/docker-compose.yml
version: '3'
services:
api: # Ruby on Railsが起動するコンテナ
build: .
ports:
- '3001:3000' # localhostの3000ポートでアクセスできるようにする
volumes:
- .:/rails6_api_mysql8 # アプリケーションファイルの同期
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
+ networks:
+ - external-api
db: # MySQLが起動するコンテナ
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql # データの永続化
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password # 認証方式を8系以前のものにする。
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'rails6_api_mysql8_development'
volumes:
mysql_data: # データボリュームの登録
+ networks:
+ external-api:
+ external: true
外部連携が不要なコンテナに対して独自のネットワークを設定する
Docker Compose内で通信をするDockerネットワークを作成します。
以下のようにすると、フロントエンド環境のwebコンテナとdbコンテナはinternal-db
というDockerネットワークで通信することになります。
frontend/docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails6_mysql8
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
networks:
- external-api
+ - internal-db
db:
image: mysql:8.0.21
(略)
+ networks:
+ - internal-db
volumes:
mysql_data_web:
networks:
external-api:
external: true
+ internal-db:
同様にバックエンドのDocker Composeも修正します。
backend/docker-compose.yml
version: '3'
services:
api: # Ruby on Railsが起動するコンテナ
build: .
ports:
- '3001:3000' # localhostの3000ポートでアクセスできるようにする
volumes:
- .:/rails6_api_mysql8 # アプリケーションファイルの同期
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
networks:
- external-api
+ - internal-db
db: # MySQLが起動するコンテナ
image: mysql:8.0.21
(略)
+ networks:
+ - internal-db
volumes:
mysql_data: # データボリュームの登録
networks:
external-api:
external: true
+ internal-db:
動作確認
異なるDocker環境どうしで通信ができるか確認をします。
Dockerネットワークの作成
externalなDockerネットワークはdocker-compose up
で自動作成されないため、Docker起動前に作成しておく必要があります。
$ docker network create external-api
$ docker network ls
-> external-api があればOK
アプリケーションの起動
$ cd /path/to/frontend
$ docker-compose up
$ cd /path/to/backend
$ docker-compose up
docker network inspect
でDockerネットワークの詳細がわかります。
external-api
ネットワークにwebコンテナとapiコンテナが存在していればOKです。
$ docker network inspect external-api
[
{
"Name": "external-api",
(略)
"Containers": {
// フロントエンドのwebコンテナ
"06a39d2c3fb39f9807bb60c3d6181fa37e2a7c53b16c486d906da966e3bbf023": {
"Name": "docker-rails6-mysql8_web_1",
"EndpointID": "4124f872a642f3f08c4b01a99414570e87a5b1beb81fb2b3547bd688b3cce594",
"MacAddress": "02:42:c0:a8:d0:02",
"IPv4Address": "192.168.192.2/20",
"IPv6Address": ""
},
// バックエンドのapiコンテナ
"26ae6153a07325c9409fed41195c2aca10634a37ab781123d12ddf8adfcf511c": {
"Name": "docker-rails6-api-mysql8_api_1",
"EndpointID": "df0234c0cc59bc9a2e42d686d4506bd2c79e1c251f8b4416d0299c6aedeb5123",
"MacAddress": "02:42:c0:a8:d0:03",
"IPv4Address": "192.168.192.3/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
コンテナ間での疎通確認
docker inspect
に記載されていたコンテナのIPアドレスに対してping
を実行して反応があればOKです。
### webコンテナからapiコンテナ(192.168.192.3)に対してpingを実行
$ cd /path/to/frontend
$ docker-compose exec web ping 192.168.192.3
PING 192.168.192.3 (192.168.192.3) 56(84) bytes of data.
64 bytes from 192.168.192.3: icmp_seq=1 ttl=64 time=0.087 ms
64 bytes from 192.168.192.3: icmp_seq=2 ttl=64 time=0.065 ms
64 bytes from 192.168.192.3: icmp_seq=3 ttl=64 time=0.061 ms
バックエンドで作成したWeb APIをフロントエンドから呼んでみる
バックエンドで実装したWeb APIをフロントエンドから呼び出せるのか確認してみます。サンプルコードはRails・Rubyです。
まずはバックエンド環境でWeb APIを実装します。
$ cd /path/to/backend
### Eventを操作する機能(モデル、ビュー、コントローラー)を一括作成
$ docker-compose exec api rails g scaffold event title:string
### eventsテーブルを作成
$ docker-compose exec api rails db:migrate
### rails consoleでeventsのレコードを作成
$ docker-compose exec api rails c
> event = Event.new(title: 'sample event')
> event.save
次にフロントエンド環境からバックエンドのAPIを呼びます。レスポンスが返ってくればOKです。
$ cd /path/to/frontend
### irbでバックエンドに対してHTTPリクエストを行う
$ docker-compose exec web irb
> require 'net/http'
> response = Net::HTTP.get_response(URI.parse("http://192.168.192.3:3000/events"))
> response.body
=> "[{\"id\":1,\"title\":\"sample event\",\"created_at\":\"2021-04-04T09:06:17.348Z\",\"updated_at\":\"2021-04-04T09:06:17.348Z\"}]"
docker networkの削除方法
externalなDockerネットワークの削除はdocker network rm
で行います。
$ docker network rm external-api
参考: コンテナに割り振られるIPアドレスを固定する方法
docker network inspect
で調べたIPアドレスを利用して通信する方法でもよいのですが、毎回IPアドレスを調べるのは面倒です。
コンテナに割り振られるIPアドレスを固定する場合は、以下のようにします。
### subnet, gatewayを指定してexternalなDockerネットワークを作成
$ docker network create external-api --subnet=192.168.192.0/20 --gateway=192.168.192.1
frontend/docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails6_mysql8
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
networks:
# Dockerネットワークのサブネットに属する任意のIPを指定
+ external-api:
+ ipv4_address: 192.168.192.2
internal-db:
db:
image: mysql:8.0.21
(略)
networks:
- internal-db
volumes:
mysql_data_web:
networks:
external-api:
external: true
internal-db:
backend/docker-compose.yml
version: '3'
services:
api: # Ruby on Railsが起動するコンテナ
build: .
ports:
- '3001:3000' # localhostの3000ポートでアクセスできるようにする
volumes:
- .:/rails6_api_mysql8 # アプリケーションファイルの同期
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
networks:
# Dockerネットワークのサブネットに属する任意のIPを指定
+ external-api:
+ ipv4_address: 192.168.192.3
internal-db:
db: # MySQLが起動するコンテナ
image: mysql:8.0.21
(略)
networks:
- internal-db
volumes:
mysql_data: # データボリュームの登録
networks:
external-api:
external: true
internal-db:
docker network inspect
で確認すると、指定したIPアドレスがコンテナに割り当てられていることがわかります。
$ docker network inspect external-api
[
{
"Name": "external-api",
(略)
"Containers": {
"4b30e4985e56a317a751fef74cabeb68e0c1be07a1af5ea2b7431f845ff817e4": {
"Name": "docker-rails6-mysql8_web_1",
"EndpointID": "29c156b1021dbfea785ce088848a247b2b1685be8403392a94d7fba6e54676f1",
"MacAddress": "02:42:c0:a8:c0:03",
// ↓ docker-composeで指定したIPアドレスになっている
"IPv4Address": "192.168.192.2/20",
"IPv6Address": ""
},
"6b7296dbf4636b3b287a9cd0302e4207fd9ddcbbd3e9ab7e039be82c162c0e05": {
"Name": "docker-rails6-api-mysql8_api_1",
"EndpointID": "025f62c11dbc00521e09d2e691d388399ffcb3bd6db90b95247b1af6431d54a6",
"MacAddress": "02:42:c0:a8:c0:02",
// ↓ docker-composeで指定したIPアドレスになっている
"IPv4Address": "192.168.192.3/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
まとめ
- Docker環境を連携するExternalなDockerネットワークを作成
- 作成したDockerネットワークを各docker-compose.ymlで定義
- IPアドレスを固定する場合はDockerネットワークのサブネットに属するIPを指定
- Docker環境内で連携するコンテナどうしにはExternalとは別のDockerネットワークを指定
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!