backcheck事業部の前田です。
この記事は個人ブログと同じ内容になります。
わけあって、公式のものとは別にMySQLのDockerイメージを作成しました。
この記事はその備忘録です。
公式のイメージはデータ保存の部分がVOLUMEマウントされているので、イメージの中にデータを含められないからです。
公式イメージではデータを保存できない
Docker環境でMySQLを利用したい場合はMySQLの公式イメージを使用するのが一番ラクで便利です。
ですが、公式イメージではデータを保存しておくことができません。
MySQLではデータを /var/lib/mysql
というディレクトリ内に保存するのですが、このディレクトリがVOLUMEマウントの対象になっているためです。
「データをdockerイメージに含めるのはおかしい」という話はもちろんあるのですが、それでも含めたいときがあるじゃないですか。。。というお話です。
試行錯誤してみた
自分で色々とやってみて、たどり着いたのが以下でした。
Dockerfile
FROM ubuntu:18.04 ENV TZ Asia/Tokyo ENV DEBIAN_FRONTEND noninteractive RUN apt-get update \ && apt-get install -y --no-install-recommends \ mysql-server \ tzdata \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # timezone WORKDIR /opt RUN mysql_tzinfo_to_sql /usr/share/zoneinfo > timezone.sql # mysql RUN mkdir -p /var/run/mysqld \ && chown mysql:mysql /var/run/mysqld \ && usermod -d /var/lib/mysql mysql \ && mv /etc/mysql/conf.d/mysql.cnf /etc/mysql/conf.d/00_mysql.cnf \ && mv /etc/mysql/conf.d/mysqldump.cnf /etc/mysql/conf.d/01_mysqldump.cnf \ && mv /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/00_mysqld.cnf \ && mv /etc/mysql/mysql.conf.d/mysqld_safe_syslog.cnf /etc/mysql/mysql.conf.d/01_mysqld_safe_syslog.cnf \ && sed -i -e "s~log_error~# log_error~g" /etc/mysql/mysql.conf.d/00_mysqld.cnf \ && sed -i -e "s~bind_address~# bind_address~g" /etc/mysql/mysql.conf.d/00_mysqld.cnf COPY original_mysql.cnf /etc/mysql/conf.d/02_original_mysql.cnf COPY original_client.cnf /etc/mysql/conf.d/03_original_client.cnf COPY original_mysqld.cnf /etc/mysql/mysql.conf.d/02_original_mysqld.cnf COPY entrypoint.sh entrypoint.sh RUN chmod +x entrypoint.sh EXPOSE 3306 ENTRYPOINT ["./entrypoint.sh"]
entrypoint.sh
#!/bin/sh set -e find /var/lib/mysql -type f -exec touch {} \; if [ ! -f timezone_applied ];then mysqld & sleep 3 mysql -uroot -D mysql < timezone.sql mysqladmin shutdown -uroot touch timezone_applied fi mysqld
ひとつずつ解説していきます。
Dockerfile
FROM ubuntu:18.04
コンテナと言えばAlpine Linuxですが、Alpineのパッケージ管理ツール(apk)では、デフォルトではMySQLをインストールできません。
MySQL互換のMariaDBならインストールすることができますが、MariaDBは認証まわりがMySQLとだいぶ違うため、今回は見送りました。
Alpine以外なら何でも大丈夫だと思います。Ubuntuは私の趣味です。
ENV TZ Asia/Tokyo
TZ
というタイムゾーンを指定することで、MySQLのタイムゾーンの指定をすることができます。1
ENV DEBIAN_FRONTEND noninteractive RUN apt-get update \ && apt-get install -y --no-install-recommends \ mysql-server \ tzdata \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
MySQLとtzdataをインストールしています。
tzdataはタイムゾーンの情報が入ったパッケージです。
MySQLではデフォルトではタイムゾーンの情報が入っていないため、このtzdataをベースにタイゾーン情報を入れていきます。
また、tzdataをインストールする際に対話シェルが開始されます。
Dockerイメージをビルドする際に対話シェルが開始されると処理が止まってしまうので、 DEBIAN_FRONTEND
を指定して対話シェルが開始されないようにしています。
apt使用後はaptに関するデータは不要になるので、最後に使用されていないパッケージや更新されたリポジトリリストを削除しています。
WORKDIR /opt
ここからはファイルを生成したりしていくので、ルートディレクトリから移動をしています。
どこでもいいので、とりあえず /opt
に設定しています。
RUN mysql_tzinfo_to_sql /usr/share/zoneinfo > timezone.sql
先ほどインストールしたtzdataの情報をSQLに変換しています。
このSQLはentrypointで使用します。
RUN mkdir -p /var/run/mysqld \ && chown mysql:mysql /var/run/mysqld \
MySQLのデーモンがpidファイルを作成するディレクトリを作成しています。
また、Dockerイメージをビルドする際は基本的にrootユーザなので、ディレクトリのオーナーもmysqlに変更しています。
&& usermod -d /var/lib/mysql mysql \
mysqlユーザのホームディレクトリが設定されていない場合、 No directory, logging in with HOME=/
というエラーが発生するので、ここでmysqlユーザのホームディレクトリを設定しています。
&& mv /etc/mysql/conf.d/mysql.cnf /etc/mysql/conf.d/00_mysql.cnf \ && mv /etc/mysql/conf.d/mysqldump.cnf /etc/mysql/conf.d/01_mysqldump.cnf \ && mv /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/00_mysqld.cnf \ && mv /etc/mysql/mysql.conf.d/mysqld_safe_syslog.cnf /etc/mysql/mysql.conf.d/01_mysqld_safe_syslog.cnf \
Debian系のLinuxでは、設定をひとつのmy.cnfに書かずに、それぞれの設定をファイルに分割して書きます。
その際にファイル名の順番で読み込まれていきます。
読み込まれる設定の順番を管理するために、デフォルトの設定ファイルに連番のプレフィックスを割り振っています。
ちなみに、 /etc/mysql/mysql.conf.d/
がデーモン寄りの設定が入っており、 /etc/mysql/conf.d/
にクライアント寄りの設定が入っているようです。
&& sed -i -e "s~log_error~# log_error~g" /etc/mysql/mysql.conf.d/00_mysqld.cnf \ && sed -i -e "s~bind_address~# bind_address~g" /etc/mysql/mysql.conf.d/00_mysqld.cnf
デフォルトの設定のいくつかを無効化(コメントアウト)しています。
Dockerではログファイルにログを吐かれても仕方がないので、 log_error
を無効にしています。
また、Dockerコンテナを使う以上、外部ネットワークからの接続を期待するので、 bind_address
を無効にしています。
COPY original_mysql.cnf /etc/mysql/conf.d/02_original_mysql.cnf COPY original_client.cnf /etc/mysql/conf.d/03_original_client.cnf COPY original_mysqld.cnf /etc/mysql/mysql.conf.d/02_original_mysqld.cnf
自分の作成した設定ファイルをコピーしています。
COPY entrypoint.sh entrypoint.sh RUN chmod +x entrypoint.sh
自分で作成したエントリーポイントを使用したいため、コピーしてきています。
また、実行可能にするために実行権限を付与しています。
EXPOSE 3306 ENTRYPOINT ["./entrypoint.sh"]
MySQLなので3306番ポートを開き、エントリーポイントを設定しています。
entrypoint.sh
#!/bin/sh set -e
set -e
を指定すると、エラー発生時にスクリプトを止めてくれます。
find /var/lib/mysql -type f -exec touch {} \;
Dockerのストレージエンジン(overlay2)とMySQLの相性が悪いらしく、そのままではCan’t open and lock privilege tables: Table storage engine for ‘user’ doesn’t have this option
というエラーが発生します。
このコメントを参考にさせていただきました。
if [ ! -f timezone_applied ];then
このあとの処理で、タイムゾーンの設定が完了した際に timezone_applied
というファイルを作成しています。
つまり、タイムゾーン設定が完了していない場合にタイムゾーンの設定を行っています。
mysqld & sleep 3
mysqldを起動し、起動が完了するまで待機しています。
3秒なのは手抜きです。本当は起動完了しているかどうかを何かしらの方法でチェックする必要があります。
mysql -uroot -D mysql < timezone.sql
MySQLにタイムゾーンのデータをインポートしています。
timezone.sql
はDockerfile内で作成しました。
mysqladmin shutdown -uroot touch timezone_applied fi
mysqldを終了し、 timezone_applied
ファイルを作成しています。
これで2回目以降はタイムゾーンの設定がスキップされます。
mysqld
MySQLを実行します。実質のentrypointです。
まとめ
自分でMySQLのイメージを作成してみました。
公式のMySQLのDockerfileがめちゃくちゃ勉強になります。
最初は全く読めないので、こちらの記事がとても参考になりました。