1
3

Docker Composeで3層アーキテクチャ( JS + Python + MySQL)を構築する

Posted at

はじめに

Docker Composeで3層アーキテクチャの開発環境を構築していきます。

  • プレゼンテーション層はJavaScript
  • アプリケーション層はPython
  • データ層はMySQL

を使用します。

ソースコードはこちら

スクリーンショット 2024-04-08 13.22.03.png

開発環境

  • MacBook M2
  • Docker v.25.0.3
  • Docker Compose v2.24.5

ディレクトリ構成

root/
    ├ web/
    |   └ sample.js
    ├ app/
    |   ├ api.py
    |   └ con_db.py
    ├ docker/
    |   ├ web_context/
    |   |   └ Dockerfile
    |   ├ app_context/
    |   |   ├ Dockerfile
    |   |   └ requirements.txt
    |   ├ db_context/
    |       ├ Dockerfile
    |       └ init/
    |           └ init.sql
    └ docker-compose.yml

手順

1. Webコンテナイメージの作成

./docker/web_context/Dockerfileを作成します。

Dockerfile
# 元イメージを定義
FROM node:20.12-bullseye-slim

# apt-getの更新
RUN apt-get update && apt-get -y install

2. Appコンテナイメージの作成

./docker/app_context/requirements.txtを作成します。

今回は、APIをFastAPIで作成するので、fastapiuvicornを追加します。
また、MySQLと通信するためにmysql-connector-pythonも追加します。

requirements.txt
fastapi==0.110.1
uvicorn==0.29.0
mysql-connector-python==8.3.0

./docker/app_context/Dockerfileを作成します。

Dockerfile
# 元イメージを定義
FROM python:3.11.9-slim-bullseye

# apt-getの更新とユーザを追加
RUN apt-get update && apt-get -y install && \
    groupadd -g 1100 pyuser && \
    useradd -m -s /bin/bash -u 1100 -g 1100 pyuser

# ホスト上のrequirements.txtをコピー
COPY requirements.txt /home/pyuser

# 作業ディレクトリを変更
WORKDIR /home/pyuser

# パッケージをインストール
RUN pip install -r requirements.txt

Dockerイメージの軽量化のため、今回はslimを使用します。

pyuserはDocker Composeで実行ユーザとして使用します。

3. DBコンテナイメージの作成

./docker/db_context/Dockerfileを作成します。

Dockerfile
# 元イメージを定義
FROM mysql:8.3.0

# ユーザを追加 (mysqluser)
RUN groupadd -g 1100 mysqluser && \
    useradd -m -s /bin/bash -u 1100 -g 1100 mysqluser

mysqluserはDocker Composeで実行ユーザとして使用します。

4. Docker Composeの作成

Docker Composeで先ほど作成したイメージを使ってコンテナを構築します。

ルートディレクトリにdocker-compose.ymlを作成します。

docker-compose.yml
# Docker Composeのバーション
version: '3'

# サービスの定義
services:
  # Webコンテナ
  web:
    container_name: web
    build:
      context: ./docker/web_context/
      dockerfile: ./Dockerfile
    user: node
    working_dir: /home/node/web
    volumes:
      - ./web:/home/node/web
    ports:
      - 3000:3000
    tty: true
    depends_on:
      - app
    networks:
      - client

  # Appコンテナ
  app:
    container_name: app
    build:
      context: ./docker/app_context/
      dockerfile: ./Dockerfile
    user: pyuser
    working_dir: /home/pyuser/app
    volumes:
      - ./app:/home/pyuser/app
    command: uvicorn api:app --host=0.0.0.0 --reload
    ports:
      - 8000:8000
    tty: true
    depends_on:
      - db
    networks:
      - client
      - server

  # DBコンテナ
  db:
    container_name: db
    build:
      context: ./docker/db_context/
      dockerfile: ./Dockerfile
    user: mysqluser
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample_db
      MYSQL_USER: sample_user
      MYSQL_PASSWORD: sample_user
    volumes:
      - ./docker/db_context/init:/docker-entrypoint-initdb.d
    ports:
      - 3006:3006
    networks:
      - server

networks:
  client:
    driver: bridge
  server:
    driver: bridge

Webコンテナ

イメージは./docker/web_context/Dockerfileを使用するので、
ビルド情報のコンテキストは./docker/web_context/のディレクトリを指定します。

作業ユーザはNodeイメージにはnodeというユーザが用意されているので、それを使います。
作業ディレクトリはnodeユーザのホームディレクトリである./home/node/の中にweb/というディレクトリを作成して使用します。
working_dir: /home/node/webと記述すると、webディレクトリが存在しないときは、自動で作成してくれます。

