どすえのブログ

ソフトウェア開発ブログ

複数docker-compose間での通信

Dockerを用いた開発において、複数のサービスについて各々docker-compose.ymlを作成するケースがある。マイクロサービスの枠組みではこれらのサービス間での通信が必要であるが、ローカル環境において通信に手間取ったのでメモ。

状況

  • ローカルにて、別々のdocker-compose.ymlでサービスAとサービスBを立ち上げたところhttp://127.0.0.1で通信が取れない。
  • Dockerコンテナで独立したネットワークが作成され、docker-composeによってコンテナ間を接続するネットワークが作成される。しかしdocker-composeを複数展開すると、デフォルトではdocker-compose間を接続するネットワークが存在しないため通信できない。

解決法

以下のコマンドでshared_networkという名前の共有ネットワークを作成する。このときCIDR形式でサブネットの指定をしていることに注意。

$ docker network create shared_network --driver bridge --subnet 172.31.0.0/24

ここではdjangoとnginxでAPIを立ち上げる状態を仮定する。docker-compose.ymlは以下のように記述する。最上位のnetworksにてこのサービスが接続するネットワークを指定する。 各サービスのnetworksにて接続する共有ネットワークshared_networkを指定し、さらに固定のipv4アドレスを割り当てる。この時IPアドレスは共有ネットワーク作成時のサブネットの範囲内であることと、他のサービスのIPアドレスと競合しないことに注意する。

サービスAとサービスBは同じローカル環境で起動しているので、ポートの衝突を避けるために以下では80008080に設定している。

サービスA

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    command: "python manage.py runserver 8000"
    networks:
      shared_network:
        ipv4_address: 172.31.0.2
  nginx:
    image: nginx:1.17.7
    tty: true
    ports:
      - 8000:80
    networks:
      shared_network:
        ipv4_address: 172.31.0.3
    depends_on:
      - web
volumes:
  postgres_data:
  web:
    driver: local

networks:
  shared_network:
    external:
      true

サービスBに関しても同様.サービスAも含めたネットワーク全体で,IPアドレスが競合しないように注意する.

サービスB

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    command: "python manage.py runserver 8080"
    container_name: web
    networks:
      shared_network:
        ipv4_address: 172.31.0.5
  nginx:
    image: nginx:1.17.7
    tty: true
    container_name: nginx
    depends_on:
      - web
    ports:
      - 8080:80
    networks:
      shared_network:
        ipv4_address: 172.31.0.6
volumes:
  web:
    driver: local

networks:
  shared_network:
    external:
      true

するとサービスAからサービスBに向けて、たとえば以下のリクエストで通信が取れるようになる。

import json
import requests

url = 'http://172.31.0.6'  # サービスBのnginx
response = requests.post(
    url,
    data=json.dumps(request_data),
    headers={'Content-Type': 'application/json'}
)