Dockerを利用して自分だけのDB環境をつくってみた

## はじめに

あんなテーブル定義を作ってみたい、こんなクエリを試してみたい…！  
そんなアツい想いから、dockerを使って自分だけのDB(Mysql)環境をたててみました。  
何番煎じかわかりませんが、本記事ではDockerfileやcompose.yamlの書き方から初期データの投入までを記載します。

## 前提

- Dockerがインストールされている
- docker-composeがインストールされている
- 基本的なSQLがわかる

## ディレクトリ構成

最終的なディレクトリ構成はこのようになります。

```
mydb
├── Dockerfile
├── compose.yaml
└─┬ init
  ├── 01_create_databases.sql
  ├── 02_create_tables.sql
  └── 03_insert_my_mdb.sql
```

## Dockerfileの作成

わかりやすいように専用のディレクトリをきって、Dockerfileを作成します。

```
$ mkdir mydb
$ cd mydb
$ touch Dockerfile
$ ls -1
Dockerfile
```

Dockerfileは以下のように記述しました。  

```
FROM mysql:8.0-debian

# set timezone
ENV TZ=Asia/Tokyo

# set locale
RUN apt-get update
RUN apt-get install -y locales
RUN echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen
ENV LANG=ja_JP.UTF-8
```

## compose.yamlの作成

続いて、compose.yamlを作成します。

```
$ touch compose.yaml
$ ls -1
Dockerfile
compose.yaml
```

compose.yamlの中身を記述します。  
ローカルで自分だけで遊ぶくらいなら以下のような構成で十分じゃないかと思います。  

```
services:

  db:
    container_name: mydb
    build: .
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
```

## イメージのビルド・コンテナの起動

以下のコマンドでイメージのビルドとコンテナの作成・起動を行います。

```
$ docker compose -p mydb up --build -d                            
```

起動ができたら、コンテナ一覧を確認してみましょう。

```
$ docker container ls
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS         PORTS                 NAMES
2ed39f82b62c   mysql:8.3.0   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   3306/tcp, 33060/tcp   mydb
```

コンテナが作成されていますね！  
早速DBにアクセスしてみましょう。

```
$ docker container exec -it mydb mysql -uroot -prootpass
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.3.0 MySQL Community Server - GPL

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

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

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)
```

アクセスできました！(CLIでパスワードを設定しているのはセキュアじゃないよって怒られていますが、本記事の趣旨とは異なるため今回は無視します)  
しかし、今のままだとデフォルトで作成されたデータベースがあるのみで、まだ自分が遊ぶ用のデータベースが用意されていません。 

## 初期データを準備する

mysqlのdockerhubを確認すると、下のような記述があります。

> When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions `.sh`, `.sql` and `.sql.gz` that are found in `/docker-entrypoint-initdb.d.` Files will be executed in alphabetical order.

コンテナ起動時に、`/docker-entrypoint-initdb.d.`に格納された拡張子`.sh`, `.sql`, `.sql.gz`のファイルが実行されるそうです。  
つまり、ローカルに初期データ投入用の`.sql`ファイルが格納されたディレクトリを用意し、`/docker-entrypoint-initdb.d.`にマウントすれば、初期データの投入ができそうです。

まず、初期データ投入用の`.sql`ファイルを格納するディレクトリを作成します。

```
$ mkdir init
$ ls -1
Dockerfile
compose.yaml
init
```

`init`ディレクトリ内に、初期データの登録を記述した`.sql`ファイルを配置します。  
今回はこのようにファイルを用意しました。

```
$ ls -1 ./init                                            
01_create_databases.sql
02_create_tables.sql
03_insert_my_mdb.sql
```

中身はこんな感じにしてみました。

