mysqlのコンテナに初期データを流し込む

ゆるっと Advent Calendar 2020 13日目です。今回はmysqlのコンテナに初期データを流し込む方法について紹介します。

概要

開発環境を構築するためにDBのテーブルや初期データを配置したい場合があると思います。コンテナ起動後にデータを流し込む方法がありますがなるべく手間が無く構築できる方法がないか探しました。今回はmysqlについて探したところInitializing a fresh instanceの機能がありました。

MysqlのDockerhubのドキュメントに書かれているInitializing a fresh instanceの項目を引用すると

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. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.

つまり/docker-entrypoint-initdb.d ディレクトリに.sh, .sql, .sql.gzの拡張子のファイルを配置すると対象のファイルを読み込み、実行してくれます。実際に試してみます。

使用環境

  • macOS 11.0.1
  • docker 20.10.0
  • docker-compose 1.27.4
  • mysql 8.0

実行例

下記のdocker-compose.ymlを用意します。

docker-compose.yml

version: '3'
services:
  db:
    image: mysql:8.0
    ports:
      - 3306:3306
    environment:
      TZ: Asia/Tokyo
      MYSQL_ROOT_PASSWORD: root
    volumes:
        - ./mysql/:/docker-entrypoint-initdb.d/
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

次に初期データを流し込むために下記のsqlファイルを用意します。

sample.sql

CREATE DATABASE IF NOT EXISTS sample;
USE sample;

CREATE TABLE IF NOT EXISTS sample_table
(
  `id`         int(11) NOT NULL AUTO_INCREMENT,
  `sample`     text,
  PRIMARY KEY (`id`)
);

INSERT INTO sample_table (sample) VALUES ("sample1");
INSERT INTO sample_table (sample) VALUES ("sample2");

最終的に下記のディレクトリ構成でdocker-compose upを行いコンテナを生成、実行してみます。

./
├── mysql
│   └── sample.sql
├── docker-compose.yml

コンテナ起動後にdatabaseを確認すると無事テーブルと初期データが作成されていることがわかります。

mysql> select * from sample_table;
+----+---------+
| id | sample  |
+----+---------+
|  1 | sample1 |
|  2 | sample2 |
+----+---------+
2 rows in set (0.01 sec)

ではどのような仕組みで実行されているのかDockerfileの中身をみてみようと思います。

仕組み

対象のリポジトリは下記になります。

github.com

対象のDockerfileを確認するとentrypointがdocker-entrypoint.shに設定されています。docker-entrypoint.shの中身を見てみると _main関数が初めに実行されることがわかります。関数内でdocker_temp_server_startが呼ばれてmysqlサーバが起動されているので以降の処理を追ってみます。docker_process_init_files関数で/docker-entrypoint-initdb.d/配下のファイルが渡されているので、この処理でsqlファイルの実行が行われていそうな気がします。

対象の関数内の中身をみると各拡張子ごとに処理を分けている部分があります。その中でdocker_process_sql関数が実行されmysqlコマンドが実行されているのでこの関数内でSQLが実行されていることがわかります。

まとめ

コンテナ作成時に初期データを流し込めるのは便利だと思いました。実装に関しても特に難しい処理は行われていないので他のDBにも流用できると思いました。