shimgo
8/27/2017 - 9:39 AM

公式のMySQLイメージのmysqldのログを標準出力に書き込めない

公式のMySQLイメージのmysqldのログを標準出力に書き込めない

###状況 公式のMySQLイメージを起動してdocker logsコマンドで確認すると初回は以下のようなログが出ている。

略
MySQL init process done. Ready for start up.

mysqldのログをdocker logsコマンドで確認したいので以下のように起動、あるいは設定ファイルを編集して起動。

sudo docker run -it -u mysql --name mysql -e MYSQL_ROOT_PASSWORD=pass db --general-log=true --general-log-file=/dev/stdout

これでdocker logsしても初回なら最初の例のメッセージが表示される。データディレクトリが既にセットアップ済みなら何の表示もない。
###調査 ####結局 色々調べた結果、単純にMySQLがファイルへの書き出しに対応していないだけかも。
参考:https://dev.mysql.com/doc/refman/5.6/ja/log-destinations.html
単純にmy.cnf内でログの出力先を/proc/1/fd/1(PID=1の標準出力)や/dev/pts/0(Dockerがコンテナの通信で使う仮想端末)にしただけではうまくいかなかった。 ####もしかしたら 公式イメージを-dオプションをつけずにdocker runすると最初の例のログが出力された後何もできなくなる。
Ctrl-cしても終了できない。Ctrl-\でなら終了することができる。
これはCtrl-cはSIGINTシグナルを送っていてmysqldはこれを無視するようになっている(おそらく。SIGINTを無視するオプションも存在する)ため、Ctrl-cで終了できないものの、Ctrl-\はSIGQUITシグナルでこれがアプリで処理されなかったらOSレベルでアプリが強制終了される。
参考: https://stackoverflow.com/questions/9694256/terminating-mysqld-session-to-go-back-to-command-prompt
この挙動により標準出力に何か書き込んでもmysqldに割り込むことができない? とはいっても仮想端末に直接書き込むことでdocker run した端末上に出力されたのでやっぱり違う可能性が高い。
ちなみに公式nginxは標準出力への書き込み、docker logsからの確認ができた。nginxはCtrl-cで終了できる。 ####標準出力への書き込み方法 docker logsはpid=1のプロセスの標準出力・標準エラーに出力された内容を記録している。
そのためpid=1の標準出力には以下のコマンドで書き込みできる。これを実行するのはpid=1のプロセスでなくてもいい。ただし、書き込み権限がないとだめ。

# /proc/<pid>/fd/<1:stdout | 2:stderr | 他にもいろいろ>
echo 'hello' > /proc/1/fd/1

ここでecho 'hello' > /dev/stdoutとしていないのは、この場合は'このプロセスの標準出力をこの標準出力に出力'となってしまうため。
/dev/stdoutは/proc/self/fd/1へのシンボリックリンクなのでpid=1の標準出力にはいかない。 参考:https://github.com/moby/moby/issues/19616#issuecomment-174355979
###解決策 Dockerfile内で、下記コマンドを記述したスクリプトを/docker-entrypoint-initdb.dに配置してDockerイメージを作成する。

tail -f /var/log/mysql/general.log > /dev/pts/0

/docker-entrypoint-initdb.d はMySQL公式イメージ内でデータベースの初回セットアップに使うもので、ここの中にある.sqlまたは.shが実行される。
/dev/pts/0はDockerがコンテナとの通信に使う仮想端末。docker logsはここを見ているのでlogsで拾いたい情報をここに送る。 ####注意 この方法だと初回にしか実行されないのでDBのセットアップが終わっていると実行されないので注意。