ボリュームマウント( volumes: - ./web:/home/node/web )することで、ファイルを永続化しています。

ポート番号はReactを想定して、3000にしています。Vue.jsはports: -8080:8080など、適宜変更してください。

WebコンテナはAppコンテナのAPIと通信する想定のため、Appコンテナが起動してから、Webコンテナを起動するようにします( depends_on: - app )。

また、WebコンテナがDBコンテナと通信できないようにするため、clientというネットワークを作り、Appコンテナにだけ接続するようにしています。

Appコンテナ

FastAPIを使用しているので、コンテナ起動時にサーバが立ち上がるようにするため、command: uvicorn api:app --host=0.0.0.0 --reloadを設定しています。
コンテナ起動時に、APIが作成されていないと、このコマンドでエラーが発生するので、FastAPIで簡単なコードを作成しておきます。

./app/api.pyを作成します。

api.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}

AppコンテナはDBコンテナとデータのやり取りとするので、DBコンテナが起動してから、Appコンテナが起動するようにしています( depends_on: - db )。

また、AppコンテナはWebコンテナ、DBコンテナ両方と通信するため、両方のネットワークと接続するようにしています。

DBコンテナ

環境変数でMySQLの設定を追加しています。

  • MYSQL_ROOT_PASSWORD
    ルートユーザのパスワード
  • MYSQL_DATABASE
    データベースの作成
  • MYSQL_USER
    ユーザの作成
  • MYSQL_PASSWORD
    作成したユーザのパスワード

初期データの作成

MySQLでは、docker-entrypoint-initdb.dディレクトリにソースコードを置くことで、コンテナ起動時に自動で初期データを作成することができます。

今回はホスト上の./docker/db_context/init/にソースコードを作成して、コンテナ内のdocker-entrypoint-initdb.dとボリュームマウントさせて、初期データを作成します。

./docker/db_context/init/init.sqlを作成します。

初期データの例

init.sql
use sample_db;

create table sample_table(
    id integer not null,
    name char(20) not null,
    primary key (id)
    );

insert into sample_table(
    id,name
)
values(
    1,"sample_user"
);

ネットワークの作成

  • clientネットワーク
    Webコンテナ - Appコンテナ間の通信に使用します。

  • serverネットワーク
    Appコンテナ - DBコンテナ間の通信に使用します。

5. コンテナの起動

以下のコマンドでコンテナを起動します。

$ docker compose up --build

6. コンテナ間の通信

Webコンテナ - Appコンテナ

WebコンテナからAppコンテナにあるAPIを叩いてみます。

  1. ./web/sample.jsを作成
    Dockerではコンテナ間でネットワークが繋がっているとサービス名を使って名前解決してくれます。
    WebコンテナからAppコンテナはネットワークが繋がっているので、
    URLはhttp://app、ポート番号はFastAPIのデフォルトポート番号の8000を使います。
    * ブラウザで動かす時( ReactやVue.jsなど )は名前解決してくれないので、http://localhost:8000/を使います。
sample.js
const appUrl="http://app:8000";

const fetchApp=async()=>{
    const response= await fetch(appUrl);
    const data=response.json();
    return data;
};

(async()=>{
    try{
        const data= await fetchApp();
        console.log(data);
    }catch(error){
        console.log(error);
    };
})();

2.Webコンテナに入る

$ docker compose exec web bash
  1. Webコンテナ内でsample.jsを実行
$ node sample.js

{"Hello":"World"}がコンソールに表示されれば成功!

Appコンテナ - DBコンテナ

AppコンテナでDBコンテナからデータを取り出します。

  1. ./app/con_db.pyを作成
con_db
import mysql.connector

cnx = mysql.connector.connect(
    user='sample_user',
    password='sample_user',
    host='db',
    database='sample_db')

with cnx.cursor() as cursor:
    result = cursor.execute("SELECT * FROM sample_table")
    rows = cursor.fetchall()
    print("sample_table")
    for rows in rows:
        print(rows)
  1. Appコンテナに入る
$ docker compose exec app bash
  1. con_db.pyを実行
$ python3 con_db.py
sample_table
(1, 'sample_user')

がコンソールに表示されれば成功!

まとめ

Docker Composeで Javascript・Python ( FastAPI )・MySQLのコンテナ作成と通信方法についてまとめました。

全体のソースコードはGitHubを参照してください!

参考資料

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3