対象読者
Docker ComposeでMySQLコンテナを立ち上げて、初期データを投入しようとしてるけど、なぜか初期データが全然入らない!と困っている人向け
結論
私の場合は、コンテナのみ削除していて、Dockerボリュームの削除をし忘れていたことが原因でした。。
これを機にちょっとDockerボリュームについて学び直してみようと思い、このブログを書きました!
ボリュームのマウントの方法について
ボリュームのマウントタイプは三つあります。
- Volumes
- bind mounts
- tmpfs mounts
ボリュームのマウントタイプ①:Volumes
- データ永続化や管理が簡単
- コンテナを削除してもデータは残る
- Docker(Linuxでは/ var / lib / docker / volumes /)によって管理されるホストファイルシステムの一部に保存される
- 複数のコンテナ間で共有可能
- ボリュームをマウントする場合、ボリュームは名前付きか匿名で作成することができる
匿名ボリュームは、管理が大変そう。。
匿名ボリュームは、名前が指定されていないボリュームで、Dockerが毎回ランダムな名前を生成します。この特性により、コンテナの終了後にそのボリュームを手動で削除しないと、システムに不要なボリュームが溜まってしまうリスクがあります。
また、匿名ボリュームは特定の名前で管理されないため、他のコンテナと共有したり再利用したりするのが難しく、結果として運用管理が手間になります。
ボリュームのマウントタイプ②:bind mounts
- ホストの特定のディレクトリをコンテナにマウントする方法で、開発時やデバッグ時に便利
ホストのファイルシステム変更・削除のリスク
- デフォルトの書き込みアクセス:
- バインドマウントでは、デフォルトでホスト上のファイルやディレクトリに対してコンテナ内のプロセスが自由に書き込みできます。これにより、意図せずホスト上の重要なファイルが削除されたり、改ざんされたりする危険性があります。
- ユーザーパーミッションの問題:
- バインドマウントを使用すると、ホストとコンテナ間で同じユーザー権限が適用されるため、ホスト上のファイルを操作する際に、特定のプロセスが意図しない影響を及ぼす可能性があります。
読み取り専用(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をつけて実行しボリュームを削除したのち、再度コンテナを立ち上げるとテーブル作成されていた
↓
なるほど!
実際に使用したファイルと修正点
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
CREATE DATABASE IF NOT EXISTS `paku` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
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;
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の操作ミスには注意! ホスト側のファイルも変えちゃう危険あり。
- 「なんで初期データが入らないの?」ってなったら、ボリューム削除をしているか確認してみる。