【Docker】docker-entrypoint-initdb.dに初期化のsqlを置いたけど実行してくれない

# 対象読者
Docker ComposeでMySQLコンテナを立ち上げて、初期データを投入しようとしてるけど、なぜか初期データが全然入らない！と困っている人向け

# 結論

私の場合は、コンテナのみ削除していて、Dockerボリュームの削除をし忘れていたことが原因でした。。
これを機にちょっとDockerボリュームについて学び直してみようと思い、このブログを書きました！

# ボリュームのマウントの方法について

ボリュームのマウントタイプは三つあります。
- Volumes
- bind mounts
- tmpfs mounts

![スクリーンショット 2024-11-24 10.14.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1611984/cd72ac85-486c-8fc3-71bd-c18e8d6315f6.png)

参考）[適切なマウントタイプを選択する](https://docs.docker.com/engine/storage/#choose-the-right-type-of-mount)



## ボリュームのマウントタイプ①：Volumes

- データ永続化や管理が簡単
- コンテナを削除してもデータは残る
- Docker（Linuxでは/ var / lib / docker / volumes /）によって管理されるホストファイルシステムの一部に保存される
- 複数のコンテナ間で共有可能
- ボリュームをマウントする場合、ボリュームは名前付きか匿名で作成することができる

:::note warn
匿名ボリュームは、管理が大変そう。。
匿名ボリュームは、名前が指定されていないボリュームで、Dockerが毎回ランダムな名前を生成します。この特性により、コンテナの終了後にそのボリュームを手動で削除しないと、システムに不要なボリュームが溜まってしまうリスクがあります。
また、匿名ボリュームは特定の名前で管理されないため、他のコンテナと共有したり再利用したりするのが難しく、結果として運用管理が手間になります。
:::

## ボリュームのマウントタイプ②：bind mounts

- ホストの特定のディレクトリをコンテナにマウントする方法で、開発時やデバッグ時に便利

:::note alert
ホストのファイルシステム変更・削除のリスク
- デフォルトの書き込みアクセス: 
    - バインドマウントでは、デフォルトでホスト上のファイルやディレクトリに対してコンテナ内のプロセスが自由に書き込みできます。これにより、意図せずホスト上の重要なファイルが削除されたり、改ざんされたりする危険性があります。
- ユーザーパーミッションの問題: 
    - バインドマウントを使用すると、ホストとコンテナ間で同じユーザー権限が適用されるため、ホスト上のファイルを操作する際に、特定のプロセスが意図しない影響を及ぼす可能性があります。
:::

:::note info
読み取り専用（Read-Only）モードを活用する
バインドマウントの設定時に「読み取り専用モード（ro）」を指定することで、コンテナからホスト上のファイルの変更を防ぐことができます。
```
volumes:
  - /host/path:/container/path:ro
```
:::


## ボリュームのマウントタイプ③：tmpfs mounts

- 一時的なデータを保存するために、メモリ上にボリュームを作成
- 一時的なデータ保存に最適
- コンテナの存続期間中、非永続的な状態や機密情報を保存


# 今回の調査の流れ

`docker-compose.yaml`で名前付きボリューム`db_volume_common`をマウントし、`./db/initdb.d/01_create_database.sql`で`/var/lib/mysql`に必要なシステムデータベース（paku）が作成した
↓
その後、データベース（paku）の中にテーブル（test）を作成したいと思い、`./db/initdb.d/02_create_tables.sql`ファイルを追加し、コンテナを削除してから再度コンテナを立ち上げたが、テーブルが削除されない
↓
どうしてなんだ？と困る
↓
docker-entrypoint-initdb.dの中に`./db/initdb.d/02_create_tables.sql`は格納されていることも一応確認
↓
ログを見ると実行されていないことが分かった
↓
どうして実行されなかったか調査すると、ボリュームの中が初期状態じゃないとinitdbが実行されないことに気がつく
↓
docker compose downを打つときに、オプション-vをつけて実行しボリュームを削除したのち、再度コンテナを立ち上げるとテーブル作成されていた
↓
なるほど！

# 実際に使用したファイルと修正点


```yaml:./docker-compose.yaml
version: "3.8"

services:
  # DBコンテナ
  db:
    platform: linux/x86_64
    image: mysql:8.0.34
    container_name: db
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "23306:3306"
    volumes:
      - ./db/initdb.d:/docker-entrypoint-initdb.d
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
      - db_volume_common:/var/lib/mysql

volumes:
  db_volume_common: null
```

```./db/initdb.d/01_create_database.sql
CREATE DATABASE IF NOT EXISTS `paku` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
```

```./db/initdb.d/02_create_tables.sql
USE `paku`;

CREATE TABLE `test` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL,
  `age` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
```

```diff_make:./Makefile
setup:
	@make build
	@make up
	@make ps
d:
-	docker compose down
+   docker compose down -v
build:
	docker compose build
up:
	docker compose up -d
ps:
	docker compose ps
db:
	docker compose exec db bash
app:
	docker compose exec app bash
```

# まとめ

- コンテナ削除だけじゃダメ！ ボリュームも消さないと初期化されない。
- 匿名ボリュームは溜まると管理地獄。名前付きボリュームを使おう。
- bind mountsの操作ミスには注意！ ホスト側のファイルも変えちゃう危険あり。
- 「なんで初期データが入らないの？」ってなったら、ボリューム削除をしているか確認してみる。