ローカル環境でDockerコンテナ上にDBを構築して接続する

## 環境

- MacBook Pro M3
- macOS Sonoma
- Rancher Desktop 1.14.0

## 方法

ローカル環境でDockerコンテナ上にDBを構築する方法を説明します。DBMSはMySQLを使用します。
構築時にはDBに初期データを投入します。また、コンテナ上のDBに接続して、初期データが登録できているか確認します。
最後に、作成したコンテナとボリューム、自動で作成されたデフォルトネットワークを削除します。

### コンテナ上にDBを構築する方法

以下の各ファイルをコピペして用意してください。

```
explain-db
├── docker-compose.yaml  # コンテナの設定を記載します。
└── initdb.d
    ├── 1-schema.sql  # DBのテーブル定義を記載します。
    └── 2-data.sql  # 初期データをDBに投入するSQL文を記載します。
```

```docker-compose.yaml
services:
  db:
    container_name: test-container
    image: mysql:8
    volumes:
      - ./initdb.d:/docker-entrypoint-initdb.d
      - test-database-volume:/var/lib/mysql
    environment:
      MYSQL_DATABASE: test-database
      MYSQL_ROOT_PASSWORD: root
      TZ: Asia/Tokyo
    ports:
      - "3306:3306"

volumes:
  test-database-volume:
```

```1-schema.sql
USE test-database;

CREATE TABLE organization_t (
    organization_id   MEDIUMINT   NOT NULL AUTO_INCREMENT,
    organization_name VARCHAR(64) NOT NULL,
    register_time     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    update_time       DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (organization_id)
);
```

```2-data.sql
INSERT INTO organization_t (organization_name)
VALUES
    ('Google'),
    ('Microsoft');
```

以下のコマンドを実行して、コンテナを起動します。

```
$ docker compose up -d

[+] Running 3/3
 ✔ Network explain-db_default                Created                                                      0.1s
 ✔ Volume "explain-db_test-database-volume"  Created                                                      0.0s
 ✔ Container test-container                  Started                                                      0.3s
```

:::note
`-d`（または`--detach`）オプションはバックグラウンドでコンテナを起動します。
:::

以下のコマンドを実行すると、起動中のコンテナの一覧を確認できます。

```
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
68e5a2024cae   mysql:8   "docker-entrypoint.s…"   5 seconds ago   Up 3 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   test-container
```

以下のコマンドを実行すると、ボリュームの一覧を確認できます。

```
$ docker volume ls
DRIVER    VOLUME NAME
local     explain-db_test-database-volume
```

これでDBの構築は完了です。

次にコンテナ上のDBに接続してSQL文を実行し、初期データが登録できているか確認します。

### コンテナ上のDBに接続してSQL文を実行する方法

以下のコマンドを実行して、コンテナ内にbashシェルを起動し、現在のターミナルウィンドウに入力されるコマンドが、コンテナ内のbashシェルに渡されて実行されるようにします。

```
$ docker exec -it test-container bash
```

以下のコマンドを実行して、DBに接続します。

```
bash-5.1# mysql -u root -proot
```

以下の２つのコマンドを実行して、初期データが登録できているか確認します。

```
mysql> use test-database;
Database changed

mysql> select * from organization_t;
+-----------------+-------------------+---------------------+---------------------+
| organization_id | organization_name | register_time       | update_time         |
+-----------------+-------------------+---------------------+---------------------+
|               1 | Google            | 2024-07-13 11:43:44 | 2024-07-13 11:43:44 |
|               2 | Microsoft         | 2024-07-13 11:43:44 | 2024-07-13 11:43:44 |
+-----------------+-------------------+---------------------+---------------------+
2 rows in set (0.00 sec)
```

### コンテナとボリュームを削除する方法

以下のコマンドを実行して、作成したコンテナとボリューム、自動で作成されたデフォルトネットワークを削除します。

```
$ docker compose down -v

[+] Running 3/3
 ✔ Container test-container                Removed                                                        1.9s
 ✔ Volume explain-db_test-database-volume  Removed                                                        0.0s
 ✔ Network explain-db_default              Removed                                                        0.5s
```

:::note
`-v`（または`--volumes`）オプションはボリュームを一緒に削除します。
:::

以下のコマンドを実行して、起動中あるいは停止中のコンテナが存在しないことを確認します。

```
$ docker ps -a

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
```

:::note
`-a`（または`--all`）オプションは停止中のコンテナも含めて全てのコンテナを表示します。
:::

以下のコマンドを実行して、ボリュームが存在しないことを確認します。

```
$ docker volume ls

DRIVER    VOLUME NAME
```

## トラブルシュート

エラーが出力される場合や記述通りの実行結果が得られない場合は以下を参考にしてください。

### 「`port is already allocated`」というエラー文が表示される場合

```
$ docker compose up -d

Error response from daemon: driver failed programming external connectivity on endpoint test-container (f6205f9951d14670ecf150984863cbacda7d598a39e5d95ecdd33557eedfd149): Bind for 0.0.0.0:3306 failed: port is already allocated
```

`docker-compose.yaml`でホストマシンの`3306`ポートを指定しましたが、既にそのポートを他のプロセスが占有している（つまり、そのポートで他のプログラムが実行されている）際に起きるエラーです。

このエラーは以下のどちらかを行うと解決できます。

#### 解決方法(1) `docker-compose.yaml`で指定したポート番号を変更する

`ports`の`:`の左側の番号を別の番号に変更します。

:::note warn
- 0番から1023番は使用しないでください
- 変更後のポート番号も、また別のプロセスと競合する可能性があります
:::

```docker-compose.yaml
ports:
  - "3330:3306" # {ホストマシンのポート}:{コンテナのポート}の形式
```

#### 解決方法(2) ホストマシンの`3306`ポートを占有するプロセスを強制終了させる

まず、以下のコマンドを実行して、`3306`ポートを占有しているプロセスを調べます。

```
$ lsof -i :3306

COMMAND  PID       USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ssh     2063 xxxxxx-xxx   35u  IPv4 xxxxxxxxxxxxxxxxxx      0t0  TCP *:mysql (LISTEN)
```

次に、上記で調べたプロセスのPIDを指定して、プロセスを強制終了させます。

```
kill -9 2063
```