はじめに
この記事では、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
が作成されます。
{
"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
にインストールしたパッケージが表示されます。
{
...
"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
) で利用する設定に変更します。
{
"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 サーバーを起動して、利用ポートのログを出力するコードを実装します。
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 起動スクリプトを追加します。
{
...
"scripts": {
"start": "ts-node src/index.ts",
...
},
...
}
ここまでできたら一度起動確認をします。
npm start
実装したログが出力されます。
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接続可能
- コンテナ名:
- コンテナ間通信可能
サービス・コンテナ名
各コンテナのサービスとコンテナ名を定義します。
services:
app:
container_name: "app-container"
db:
container_name: "db-container"
ボリューム
MySQL の以下の要件を満たすため、DBコンテナ用のボリュームを定義します。
- データはコンテナを削除しても残す
services
と同じ階層に定義します。
services:
...
volumes:
db-volume:
ビルド・イメージ
Appコンテナは Dockerfile から、DBコンテナは公式イメージからビルドするように定義します。
services:
app:
...
build: . # Dockerfile path
db:
...
image: mysql:9.0.0
...
ポート
公開するポートを定義します。
services:
app:
...
ports:
- "3000:3000"
db:
...
ports:
- "3306:3306"
...
バインドマウント
Appコンテナのソースコード(後ほど ./src
に作成)とDBコンテナの初期データ作成ファイル(以下)をバインドマウントします。
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");
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コンテナにマウントします。
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接続情報を環境変数に定義します。
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
は以下の通りです。
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
に実装したログが出力されます。
コンテナ一覧を確認します。
docker container ls
compose.yaml
で定義した名前通りのコンテナが作成されています。
この時点では、AppコンテナとDBコンテナがそれぞれ起動しているだけで、コンテナ間の通信は発生していません。
DB接続処理の実装
AppコンテナからDBコンテナに接続する処理を実装します。
まず MySQL クライアントとして MySQL2 をインストールします。
npm install mysql2
DB接続処理を実装します。先ほどインストールした MySQL2 の createConnection
を利用して、DBへ接続する関数を実装します。
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
で呼び出します。
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接続のログも出力されます。
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)
参考
関連