MySQL の general_log や slow_query_log を docker compose logs から見えるようにしたい

## TL;DR 1. general_log や slow_query_log を任意の docker volume 上のファイルに出力します。 2. volume を共有するコンテナを別途用意し、そのコンテナからログファイルを `tail -f` します。 3. `tail -f` コンテナを経由して、 general_log や slow_query_log の内容が docker compose logs に流れます。 4. おわり。 具体的な `docker-compose.yml` を以下に例示します。 ```yml version: '3.8' services: mysql: image: "mysql:8.0" ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: root volumes: - mysql_volume:/var/lib/mysql command: - --general-log=true # (1). ここで指定したログファイルを - --general-log-file=/var/lib/mysql/general.log - --slow-query-log=true - --slow-query-log-file=/var/lib/mysql/slow.log mysql_general_log: image: "debian" volumes: # (2). volume で共有して - mysql_volume:/var/lib/mysql command: | # (3). 別コンテナから tail -f する tail -f /var/lib/mysql/general.log mysql_slow_log: image: "debian" volumes: - mysql_volume:/var/lib/mysql command: | tail -f /var/lib/mysql/slow.log ``` 以上です。 以下は余談です。 ## うまくいかない例: general_log や slow_query_log を /dev/stdout や /dev/stderr に出力しようとする 例えば、以下のような `docker-compose.yml` を用意します。 ```yml version: '3.8' services: mysql: image: "mysql:8.0" environment: MYSQL_ROOT_PASSWORD: root command: - --general-log=true - --general-log-file=/dev/stdout - --slow-query-log=true - --slow-query-log-file=/dev/stdout ``` これを `docker compose up` すると、 mysqld は以下のようなエラーを出力します。 ```text myapp-mysql-1 | 2022-08-08T14:00:01.678166Z 0 [ERROR] [MY-011263] [Server] Could not use /dev/stdout for logging (error 2 - No such file or directory). Turning logging off for the server process. To turn it on again: fix the cause, then either restart the query logging by using "SET GLOBAL SLOW_QUERY_LOG=ON" or restart the MySQL server. myapp-mysql-1 | 2022-08-08T14:00:01.678377Z 0 [ERROR] [MY-011263] [Server] Could not use /dev/stdout for logging (error 2 - No such file or directory). Turning logging off for the server process. To turn it on again: fix the cause, then either restart the query logging by using "SET GLOBAL SLOW_QUERY_LOG=ON" or restart the MySQL server. ``` `/dev/stdout` が `error 2 - No such file or directory` になるわけがないのですが、なぜか mysqld くんは確実にコンテナ内に存在するシンボリックリンク `/dev/stdout` を見つけられません。 ちなみに、 `--slow-query-log-file` の方を適当に root ユーザーで `touch /dev/test` したファイルに向けてやると、エラーの内容が変わります。 ``` myapp-mysql-1 | 2022-08-08T14:26:17.525209Z 0 [ERROR] [MY-011263] [Server] Could not use /dev/test for logging (error 13 - Permission denied). Turning logging off for the server process. To turn it on again: fix the cause, then either restart the query logging by using "SET GLOBAL SLOW_QUERY_LOG=ON" or restart the MySQL server. myapp-mysql-1 | 2022-08-08T14:26:17.525232Z 0 [ERROR] [MY-011263] [Server] Could not use /dev/stdout for logging (error 13 - Permission denied). Turning logging off for the server process. To turn it on again: fix the cause, then either restart the query logging by using "SET GLOBAL GENERAL_LOG=ON" or restart the MySQL server. ``` 見慣れた `error 13 - Permission denied` に変わりました。意味がわからない…。 ソースを読む元気はなかったのでここでトラブルシューティングは終了しました。 ## あとがき 実は、 - 「 [gosu](https://github.com/tianon/gosu) が悪さをしているんじゃないか?」とか - 「最初から mysql ユーザーとして実行したらうまくいくんじゃないか?」とか - 「 Debian ベースのイメージだったら…」とか - 「 mysqld プロセス以外でも No such file or directory になるのか?」とか など、色々なトラブルシューティングをして、とてもたくさんの時間を溶かしたのですが、身にならない話ばかりだったので省きました。 ## 補足: gosu とは > [tianon/gosu: Simple Go-based setuid+setgid+setgroups+exec](https://github.com/tianon/gosu) - `su` や `sudo` を使ってユーザーを切り替えつつコンテナを起動すると、 `su` コマンドや `sudo` コマンド自体のプロセスが PID 1 として生まれてしまう - 本来実行したいアプリケーションプロセスが `su` や `sudo` の子プロセスとして実行されてしまう - そうすると、コンテナへ送られたシグナルが実際のアプリケーションプロセスへうまく伝わらないことがある そんな問題を回避すべく作られた、ミニマルな `su` コマンドだそうです。 [docker-library/mysql](https://github.com/docker-library/mysql) では [こちら ( `docker-entrypoint.sh#L367` )](https://github.com/docker-library/mysql/blob/8a1c1d3c30e71c8efef064e26064a195737e7e1d/8.0/docker-entrypoint.sh#L367) で使用されています。 詳細はぜひリポジトリの README へどうぞ。 上述の問題について詳しく解説されています。
このエントリーをはてなブックマークに追加

このブログの人気の投稿

OpenTelemetry 互換の Observability 基盤比較検討

AWS アカウントを作成したらまず実行するスクリプト

Hello, World!