docker composeでコンテナ間通信できるフルスタック開発環境を構築

**実装で学ぶフルスタックWeb開発 エンジニアの視野と知識を広げる「一気通貫」型ハンズオン**
という本を今読んでいます。

 https://amzn.asia/d/9aL1EX6
 
この本のなかでは、フロントエンド、バックエンド、DBの環境はdocker composeを使わず、それぞれ独立したコンテナとして構築していたのですが、docker composeプロジェクトで構築してみたいと思ったので、その内容を実践してみたメモを書きます。



 # 要約
 - docker compose up一発でDB、フロントエンドサーバー、バックエンドサーバーを起動可能
 - docker-compose.ymlで定義した各コンテナサービスは、networkの指定がない場合、デフォルトブリッジネットワークに追加される
 - コンテナ内 -> 外の通信を、フロントエンド -> バックエンド、 バックエンド -> DBで通信接続できることを確認
 - 実際のコードはこちら

https://github.com/masafumi330/fullstack-react-go

# 環境
- Macbook Pro M1 
- Docker Compose version v2.24.6-desktop.1


# 構成イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651150/7986aff3-791a-87c2-2929-135a62ba6ce9.png)


# docker-compose.ymlの記述

```yml:docker-compose.yml
services:
  app-db:
    image: mysql:8
    command:
      --collation-server=utf8mb4_0900_bin
      --transaction-isolation=READ-COMMITTED
    restart: always
    container_name: app-db
    hostname: app-db
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: password
      TZ: Asia/Tokyo
    ports:
      - 53306:3306
  app-frontend:
    build: ./dev/app/frontend
    container_name: app-frontend
    hostname: app-frontend
    volumes:
      - $PWD/dev/app/frontend:/app
      - $PWD/dev/app/frontend/node_modules:/app/node_modules
    ports:
      - 3000:3000
    environment:
      NODE_ENV: development
    stdin_open: true
  app-backend:
    build: ./dev/app/backend
    container_name: app-backend
    hostname: app-backend
    volumes:
      - $PWD/dev/app/backend:/go/src/app
    ports:
      - 4000:4000
    stdin_open: true
```

# Dockerfileの記述

```dockerfile:frontend/Dockerfile
FROM node:20-alpine

WORKDIR /app/

COPY ./package.json ./
COPY src ./src

# buid時に実行
RUN yarn install

# コンテナ起動時に実行
CMD ["yarn", "dev"]
```

```dockerfile:backend/Dockerfile
FROM golang:1.19-alpine

# ログに出力する時間をJSTにするため、タイムゾーンを設定
ENV TZ /usr/share/zoneinfo/Asia/Tokyo

ENV ROOT=/go/src/app
WORKDIR ${ROOT}

# ModuleモードをON
ENV GO111MODULE=on

COPY . .
EXPOSE 4000

# Airをインストールし、コンテナ起動時に実行する
# go1.19をサポートしているのは air v1.41.0
RUN go install github.com/cosmtrek/air@v1.41.0
CMD ["air"]

```

# networkの確認
docker compose では、networkの指定がなければ、デフォルトのブリッジネットワークに各コンテナを登録します。

`$ docker network ls` でネットワークを確認すると、

```sh
$ docker network ls
NETWORK ID     NAME                         DRIVER    SCOPE
1bbc5d4c0d99   fullstack-react-go_default   bridge    local
```

デフォルトのネットワークが追加されており、これをdocker network inspect で確認すると、各コンテナサービスが同一ネットワークに追加されていることを確認できます。

```sh
$ docker network inspect fullstack-react-go_default
[
    {
        "Name": "fullstack-react-go_default",
        "Id": "1bbc5d4c0d99c70d54d4518de6708bc137522696f2fd069ca170ebfdd37dee24",
        "Created": "2024-03-20T02:26:36.947254879Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "xxxxxxxxxx",
                    "Gateway": "xxxxxxxxxx"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        # 各コンテナサービスが同一ネットワークに追加していることが確認できる
        "Containers": {
            "1e425d166b409a10dd721d16acba68a2a53159e89299f2ef2fc0c19aa4d13401": {
                "Name": "app-backend",
                "EndpointID": "5518afa96388efc2fc3f016cf36b6a24c25d88a76a487be9a8299121c38cd97e",
                "IPv4Address": "xxxxxxxxxx",
                "IPv6Address": ""
            },
            "34a1d7f3cfe58a3dd3cb8a9fbe943ef0094448f385f53e4a35f6dd0c9f4aace5": {
                "Name": "app-db",
                "EndpointID": "882bb1c9e69fe82b97998815aaea872d4f44d4f853bc17a929c6769364d6e615",
                "IPv4Address": "xxxxxxxxxx",
                "IPv6Address": ""
            },
            "c8fd9eef063a266f2be5727619464a0fe02607bd0c6f8b6f587cdf347a9ad67d": {
                "Name": "app-frontend",
                "EndpointID": "0f91d208a035d9330939f186e804b5f0fe8783fd2b693c2fa5f7795798d69028",
                "IPv4Address": "xxxxxxxxxx",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "fullstack-react-go",
            "com.docker.compose.version": "2.23.3"
        }
    }
]
```

# 通信の確認

同一ネットワークに追加していれば、各コンテナ内から別のコンテナへ名前解決を行い通信することができます。

frontend -> backendの通信を確認。
```sh
$ docker exec -it app-frontend sh
/app # curl http://app-backend:4000
Hello!
# 接続OK
```

backend -> dbの通信を確認。
```sh
$ docker exec -it app-backend sh

# mysql -u root -h app-db -P 3306 --protocol tcp -ppassword
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.3.0 MySQL Community Server - GPL

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>
# 接続OK
```

各コンテナ間の通信を確認することができました。