🦔

Docker Composeにおけるログのローテート

2023/09/26に公開

はじめに

今回は、Docker Composeで立ち上げたコンテナ内部のログをローテートするための方法を紹介します。

色々なやり方があると思いますが、ここでは「コンテナの中にlogrotateを導入し、ホストのcronから実行する」というアプローチをとります。

例として、実際にDocker Composeプロジェクトを起動し、そのコンテナのログをローテートしてみます。

サンプルプロジェクト

1台のNginxコンテナだけのプロジェクトを立ち上げます。

プロジェクトの構成

ディレクトリ構成は次の通りです。

.
├── compose.yaml
└── web
    ├── Dockerfile
    └── nginx.logrotate

そして、各ファイルの中身は以下のようになっています。

./compose.yaml
services:
  web:
    build:
      context: ./web
    ports:
      - "8080:80"
./web/Dockerfile
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
./web/nginx.logrotate
/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ユーザーで起動したい場合はUSERchmodの行を削ってください。
また、ベースの公式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 に次のように追記します。

/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つです。

  1. logrotateをコンテナに導入
  2. ホストの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が使える場合はオススメです。

ぜひ参考にしてみてください。


脚注
  1. 公式の2種類のインストール方法のうちInstall the plugin manuallyではユーザーごとにインストールするため、rootユーザーにもインストールしないとrootからDocker Composeを使うことはできません。 ↩︎

Discussion