```
$ cat ./init/01_create_databases.sql 
CREATE DATABASE `my_mdb` /*DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */; 

$ cat ./init/02_create_tables.sql         
SET SESSION FOREIGN_KEY_CHECKS=0;

/* Drop Tables */

DROP TABLE IF EXISTS my_mdb.m_spot;
DROP TABLE IF EXISTS my_mdb.m_region;




/* Create Tables */

-- M地方
CREATE TABLE my_mdb.m_region
(
  -- 地方ID
  region_id bigint NOT NULL COMMENT '地方ID',
  -- 地方の名称
  region_name text NOT NULL COMMENT '地方名',
  -- 登録された日時
  created_at datetime NOT NULL COMMENT '登録日時',
  -- 更新された日時
  updated_at datetime NOT NULL COMMENT '更新一時',
  PRIMARY KEY (region_id)
) COMMENT = 'M地方';


$ cat ./init/03_insert_my_mdb.sql 
INSERT INTO my_mdb.m_region (region_id, region_name, created_at, updated_at) VALUES 
 (1, 'カントー地方', NOW(), NOW()),
 (2, 'ジョウト地方', NOW(), NOW()),
 (3, 'ホウエン地方', NOW(), NOW()),
 (4, 'シンオウ地方', NOW(), NOW());

```

好きなゲームをもとに作ってみました、へへ。

先ほど作成したcompose.yamlに、`/docker-entrypoint-initdb.d.`と`init`ディレクトリをマウントする旨を記述をします。

```
services:

  db:
    container_name: mydb
    build: .
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
    volumes:
      - ./init:/docker-entrypoint-initdb.d
```

`docker-entrypoint-initdb.d`内の.`sql`ファイル等は、コンテナの初回起動時のみ実行されるため、一旦いま起動しているコンテナを破棄します。

```
$ docker compose -p mydb down                           
[+] Running 2/1
 ⠿ Container mydb        Removed                                                                                                                                                                                                       1.0s
 ⠿ Network mydb_default  Removed                                                                                                                                                                                                       0.1s
```

再度`docker compose up --build -d`を叩きます。  
このときのログを確認してみましょう。

```
$ docker logs mydb

(中略)

2024-04-06 13:20:49+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/01_create_databases.sql


2024-04-06 13:20:49+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/02_create_tables.sql


2024-04-06 13:20:49+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/03_insert_my_mdb.sql
```

`init`下に配置した.`sql`ファイルが実行されているのが確認できました。  
コンテナが起動できたら、DBに接続してデータベースやテーブルが作成されているか、初期データが投入されているか等、確認してみましょう。

```
$ docker container exec -it mydb mysql -uroot -prootpass
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.3.0 MySQL Community Server - GPL

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

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

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| my_mdb             |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

mysql> USE my_mdb
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW TABLES;
+------------------+
| Tables_in_my_mdb |
+------------------+
| m_region         |
+------------------+
1 rows in set (0.01 sec)

mysql> SELECT * FROM m_region;
+-----------+--------------------+---------------------+---------------------+
| region_id | region_name        | created_at          | updated_at          |
+-----------+--------------------+---------------------+---------------------+
|         1 | カントー地方         | 2024-04-06 13:20:49 | 2024-04-06 13:20:49 |
|         2 | ジョウト地方         | 2024-04-06 13:20:49 | 2024-04-06 13:20:49 |
|         3 | ホウエン地方         | 2024-04-06 13:20:49 | 2024-04-06 13:20:49 |
|         4 | シンオウ地方         | 2024-04-06 13:20:49 | 2024-04-06 13:20:49 |
+-----------+--------------------+---------------------+---------------------+
4 rows in set (0.00 sec)
```

初期データが投入されていますね、やったー！

## さいごに

自分だけのDBが作れました！  
これで、あんなテーブル定義やこんなクエリを試したい放題ですね。  
細かい設定をしたい方は、`conf.d`ディレクトリ(仮名)を作成して`my.cnf`を記述して配置し、`/etc/mysql/conf.d`にマウントするなどしてもいいかもしれないですね！