コンテナ間でlogrotateしたいpart1

この記事は[富士通クラウドテクノロジーズ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/fjct)の17日目の記事です。

9日目の記事は @digfield の[zabbixでサーバの死活監視設定を行った話](https://qiita.com/digfield/private/ea721ef3a36abd69fa56)でした。やはりリアルタイムで死活監視できるようにするととても便利だなと感じました。自分はまだzabbixを使ったことがありませんが、その時がきたらぜひ参考にしたいと思います。

本記事では、nginx(openresty)のログファイルを別のlogrotate用コンテナ（以下logrotateコンテナ）を用いてログローテーションを実装する方法について記述します。
※この記事はpart1です。part2は[こちら](https://qiita.com/toyo_mura/items/b50b75b175ecf45390e4)
part2: https://qiita.com/toyo_mura/items/b50b75b175ecf45390e4

~~Qiita初投稿なので間違い等ありましたら優しく教えていただきたいです。~~

# はじめに
人はめんどくさがりです。何をやるにしても効率化を考えます。
テレビをリモコンで操作するのがめんどくさいのでAlexaを使って声で操作できるようにしたり、手作業で入力するのがめんどくさいので自動で入力してくれるプログラムを作成したりします。

私ももちろんめんどくさがりです。
業務で複数コンテナから吐き出されるログをローテーションさせる必要があり、どうせならホストに依存せずその機能を実現したいと思いました。
そのとき調査した内容を自身の備忘録的に記事にしています。

# 検討したlogrotateの方法
複数コンテナにローテーションしたいファイルがある時、私はlogrotateを使用するのがいいのではないかと考えました。（linuxでログローテーションと言ったらlogrotateのため）

その場合の、logrotateを動かす場所について考えます。

* **ローテーションしたいファイルのあるコンテナ上でlogrotateを動かす**
これはローテーションしたいファイルのあるコンテナが1つの場合なら有効かもしれません。しかし、複数のコンテナにローテーションしたいファイルがある場合、この方法では1つ1つのコンテナを設定していく必要があり、めんどくさいです。

* **マウントしたディレクトリにローテーションしたいファイルを出力してホストでlogrotateを動かす**
多分これが一番簡単にlogrotateを設定できる方法です。ローテーションしたいコンテナの数が増えても簡単に対応できます。
大体の運用ではこの設定で大丈夫だと思われます。
しかし、この方法ではホストでlogrotateを動かすことになるので、ホストを再構築するとなった場合に改めてlogrotateの設定が必要になってしまいます。めんどくさいです。

* **マウントしたディレクトリにローテーションしたいファイルを出力してlogrotateコンテナでlogrotateを動かす**
コンテナの中でcronやlogrotateを動かす必要があったり、コンテナ間の連携が必要になったりと、上記2つよりめんどくさいです。
ですが、実装した暁には~~多分~~管理が1番簡単になります。ホストを再構築する場合も、コンテナを再度立ち上げるだけでlogrotateの設定が完了するからです。最高です。

今回は検討した結果、実装はめんどくさいですが管理が簡単なため、3つ目の方法で実装することにしました。
これについて試行錯誤した点も踏まえて書いていきます。

# ゴール

複数のコンテナで吐き出されているnginxのログをlogrotateコンテナで一括管理・ローテーションできるようになることをゴールとします。

# 実装の流れ
1. ローテーションしたいファイルのあるコンテナ（以下nginxコンテナ）にディレクトリをマウント（コンテナ起動オプション変更）
1. logrotateコンテナ作成
1. 動作確認（途中経過）←この記事ではここまで
1. nginxにログファイルを開きなおさせるかnginxの再起動の設定
1. 動作確認

# 環境
* ホスト
  * OS: CentOS 7.9.2009
  * Docker: 20.10.9
* nginxコンテナ
  * OS: CentOS 7.6.1810
  * nginx: openresty/1.19.3.1

# nginxコンテナ内のログファイルを出力するディレクトリをマウント
まずはnginxコンテナ内のnginxログが出力されるディレクトリをマウントします。
現在のコンテナ起動オプションに以下を追加します。

~~~
        -v /hogehoge/nginx/access/:/usr/local/openresty/nginx/logs/access/ \
        -v /hogehoge/nginx/error/:/usr/local/openresty/nginx/logs/error/ \
~~~

# logrotateコンテナを作成する
次にlogrotateコンテナを作成します。

概要は以下です。

* Dockerファイルに記述する項目
  * rsyslog
  * vim
  * cron
  * logrotate
  * logrotare設定ファイル
  * tzdata
  * entrypoint.sh

Dockerファイルは以下です。

~~~Dockerfile
FROM ubuntu:20.04

COPY entrypoint.sh /tmp/

##### rsyslog settings #####
RUN apt-get update
RUN apt-get -y install rsyslog
RUN sed -i -e "s/^#cron/cron/" /etc/rsyslog.d/50-default.conf

##### vim settings #####
RUN apt-get -y install vim
RUN echo 'set encoding=utf-8\n\
set fileencodings=iso-2022-jp,euc-jp,sjis,utf-8\n\
set fileformats=unix,dos,mac' >> ~/.vimrc

##### cron setting #####
RUN apt-get -y install cron

##### logrotate settings #####
RUN apt-get -y install logrotate
COPY ./nginx /etc/logrotate.d/

##### timezone settings #####
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get -y install tzdata
ENV TZ Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

ENTRYPOINT ["sh","/tmp/entrypoint.sh"]
CMD ["cron", "-f"]
~~~

/etc/logrotate.d/に持っていっているnginxのログ用logrotate定義ファイルの中身は以下です。

~~~logrotate
/var/log/nginx/access/*.log
/var/log/nginx/error/*.log {
    ifempty
    dateext
    missingok
    compress
    delaycompress
    daily
    rotate 10
}
~~~

entrypoint.shの中身は以下です。

~~~sh
#!/bin/sh

service rsyslog start

exec $@
~~~

buildオプションは以下です。

~~~sh
docker build -t logrotate .
~~~

コンテナ起動オプションは以下です。

~~~sh
docker run -d \
        -v /hogehoge/nginx/access/:/var/log/nginx/access/ \
        -v /hogehoge/nginx/error/:/var/log/nginx/error/ \
        --name logrotate logrotate
~~~

1つずつ説明します。

#### OSがubuntuの理由
はじめはCentOSでやろうとしていました。(普段使用していて慣れているため）
ですが、コンテナ起動時にrsyslogを自動起動してほしかったのでいろいろ試してみるもうまく行かず（chkconfigがないなど）、ubuntuにすればそれが解決できそうだった（update-rc.dが使える）のでubuntuにしました。
ただ、結局update-rc.dは使ってないのでCentOSでも実装できそうな気はします。

#### rsyslog settings
rsyslogのインストール、設定をしています。
cronのログを吐き出す記述がコメントアウトされているのでコメントアウトを外します。
cronが動いたかどうかのログを確認するために入れています。

#### vim settings
vimのインストール、設定をしています。
日本語対応もついでにやっています。
後でコンテナ内で作業するために入れています。（ubuntuコンテナはデフォルトでviもvimも入っていない）
ただ、これは実装途中にいろいろ触りたかったので入れているだけで、完成形だけを求めるのであれば必要なさそうです。

#### cron setting
cronをインストールしています。
logrotateはデーモンプロセスではありません。cronで動いています。
そのためcronを入れています。
参考:https://milestone-of-se.nesuke.com/sv-basic/linux-basic/logrotate/

#### logrotate settings
logrotateのインストール、設定をしています。
ローテーションするために必須です。

#### timezone settings
コンテナのタイムゾーンを日本にしています。
これによってローテーションが正しい時間で行われます。
参考:https://qiita.com/yagince/items/deba267f789604643bab
     https://northshorequantum.com/archives/dockerbuild_tz_hang.html

#### /etc/logrotate.d/に持っていっているnginxファイル
logrotateの動作を設定しています。

ログを出力するコンテナは各々がユニークなファイル名でログを出力していて、logrotateコンテナではファイル名が重複することが無いためこのような形となっています。

この記述を行うことで、それぞれローテートさせられます。


#### entrypoint.sh
rsyslogを起動しています。
これでcronのログが吐き出されます。

#### buildオプション
logrotateと名前をつけてbuildしています。

#### コンテナ起動オプション
ログが出力されるディレクトリがマウントされているホストのディレクトリをマウントしつつ起動します。

# 動作確認（途中経過）
ここまでの実装で、ログファイルをローテーションしてくれるようにはなったはずです。
確認してみましょう。
（今回はlogrotateを強制実行はせず、のんびり2,3日待って確認しています）

~~~shell
root@a49a7d0afad0:/# ls /var/log/nginx/access/
hogehoge.com.log
hogehoge.com.log-20211212.gz
hogehoge.com.log-20211213
fugafuga.com.log
fugafuga.com.log-20211212.gz
fugafuga.com.log-20211213
~~~

問題なくローテーションできているようです。

しかし、ここで大きな問題に直面します。
見かけ上はログファイルがローテーションできているように見えますが、内部的にはローテーションできていません。
具体的には、ローテーション前のファイル名を変更して日付を入れ、新しく出力してほしいログファイルを作成したとしてもnginxはローテーション前のファイルにそのまま書き込みを続けてしまいます。

# ローテーション後のファイルにログを出力してくれない問題
nginxは仕様上、ログファイルの名前を変えても、そのままリネーム後のファイルに書き込みを続けます。
ログファイルを開き直したり、nginxを再起動したりすると、掴んでいるファイルが正しく置き換わります。

つまり、**コンテナ間でのlogrotateを完成させるには、logrotateを実行したあと、nginxにログファイルを開きなおさせるかnginxの再起動をする必要があります。**

# どうやってログファイルを開き直し or nginx再起動をするか
ここから先は記事が長くなりそうなので分割します。
~~記事公開に実装が間に合わなかったなんてことは、、~~
part2は[こちら](https://qiita.com/toyo_mura/items/b50b75b175ecf45390e4)
https://qiita.com/toyo_mura/items/b50b75b175ecf45390e4

# まとめ
* コンテナだけでlogrotateできるとホスト再構築のときでも簡単
* logrotateコンテナ自体は簡単につくれる
* コンテナ間でのローテーションを完成させるには、logrotateを実行したあと、nginxにログファイルを開きなおさせるかnginxの再起動をする必要がある。

# おわりに
めんどくさがりな誰かの参考に慣れば幸いです。
読んでいただきありがとうございました。

明日は@miyuushの「ニフクラのTerraform Providerでメタ引数を使ってみる」です。お楽しみに！！！