フロントエンドとバックエンドが別々のDocker環境で管理されている場合など、異なるDocker環境どうしでアクセスをしたい時があります。
Dockerのネットワーク機能を利用することでコンテナ間の通信をカスタマイズできます。
今回はDockerネットワークを利用した、異なるDocker環境のコンテナ間通信の実装方法について紹介します。
目次
今回実現したいDocker環境について
異なるリポジトリで管理されたフロントエンドとバックエンドのDocker環境があります。
上記のDocker環境を連携させ、コンテナ間でAPI通信をできるようにすることが今回実現したいことです。
Docker Compose外へ通信するコンテナに対してexternalなDockerネットワークを設定する
フロントエンド環境のwebコンテナとバックエンド環境のapiコンテナを連携する場合は以下のようにします。
webコンテナとapiコンテナはexternal.group
というDockerネットワーク経由で通信することになります。
frontend/docker-compose.yml
version: '3'
services:
web:
tty: true
stdin_open: true
build: .
ports:
- '3000:3000'
volumes:
- .:/app
- gem_data:/usr/local/bundle
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
+ networks:
+ - external.group
db:
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'docker_rails7_web_mysql8_boilerplate_development'
volumes:
mysql_data:
gem_data:
+ networks:
+ external.group:
+ external: true
backend/docker-compose.yml
version: '3'
services:
api:
tty: true
stdin_open: true
build: .
ports:
- '3001:3000'
volumes:
- .:/app
- gem_data:/usr/local/bundle
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
container_name: sample-app-api
+ networks:
+ - external.group
db:
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'docker_rails7_api_mysql8_boilerplate_development'
volumes:
mysql_data:
gem_data:
+ networks:
+ external.group:
+ external: true
Docker Compose内のコンテナにも接続する場合は内部通信用のDockerネットワークを別途設定する
Docker Compose外へ通信するコンテナについて、Docker Compose内のコンテナにも通信する場合は内部通信用のDockerネットワークを定義する必要があります。
今回の場合ですとフロントエンド環境のwebコンテナ、バックエンド環境のapiコンテナに対して各dbコンテナに接続するためのDockerネットワークを定義します。
default
というDockerネットワークを定義することで内部通信ができます。default
が定義されていないとdbコンテナにアクセスできず、Mysql2::Error::ConnectionError: Unknown MySQL server host 'db'
のようなDB接続エラーがDocker Composeの起動時に発生します。
frontend/docker-compose.yml
version: '3'
services:
web:
tty: true
stdin_open: true
build: .
ports:
- '3000:3000'
volumes:
- .:/app
- gem_data:/usr/local/bundle
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
networks:
- external-api
+ - default
db:
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'docker_rails7_web_mysql8_boilerplate_development'
volumes:
mysql_data:
gem_data:
networks:
external-api:
external: true
同様にバックエンドのDocker Composeも修正します。
backend/docker-compose.yml
version: '3'
services:
api:
tty: true
stdin_open: true
build: .
ports:
- '3001:3000'
volumes:
- .:/app
- gem_data:/usr/local/bundle
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
container_name: sample-app-api
networks:
- external.group
+ - default
db:
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'docker_rails7_api_mysql8_boilerplate_development'
volumes:
mysql_data:
gem_data:
networks:
external.group:
external: true
Dockerネットワーク経由でコンテナに通信する方法
Dockerネットワーク経由でDocker Compose外のコンテナに接続する方法は以下の通りです。
- コンテナに割り振られたIPアドレスを直接指定する
- サービス名を指定する
- コンテナ名を指定する
- 『サービス名.Dockerネットワーク名』を指定する
- 『コンテナ名.Dockerネットワーク名』を指定する
動作確認
異なるDocker環境どうしで通信ができるか確認をします。
Dockerネットワークの作成
外部連携用のDockerネットワークはdocker-compose up
で自動作成されないため、Docker起動前に作成する必要があります。
### Dockerネットワーク作成
$ docker network create external.group
### Dockerネットワーク一覧確認
$ docker network ls
-> external.group があればOK
事前にDockerネットワークを作成をしていない場合、ERROR: Network external.group declared as external, but could not be found. Please create the network manually using docker network create external.group and try again.
のようなエラーがDocker Compose起動時に発生します。
アプリケーションの起動
$ cd /path/to/frontend
$ docker-compose up
$ cd /path/to/backend
$ docker-compose up
docker network inspect
でDockerネットワークの詳細がわかります。
external.group
ネットワークにwebコンテナとapiコンテナが存在していればOKです。
$ docker network inspect external.group
[
{
"Name": "external.group",
(略)
"Containers": {
# バックエンドのapiコンテナ
"38f1e8abd2f8d7e451182d0af4c4b1d5ad46bf91df4f6591b78d6ef43afacc13": {
"Name": "sample-app-api",
"EndpointID": "94055f806552a03b7630693b0180f3a1748be62276075924f08e52cc955f2a49",
"MacAddress": "02:42:c0:a8:c0:02",
"IPv4Address": "192.168.192.2/20",
"IPv6Address": ""
},
# フロントエンドのwebコンテナ
"b85a290dfdd99a2dfc57cdcf8cfa846f43f43713fd990331a90cf8f0d32acded": {
"Name": "docker-rails7-web-mysql8-boilerplate_web_1",
"EndpointID": "42240d67c540c9bae6a93c09e2abd2cabb56f4c8c5f809a1b907103da55dafbd",
"MacAddress": "02:42:c0:a8:c0:03",
"IPv4Address": "192.168.192.3/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
コンテナ間での疎通確認
Dockerネットワーク経由でコンテナと通信する方法を試した結果は以下のようになります。
結果を見て分かる通り、各方法は同じIPアドレスを指します。
$ cd /path/to/frontend
$ docker-compose exec web /bin/bash
### サービス名を指定する方法
$ dig api +short
# +short: IPアドレスのみを表示する
192.168.192.2
### コンテナ名を指定する方法
$ dig sample-app-api +short
192.168.128.2
### 『サービス名.Dockerネットワーク名』を指定する方法
$ dig api.external.group +short
192.168.128.2
### 『コンテナ名.Dockerネットワーク名』を指定する方法
$ dig sample-app-api.external.group +short
192.168.128.2
webコンテナからapiコンテナにping
を実行して反応があればOKです。
$ cd /path/to/frontend
$ docker-compose exec web /bin/bash
### コンテナに割り振られたIPアドレスを直接指定する方法
$ ping 192.168.192.2
PING 192.168.192.2 (192.168.192.2) 56(84) bytes of data.
64 bytes from 192.168.192.2: icmp_seq=1 ttl=64 time=1.16 ms
### サービス名を指定する方法
$ ping api
PING api (192.168.192.2) 56(84) bytes of data.
64 bytes from sample-app-api.external.group (192.168.192.2): icmp_seq=1 ttl=64 time=0.093 ms
### コンテナ名を指定する方法
$ ping sample-app-api
PING sample-app-api (192.168.192.2) 56(84) bytes of data.
64 bytes from sample-app-api.external.group (192.168.192.2): icmp_seq=1 ttl=64 time=0.149 ms
### 『サービス名.Dockerネットワーク名』を指定する方法
$ ping api.external.group
PING api.external.group (192.168.192.2) 56(84) bytes of data.
64 bytes from sample-app-api.external.group (192.168.192.2): icmp_seq=1 ttl=64 time=0.066 ms
### 『コンテナ名.Dockerネットワーク名』を指定する方法
$ ping sample-app-api.external.group
PING sample-app-api.external.group (192.168.192.2) 56(84) bytes of data.
64 bytes from sample-app-api.external.group (192.168.192.2): icmp_seq=1 ttl=64 time=0.077 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'
### hostは『api』, 『sample-app-api』, 『api.external.group』, 『sample-app-api.external.group』でも可
> host = "192.168.192.2"
> response = Net::HTTP.get_response(URI.parse("http://#{host}:3000/events"))
> response.body
=> "[{\"id\":1,\"title\":\"sample event\",\"created_at\":\"2022-02-07T12:26:16.685Z\",\"updated_at\":\"2022-02-07T12:26:16.685Z\"}]"
なお、バックエンドのRailsが6系以上ですとホスト名でアプリケーションにアクセスした際にBlocked host: xxx
というエラーが発生し、HTTPリクエストのレスポンスが#<Net::HTTPForbidden 403 Forbidden readbody=true>
になる可能性があります。
403エラーになった場合はlocalhost以外のホスト名でローカル環境のRailsに接続する方法を参考にホスト名でRailsアプリケーションにアクセスできるようにしてください。
Dockerネットワークの削除方法
Dockerネットワークの削除はdocker network rm
で行います。
$ docker network rm external-api
さいごに
ローカル環境の場合は、Dockerネットワークを利用せずローカル経由で異なるDocker環境に接続する方法もあります。詳細解説はローカル環境の異なるDocker Compose間の通信をhost.docker.internalで解決するで紹介しています。
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!