Docker Composeにおけるログのローテート
はじめに
今回は、Docker Composeで立ち上げたコンテナ内部のログをローテートするための方法を紹介します。
色々なやり方があると思いますが、ここでは「コンテナの中にlogrotateを導入し、ホストのcronから実行する」というアプローチをとります。
例として、実際にDocker Composeプロジェクトを起動し、そのコンテナのログをローテートしてみます。
サンプルプロジェクト
1台のNginxコンテナだけのプロジェクトを立ち上げます。
プロジェクトの構成
ディレクトリ構成は次の通りです。
.
├── compose.yaml
└── web
├── Dockerfile
└── nginx.logrotate
そして、各ファイルの中身は以下のようになっています。
services:
web:
build:
context: ./web
ports:
- "8080:80"
FROM nginx:stable-alpine-slim
RUN unlink /var/log/nginx/access.log && \
unlink /var/log/nginx/error.log && \
apk add --no-cache logrotate && \
rm /etc/logrotate.conf && \
chmod gu+s /usr/sbin/nginx /usr/sbin/logrotate
COPY ./nginx.logrotate /
USER nginx
/var/log/nginx/access.log
/var/log/nginx/error.log
{
maxsize 1K
missingok
rotate 7
compress
delaycompress
sharedscripts
postrotate
nginx -s reopen
endscript
}
このサンプル限定の設定について軽く説明します。(このサンプルに限らず必要となるところは後でポイントとして説明します)
Dockerfileは、本番を見越して「nginx」という一般ユーザーでコンテナを起動するようにしています。rootユーザーで起動したい場合はUSER
とchmod
の行を削ってください。
また、ベースの公式Nginxイメージは/var/log/nginx/access.log、error.logがそれぞれ標準出力、エラー出力にリンクされているのでunlink
で解除して通常ファイルに戻しています。
ローテーションの設定ファイル(./web/nginx.logrotate)について、maxsize
で1KBを超えたログファイルのみローテートするように設定しています。(普通こんなにバイト数を制限しませんが、今回はすぐローテーションするために)
それから、ファイルが無くてもエラーにしなかったり(missingok
)、世代管理をしたり(rotate
)、圧縮したり(compress
, delaycompress
)しています。
また、Nginxの場合、ログファイルの名前が変わってもそこに出力しつづけるので、全ファイルのローテーションが終わったあと(sharedscripts
, postrotate
-endscript
)に、nginx -s reopen
でファイルの出力先を更新するようにしています。
プロジェクト起動・ログ生成
プロジェクトを起動。
$ docker compose up -d
アクセスを繰り返してログを貯めたあと、ログができているのを確認します。
$ for i in {1..12}; do curl -s -o /dev/null localhost:8080 && sleep 0.5; done
$ docker compose exec web ls -lF /var/log/nginx/
total 8
-rw-r--r-- 1 root root 1092 Sep 25 23:55 access.log
-rw-r--r-- 1 root root 518 Sep 25 23:55 error.log
ログのローテート
/etc/crontab に次のように追記します。
5 0 * * * yamada cd /path/to/project && docker compose exec -T web logrotate /nginx.logrotate
なお、上記では日付が変わったタイミングでローテートされるように実行時刻を0時5分に設定していますが、すぐに実行を確認したい場合は直近の時刻にしてもらえたらと思えます。
こうして設定時刻まで待って再度ログディレクトリを見てみます。すると、条件(1KB超)に合ったファイルのみローテートされているはずです。
$ docker compose exec web ls -lF /var/log/nginx/
total 8
-rw-r--r-- 1 nginx root 0 Sep 26 00:05 access.log
-rw-r--r-- 1 root root 1092 Sep 25 23:55 access.log.1
-rw-r--r-- 1 nginx root 815 Sep 26 00:05 error.log
以上、ログをローテートする実例を見てきました。
ここからは、このプロジェクトに言及しつつ、ポイントを説明します。
ポイント
今回の方法のポイントは、次の2つです。
- logrotateをコンテナに導入
- ホストのcronからコンテナのlogrotateを実行
1. logrotateをコンテナに導入
上のプロジェクトだと、Dockerfileのapk add --no-cache logrotate
でlogorotateをインストールしています。ベースイメージによっては元から入っていることもあるでしょう。
また、rm /etc/logrotate.conf
でlogrotateのおおもとの設定ファイルを削除しています。これは万が一余計な定期ローテーションをされたら嫌なので、その防止としてやっています。(実は不要かもしれません)
それから、COPY
でローテーションの設定ファイルをコピーしています。場所はどこでもいいですが、場所におうじてcronのコマンドに指定するパス(上の例では/nginx.logrotate
)を変えてください。
2. ホストのcronからコンテナのlogrotateを実行
コンテナ内でcronを使うのは難しいので、ホストのcronから操作します。
Docker Composeのインストール方法によってはrootユーザーでdocker compose
コマンドを実行できない[1]ので、cronジョブの実行ユーザーをDocker Composeが使えるユーザーにしています。
また、cronではTTYがないので、docker compose exec
コマンドには-T
をつけて擬似端末をオフにしたほうがいいでしょう。
おわりに
今回は、Docker Composeで立ち上げたコンテナ内部のログをローテートする方法の1つを紹介しました。
余談になりますが、このほかに、ログディレクトリをホストと共有したうえでホストのlogrotateを使うというやり方もあります。しかし、それだと権限の問題があってなかなか大変なことがあります(Rootlessモードは一層)。
ただ、今回の方法ならそうした問題もクリアできるので、ホストのcronが使える場合はオススメです。
ぜひ参考にしてみてください。
-
公式の2種類のインストール方法のうちInstall the plugin manuallyではユーザーごとにインストールするため、rootユーザーにもインストールしないとrootからDocker Composeを使うことはできません。 ↩︎
Discussion