🐳

MySQL + DynamoDB の docker-compose 環境を作成しました

2024/03/07に公開

背景

Cykinso の 研究&データ技術開発 の山﨑です。

MySQL と DynamoDB を同時に利用する環境を構築するプロダクトの開発を行いました。
各人が開発環境を構築すると大変なのでチームで共有して利用するように docker-compose.yml にまとめて VS Code の devcontainer で起動できるようにしました。

MySQL, DynamoDB 単独でまとめている記事はあったのですが両方同時に利用している例はなかったので他の方の助けになるかと思いますのでまとめておきます。

方法

Frontend: Next.js
Backend: Nest.js
プログラミング言語: TypeScript

と、今回は環境を決定し、以下の docker-compose.yml を作成しました。

docker-comopse.yml
version: "3.9"
services:
  app:
    container_name: project-app
    build: .
    ports:
      - "3000:3000"
      - "8000:8000"
      - "6006:6006"
    depends_on:
      - mysql
      - dynamodb
    networks:
      - mysql-network
      - dynamodb-network
    command: /bin/sh -c "while true; do sleep 3600; done" # keep container running
  mysql:
    container_name: project-mysql
    image: mysql:8.1.0
    restart: unless-stopped
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: mysql
      MYSQL_DATABASE: db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      TZ: "Asia/Tokyo"
    networks:
      - mysql-network
  dynamodb:
    container_name: project-dynamodb
    image: amazon/dynamodb-local:2.0.0
    restart: unless-stopped
    ports:
      - "8010:8000"
    volumes:
      - ./dynamodb_data/:/data
    networks:
      - dynamodb-network
    command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/data"]
  dynamodb-admin:
    container_name: project-dynamodb-admin
    image: aaronshaf/dynamodb-admin:4.6.1
    environment:
      - DYNAMO_ENDPOINT=http://dynamodb:8000
    ports:
      - 8001:8001
    depends_on:
      - dynamodb
    networks:
      - dynamodb-network

networks:
  mysql-network:
    driver: bridge
  dynamodb-network:
    driver: bridge

各サービスの説明をしていきます。

app

appdevcontainer を起動した時に利用するコンテナを想定しています。
Next.js で 3000, Nest.js で 8000, Next.js で利用する Storybook で 6006 のポートを利用するためにポートを開けておきます。

depends_on に後述のサービス mysql, dynamodb を指定します。 networks にこちらも後述のサービス mysql, dynamodb に接続するため mysql-network, dynamodb-network を指定します。これにより devcontainer の中から接続することができるようになります。

docker-compose (docker 全般?) は command として例えばサーバを起動するなどのことを行わないと勝手に container が閉じてしまいます。これでは devcontainer として利用ができなくなるので、 command として 60 分間 sleep するということを永遠に繰り返すようにしています。

app
  app:
    container_name: project-app
    build: .
    ports:
      - "3000:3000"
      - "8000:8000"
      - "6006:6006"
    depends_on:
      - mysql
      - dynamodb
    networks:
      - mysql-network
      - dynamodb-network
      - project_fixed_address_network
    command: /bin/sh -c "while true; do sleep 3600; done" # keep container running

build で参照している dockerfile は以下のようになっており TypeScript を利用するために必要なソフトウェアなどをインストールしています。あと珍しいのは dood を行うために docker をインストールしている点と最後の COPY . . でしょうか?
app を利用する時に devcontainer 側の mount がうまくワークしなかったためこちらで COPY を行うようにしています。
dood を行う必要がなければもちろん docker はインストールしなくてかまいません。

# Use the official Node.js image as a parent image
FROM node:20-bookworm-slim

# Install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl=7.88.1-10+deb12u4 \
    git=1:2.39.2-1.1  \
    ssh=1:9.2p1-2+deb12u1 \
    tree=2.1.0-1 \
    zsh=5.9-4+b2 \
    default-mysql-client=1.1.0 \
    awscli=2.9.19-1 \
    less=590-2 \
    ca-certificates=20230311 \
    gnupg=2.2.40-1.1 \
    lsb-release=12.0-1 \
    make=4.3-4.1 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install npm packages in global
RUN npm i -g \
    prettier@3.0.0 \
    @nestjs/cli@10.0.0 \
    dotenv-cli@7.3.0 \
    aws-cdk@2.114.1 \
    eslint@8.55.0

# Install docker
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
    && echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
    $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
    docker-ce=5:24.0.7-1~debian.12~bookworm \
    docker-ce-cli=5:24.0.7-1~debian.12~bookworm \
    containerd.io=1.6.26-1 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Set the working directory in the container
WORKDIR /app

# Copy the rest of the application to the working directory
COPY . .

mysql

mysql 公式の docker image や公式の dockerhub で紹介されている environment を用いて以下のようにログイン時のパスワードなどを設定しています。

