1
1

【Docker Compose】Node.js / TypeScript / MySQL の開発環境構築

Last updated at Posted at 2024-08-01

はじめに

この記事では、Docker Compose を利用した Node.js / TypeScript / MySQL の開発環境構築手順について記載します。

開発環境

開発環境は以下の通りです。

  • Windows11
  • Docker Engine 27.0.3
  • Docker Compose 2
  • MySQL 9.0.0
  • Node.js 20.15.1
  • npm 10.8.2
  • @types/node 20.14.11
  • ts-node 10.9.2
  • TypeScript 5.5.3

Node.js / TypeScript の環境構築

まずは Node.js / TypeScript の環境構築をおこないます。

package.json の作成

package.json を作成します。

コマンド実行中の質問をスキップするため、-y をつけて実行します。

npm init -y

package.json が作成されます。

package.json
{
  "name": "node-mysql-crud",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}

TypeScript のインストールと設定

TypeScript 及び以下のパッケージをインストールします。

  • ts-node : TypeScript を JavaScript へコンパイルせず実行できるようにする
  • @types/node : Node.js の型定義

npm install typescript ts-node @types/node --save-dev

package.json にインストールしたパッケージが表示されます。

package.json
{
  ...
  "devDependencies": {
    "@types/node": "^20.14.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.5.3"
  }
}

TypeScirpt 設定ファイルを作成します。

npx tsc --init

Node.js 20 系を、ES Modules (ECMA Script Modules) で利用する設定に変更します。

tsconfig.json
{
  "compilerOptions": {
    /* Language and Environment */
    "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "lib": [
      "ES2023"
    ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,

    /* Modules */
    "module": "node16" /* Specify what module code is generated. */,

    /* Interop Constraints */
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

    /* Type Checking */
    "strict": true /* Enable all strict type-checking options. */,

    /* Completeness */
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}

ソースコードの実装

HTTP サーバーを起動して、利用ポートのログを出力するコードを実装します。

src/index.ts
import { createServer } from "http";

const server = createServer();

const PORT = process.env.PORT || 3000;

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Node.js 起動スクリプトの追加

package.json に Node.js 起動スクリプトを追加します。

package.json
{
  ...
  "scripts": {
    "start": "ts-node src/index.ts",
    ...
  },
  ...
}

ここまでできたら一度起動確認をします。

npm start

実装したログが出力されます。

image.png

Dockerfile の作成

Node.js は Dockerfile からイメージをビルドしたいので、Dockerfile を作成します。

# Use the official Node.js image
FROM node:20-alpine3.19

# Create and set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Expose the application port
EXPOSE 3000

# Run the application
CMD ["npm", "start"]

compose.yaml の作成

以下の要件を満たす compose.yaml を作成します。

  • Node.js
    • コンテナ名:app-container
    • ビルド:Dockerfile
    • ポート:3000
    • ソースコード:ホストマシンで実装
  • MySQL
    • コンテナ名:db-container
    • ビルド:MySQL 公式イメージのタグ 9.0.0
    • ポート:3306
    • 初期データを用意
    • データはコンテナを削除しても残す
    • ホストマシンからDB接続可能
  • コンテナ間通信可能

サービス・コンテナ名

各コンテナのサービスとコンテナ名を定義します。

compose.yaml
services:
  app:
    container_name: "app-container"
  db:
    container_name: "db-container"

ボリューム

MySQL の以下の要件を満たすため、DBコンテナ用のボリュームを定義します。

  • データはコンテナを削除しても残す

services と同じ階層に定義します。

compose.yaml
services:
  ...
volumes:
  db-volume:

ビルド・イメージ

Appコンテナは Dockerfile から、DBコンテナは公式イメージからビルドするように定義します。

compose.yaml
services:
  app:
    ...
    build: . # Dockerfile path
  db:
    ...
    image: mysql:9.0.0
...

ポート

公開するポートを定義します。

compose.yaml
services:
  app:
    ...
    ports:
      - "3000:3000"
  db:
    ...
    ports:
      - "3306:3306"
...

バインドマウント

Appコンテナのソースコード(後ほど ./src に作成)とDBコンテナの初期データ作成ファイル(以下)をバインドマウントします。

db/init-users.sql
CREATE TABLE users(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) NOT NULL, email VARCHAR(32) NOT NULL);
INSERT users (name, email) VALUES ("Wyatt", "wyatt@example.com");
INSERT users (name, email) VALUES ("Billy", "billy@example.com");
compose.yaml
services:
  app:
    ...
    volumes:
      - type: bind
        source: ./
        target: /app
  db:
    ...
    volumes:
      - type: bind
        source: ./db
        target: /docker-entrypoint-initdb.d
