doilux’s tech blog

ITに関する備忘録。 DDP : http://doiluxng.hatenablog.com/entry/2018/01/01/195409

centos7 + mysql5.7のDockerFileを一部解読する

https://hub.docker.com/r/centos/mysql-57-centos7/

このイメージを使うと一つのDB、ユーザーは簡単に作れるけど、複数のDBを作りたいときにどうすればいいかわからなかったので、コンテナを起動したときに何が起きているのかをDockerFileから追ってみました。

まずはDockerFile github.com

※本記事の内容に関係するところだけ抜粋

...
ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/mysql \
    MYSQL_PREFIX=/opt/rh/rh-mysql57/root/usr \
    ENABLED_COLLECTIONS=rh-mysql57
...
ENTRYPOINT ["container-entrypoint"]
CMD ["run-mysqld"]

ENTRYPOINTとCMDを併用すると、ENTRYPOINTの引数にCMDが渡るので、ここではこんなコマンドが実行されます。

bash-4.2$ container-entrypoint run-mysqld

container-entrypointの中は以下のようになっています。

#!/bin/bash
exec "$@"

とどのつまり、exec run-mysqldが実行されるわけです。

※本記事の内容に関係するところだけ抜粋

#!/bin/bash

...
source ${CONTAINER_SCRIPTS_PATH}/common.sh
...

# pre-init files
process_extending_files ${APP_DATA}/mysql-pre-init/ ${CONTAINER_SCRIPTS_PATH}/pre-init/

if [ ! -d "$MYSQL_DATADIR/mysql" ]; then
  initialize_database "$@"
else
  start_local_mysql "$@"
fi

# init files
process_extending_files ${APP_DATA}/mysql-init/ ${CONTAINER_SCRIPTS_PATH}/init/

# Restart the MySQL server with public IP bindings
shutdown_local_mysql
...
exec ${MYSQL_PREFIX}/libexec/mysqld --defaults-file=$MYSQL_DEFAULTS_FILE "$@" 2>&1

最初みたときにprocess_extending_filesinitialize_databaseがどこに定義されているんだ?と思いましたが、${CONTAINER_SCRIPTS_PATH}/common.shに定義されていました。長くなるので割愛しますが、initialize_databaseでその名の通り、環境変数に基づいてDBの初期化(CREATE USER, CREATE DATABASE, GRANT...)をやってました。

気になったのがinitialize_databaseの後に書かれているprocess_extending_filesの部分。追加のDB作成などはここにここで呼び出されるようにすればいいんじゃないかと予想。

process_extending_filesはこうなってます。

# process_extending_files process extending files in $1 and $2 directories
# - source all *.sh files
#   (if there are files with same name source only file from $1)
function process_extending_files() {
  local custom_dir default_dir
  custom_dir=$1
  default_dir=$2

  while read filename ; do
    echo "=> sourcing $filename ..."
    # Custom file is prefered
    if [ -f $custom_dir/$filename ]; then
      source $custom_dir/$filename
    else
      source $default_dir/$filename
    fi
  done <<<"$(get_matched_files "$custom_dir" "$default_dir" '*.sh' | sort -u)"
}

つまり、process_extending_files ${APP_DATA}/mysql-init/ ${CONTAINER_SCRIPTS_PATH}/init/は、例えば50-passwd-change.shというファイルを${APP_DATA}/mysql-init/に置けばそれが実行され、なければ${CONTAINER_SCRIPTS_PATH}/init/にある同名のスクリプトが実行されます(ちなみに初期状態で${CONTAINER_SCRIPTS_PATH}/init/には50-passwd-change.shというファイルのみあります)

ということはつまり、${APP_DATA}/mysql-init/にシェルを置けば、DB初期化後に処理を追加できそうです。 なお、APP_DATAは/opt/app-root/srcです。

bash-4.2$ env | grep APP_DATA
APP_DATA=/opt/app-root/src

ということで以下のようなDockerFileを作ってみました。

FROM centos/mysql-57-centos7:latest

MAINTAINER doilux

# テストでしか使わないのと、面倒なのでrootのパスワードなどを適当に設定。
# 本番で使う場合はちゃんとしよう。
ENV MYSQL_ROOT_PASSWORD=password \
        MYSQL_DATABASE=doilux \
        MYSQL_PASSWORD=doilux \
        MYSQL_USER=doilux

# sqlディレクトリ配下のスクリプトを/opt/app-root/src/mysql-init配下にコピーする
COPY sql/*sh /opt/app-root/src/mysql-init/

# MySQLの初期化および起動
CMD ["run-mysqld"]
#!/bin/bash

mysql -u root --socket=/tmp/mysql.sock << EOF
CREATE DATABASE hr;
CREATE DATABASE sales;
CREATE DATABASE account;

CREATE USER 'ceo'@'%' IDENTIFIED BY 'ceo';

GRANT ALL ON hr.* TO 'ceo'@'%' IDENTIFIED BY 'ceo';
GRANT ALL ON sales.* TO 'ceo'@'%' IDENTIFIED BY 'ceo';
GRANT ALL ON account.* TO 'ceo'@'%' IDENTIFIED BY 'ceo';
EOF
#!/bin/bash

mysql -u ceo --socket=/tmp/mysql.sock hr << EOF
CREATE TABLE emp(
    id INT(11) PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    dept_id INT(11)
) ENGINE=InnoDB;

INSERT INTO emp(name, dept_id) VALUES('Shown White', '5');
EOF

イメージ作成&コンテナ起動

docker build ./ -t mymysql && docker run -p 3306:3306 mymysql

コンテナに接続してmysqlコマンドを叩くとテーブルを確認できた。

$ mysql -h 127.0.0.1 -P 3306 -u ceo -pceo hr

mysql> select * from emp;
+----+-------------+---------+
| id | name        | dept_id |
+----+-------------+---------+
|  1 | Shown White |       5 |
+----+-------------+---------+
1 row in set (0.00 sec)

追伸

わざわざ解読したけど、ちゃんとドキュメントに書いてたorz

mysql-init/ Shell scripts (*.sh) available in this directory are sourced when mysqld daemon is started locally. In this phase, use ${mysql_flags} to connect to the locally running daemon, for example mysql $mysql_flags < dump.sql