mysql
  mysql:
    container_name: project-mysql
    image: mysql:8.1.0
    restart: unless-stopped
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: mysql
      MYSQL_DATABASE: db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      TZ: "Asia/Tokyo"
    networks:
      - mysql-network

これにより

mysql -h mysql -u root -pmysql db

devcontainer からログインできるようになります。 -h mysql で接続できるのは networks で設定しているからです。
今回ローカル PC で開発のために利用する前提なので簡素なパスワードにしていますが、もちろん本番環境ではより堅牢なパスワードにしてくださいね。

dynamoDB

続けて dynamoDB 側です。

dynamoDB は AWS から dynamodb-local というローカルで開発する時に利用するための docker image が配布されておりこれを利用することでローカルで DynamoDB の環境を構築することができます。
dynamodb のデフォルトポートは 8000 なのですが 私の環境ではポート 8000 は Nest.js で利用しているので 8010:8000 としてホスト PC からは 8010 で接続するようにしました (Nest.js 側を 8000 にしなければよかったとちょっと後悔しています)

dynamodb
  dynamodb:
    container_name: project-dynamodb
    image: amazon/dynamodb-local:2.0.0
    restart: unless-stopped
    ports:
      - "8010:8000"
    volumes:
      - ./dynamodb_data/:/data
    networks:
      - dynamodb-network
    command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/data"]

以上の設定により devcontainer から aws cli で接続をすることができます。
ただし、 dynamodb-local を利用する場合はダミーの AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION (または AWS_DEFAULT_REGION ) をアサインする必要があるので注意してください。
(これは Nest.js などから接続する時もです)

unset AWS_PROFILE  # AWS_PROFILE が設定されている場合そちらが優先されワークしないことがあります
export AWS_ACCESS_KEY_ID="AAAAAAAAAA"
export AWS_SECRET_ACCESS_KEY="BBBBBBBBBB"
export AWS_REGION="CCCCCCCCCC"

特に、間違って AWS_REGION=ap-north-east1 などがアサインされていると aws cli でそちらのリージョンに新規テーブルなどを作成してしまうので注意してください。

設定ができたら aws cli で例えば以下のように接続します。

aws dynamodb list-tables --endpoint-url http://dynamodb:8000

dynamodb-admin

dynamodb-local のデータをホスト PC からブラウザでデータを確認するためのサービス dynamodb-admin というものがありがたいことに有志の方によって開発されています。
(こちらを利用しているので実は前述の aws cli で接続しデータの確認などをすることはあまりありません)

  dynamodb-admin:
    container_name: project-dynamodb-admin
    image: aaronshaf/dynamodb-admin:4.6.1
    environment:
      - DYNAMO_ENDPOINT=http://dynamodb:8000
    ports:
      - 8001:8001
    depends_on:
      - dynamodb
    networks:
      - dynamodb-network

サービスを起動するとブラウザの URL に http://localhost:8001 と入力することで以下のようなサイトを表示することができます。
(http://dynamodb:8000 ではないです。こちらは devcontaienr からアクセスする時の URL です)

AWS の公式とは見た目は異なりますが無料で利用できるのなら十分だと思います。

networks

最後に service 同士を連携させるために networks を設定しています。

networks
networks:
  mysql-network:
    driver: bridge
  dynamodb-network:
    driver: bridge

以上で docker-compose.yml の説明は終わります。

devcontainer.json

ディレクトリ構成は以下のようになってるものとして話を進めます。

> tree -La 2
.
├── .devcontainer
│   ├── devcontainer-template.json
│   └── devcontainer.json
├── Dockerfile
└── docker-compose.yml

devcontainer.json では以下のように docker-compose.yml とそのサービス app を指定してください。

devcontainer.json
{
  "name": "${localWorkspaceFolderBasename}",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "app",
  "workspaceFolder": "/workspace/${localWorkspaceFolderBasename}",
  // skipping...
  "mounts": [    "source=/Users/yamasakih/repositories/${localWorkspaceFolderBasename},target=/workspace/${localWorkspaceFolderBasename},type=bind,consistency=delegated",
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind,consistency=delegated"  # dood にしたいなら必要です。
  ]
}

mounts では、マウントするディレクトリをリポジトリの直下を指定し書き換えてください。 例えばディレクトリ構成が /Users/yamasakih/dev/xxxxx であるなら /Users/yamasakih/dev/${localWorkspaceFolderBasename} に書き換えてください。
(${localWorkspaceFolderBasenam} = "xxxxx" になっているイメージです)

以上まで用意し VSCode で devcontainer を起動すると app サービスのコンテナ内に入っている状態で VSCode を利用することができます。
もちろん MySQL, DynamoDB に devcontainer から接続できます。

💡 まとめ

  • MySQL と DynamoDB を同時にローカルで利用できるようにしました
  • devcontainer を利用してチームで簡単に環境を共有できるようにしました
Cykinso's Tech Blog

Discussion