...

初期データ作成ファイルのマウント先の値 /docker-entrypoint-initdb.d は、Docker Hub の MySQL リポジトリの Initializing a fresh instance に記載があります。1

ボリュームマウント

データはコンテナを削除しても残せるようにDBボリュームをDBコンテナにマウントします。

compose.yaml
services:
  ...
  db:
    ...
    volumes:
      ...
      - type: volume
        source: db-volume
        target: /var/lib/mysql
volumes:
  db-volume:

マウント先の /var/lib/mysql は、MySQL サーバーがデータを保存するデフォルトのディレクトリになります。MySQL 設定ファイル(/etc/my.cnf)で定義されています。

docker container run --rm mysql:9.0.0 cat /etc/my.cnf
[mysqld]
...
datadir=/var/lib/mysql
...

環境変数

ホストマシンからのDBコンテナ接続やコンテナ間通信を可能にするため、DB接続情報を環境変数に定義します。

compose.yaml
services:
  app:
    ...
    environment:
      DB_HOST: db
      DB_USER: user
      DB_PASSWORD: userpassword
      DB_NAME: sampledb
  db:
    ...
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: sampledb
      MYSQL_USER: user
      MYSQL_PASSWORD: userpassword

完成

完成した compose.yaml は以下の通りです。

compose.yaml
services:
  app:
    container_name: "app-container"
    build: . # Dockerfile path
    ports:
      - "3000:3000"
    volumes:
      - type: bind
        source: ./
        target: /app
    environment:
      DB_HOST: db
      DB_USER: user
      DB_PASSWORD: userpassword
      DB_NAME: sampledb
  db:
    container_name: "db-container"
    image: mysql:9.0.0
    ports:
      - "3306:3306"
    volumes:
      - type: bind
        source: ./db
        target: /docker-entrypoint-initdb.d
      - type: volume
        source: db-volume
        target: /var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: sampledb
      MYSQL_USER: user
      MYSQL_PASSWORD: userpassword
volumes:
  db-volume:

動作確認

一度動作確認をします。

docker compose up --build

app-container に実装したログが出力されます。

image.png

コンテナ一覧を確認します。

docker container ls

compose.yaml で定義した名前通りのコンテナが作成されています。

image.png

この時点では、AppコンテナとDBコンテナがそれぞれ起動しているだけで、コンテナ間の通信は発生していません。

DB接続処理の実装

AppコンテナからDBコンテナに接続する処理を実装します。
まず MySQL クライアントとして MySQL2 をインストールします。

npm install mysql2

DB接続処理を実装します。先ほどインストールした MySQL2 の createConnection を利用して、DBへ接続する関数を実装します。

src/database.ts
import mysql from "mysql2/promise";

export const createConnection = async () => {
  try {
    const connection = await mysql.createConnection({
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
    });
    console.log("Connected to database");
    return connection;
  } catch (error) {
    console.error("Failed to connecting to database", JSON.stringify(error));
    process.exit(1);
  }
};

DBへ接続する関数を src/index.ts で呼び出します。

src.index.ts
import { createServer } from "http";
import { createConnection } from "./database";

const server = createServer();

const PORT = process.env.PORT || 3000;

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
  createConnection(); // Add
});

動作確認

最後に動作確認をします。
まず、コンテナを起動します。

docker compose up --build

DB接続のログも出力されます。

image.png

MySQL に接続します。

mysql --host=127.0.0.1 --port=3306 --user=root --password=rootpassword sampledb

初期データとして追加ユーザーが確認できます。

mysql> select * from users;
+----+--------+--------------------+
| id | name   | email              |
+----+--------+--------------------+
|  1 | Wyatt  | wyatt@example.com  |
|  2 | Billy  | billy@example.com  |
+----+--------+--------------------+
4 rows in set (0.01 sec)

参考

関連

  1. https://hub.docker.com/_/mysql

1
1
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
1