LoginSignup
160
127

More than 5 years have passed since last update.

docker-composeでmysql使うとき初回起動時に複数のDBを作る方法

Posted at

とりあえず自分が慣れてるんで docker-compose のymlの書式で書きますが、コマンドラインからでも同じことはできるはずでやんす。

tl; dr

  • MYSQL_DATABASE をenvに指定すれば1個は作ってくれるけど、複数個のDBは作ってくれない
  • /docker-entrypoint-initdb.d にあるSQLは勝手に突っ込んでくれるしシェルがあればシェルを実行してくれる
  • /docker-entrypoint-initdb.d にいい感じのシェルを書くといい感じにDBが作れるというか、なんでもできる。

やりたいこと

以下のような docker-compose.yml を用意して、2つのDBを作りたいと思っています。

docker-compose.yml
version: '2'
services:
  db:
    image: mysql
    environment:
      MYSQL_DATABASE: first_database
      MYSQL_USER: username
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:
    driver: local

この段階で first_database というDBは1つ作られてますが、
同じコンテナの中に second_database というDBを作りつつ、
first_databasesecond_database それぞれにダンプを投げつけたい、
というのが今回の目的になります。

DockerのMySQLの初期化処理

「DockerのMySQL」って言葉は多分に語弊があるんですが、いわゆるオフィシャルのMySQLのことを指してます。

基本的には MYSQL_DATABASE であったり MYSQL_USER といったenvを指定して起動することで、初回起動時にそこで指定したDBやユーザーを作ってくれます。

多くのケースでは1つのDBで十分でしょうし、
複数のDBを使いたい場合は、そもそも別のDockerのMySQLインスタンス立てればいいですので、
この機能だけでだいたい大丈夫だと思います。

が、どうしても、どうしても1つのホストに複数のDBを作りたいねん・・・といったことには、このenvの設定だけでは実現できません。

また、CREATE DATABASE hoge だけじゃなくて、テーブル群も作ってほしい!ということにも、これらenvは対応していません。

Initializing a fresh instance

では、どうするか、ということで 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.

初回のコンテナの起動時に、指定された名前で新しいデータベースが作成されたり、設定された値で初期化処理が行われます。
さらに、 /docker-entrypoint-initdb.d にある .sh .sql .sql.gz ファイルが、ファイル名のアルファベット順で実行されます。

というわけで、 /docker-entrypoint-initdb.d にSQLファイルを置いたりすればいい感じにできます。
が、SQLファイルでは、 MYSQL_DATABASE に指定したDBに対してダンプファイルを流すだけですので、他のDBを作ったりすることはできません。

シェルを配置する

というわけで、以下のようなシェルを用意します。

001_second_database.sh
#!/bin/sh

echo "CREATE DATABASE IF NOT EXISTS \`second_database\` ;" | "${mysql[@]}"
echo "GRANT ALL ON \`second_database\`.* TO '"$MYSQL_USER"'@'%' ;" | "${mysql[@]}"
echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"

"${mysql[@]}" < /docker-entrypoint-initdb.d/second_database.sql_

${mysql[@]} みたいなよくわからない変数がでてきたりしてカオスですが、
1行目で second_database を作成し、
2行目で second_database に対して MYSQL_USER env で指定したユーザーに権限を与え
3行目で FLUSH PRIVILEGES し、
5行目で second_database に対してダンプしたSQLである second_database.sql_ を流し込んでる感じです。

ファイル構成

まだ、若干イメージついてないところがあると思いますので、ファイル構成を見てみましょう。

.
├── docker-compose.yml
└── sql
    ├── 000_first_database.sql
    ├── 001_second_database.sh
    └── second_database.sql_

ポイントは、 second_database.sql_ の末尾が _ で終わってることです。
_ じゃなくてもいいのですが、とにかく .sql .sh で終わらなければOKです。

この辺のハンドリング行ってるシェルの処理を見たほうが理解は早いと思います。

引用: https://github.com/infosiftr/mysql/blob/dbb83a94ee0a994651dd430d27caeac652cf9e78/5.7/docker-entrypoint.sh#L108-L116

for f in /docker-entrypoint-initdb.d/*; do
  case "$f" in
    *.sh)     echo "$0: running $f"; . "$f" ;;
    *.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
    *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
    *)        echo "$0: ignoring $f" ;;
  esac
  echo
done

こんな感じで処理されてるので、ファイル名の末尾に気をつければ、無視されるので、 .sql_ のように適当な文字列で終わらせてます。

(閑話休題)

で、最終的には以下のようになります。

docker-compose.yml
version: '2'
services:
  db:
    image: mysql
    environment:
      MYSQL_DATABASE: first_database
      MYSQL_USER: username
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
      - db-data:/var/lib/mysql

volumes:
  db-data:
    driver: local

volumes./sql のマウントの記述を追記しました。
これで初回起動時に、 ./sql以下のSQLやシェルが実行され、やりたいことを実現できました。やったぜ。

また、これは「初回起動時」限定の動作なので、すでに起動しちゃっていて配布するのでなければ、直接ダンプを流し込めばいいと思います。
配布用にバチッっと初回処理として作りたいのであれば、とりあえずボリュームを消したあとに起動すれば、./sql 内のSQLやシェルが動いているのがわかると思います。

160
127
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
160
127