この記事はCAMPHOR- Advent Calendar 2020の 17 日目の記事です。
11 月の終わり頃、個人的にインターンとインターンの合間で暇していた時期に CAMPHOR-のサーバーに Grafana/Loki を導入しました。
この記事では、Grafana/Loki を用いて、サーバー内の容量の圧迫を防ぎつつコンテナログの永続化を行う方法とその注意点を紹介します。
背景
現状 CAMPHOR-の各種サービスは docker に載った状態で動作しています。
元々改善を入れる前は特別なログの管理ということをしていませんでした。
CAMPHOR-ではサーバーの ssh を行うことができる人を制限しているので、ログを見たい時は ssh のできる人がdocker logs
からログを調査するという形になっていました。
この状態には以下の問題点があります
- コンテナ再起動に伴うログの消失
- サーバーへのアクセス権がある人のみしかログを閲覧できない
docker はデフォルトの状態でコンテナの停止時にログが消えてしまいます。そのため、コンテナを再起動させた場合などに再起動前のログを追えなくなります。
コンテナログに求められる要件
今回のコンテナログに求められることは以下です
- ログがコンテナが停止しても消えない(must)
- ログを誰でも簡単に見れる
- ログを溜めすぎてサーバーの容量一杯になるとやばいので定期的に消したい
- 新しいコンテナが追加された際の対応が簡単である
初めは、docker の吐くログファイルを直接永続化して logrotate をかけるなどのシンプルな方法を考えていましたが、要件 2 を満たすために、WebUI からログを閲覧できる必要があると考え、今回は Grafana/Loki を使用することにしました。 要件を満たす方法は他にも「EFK スタック」と呼ばれる ElasticSearch / Fluentd / Kibana などをはじめとして色々あるかと思います。
loki-docker-driver を用いたコンテナロギングの構築
Loki にログを転送するための docker-plugin としてDocker Driver Client(grafana/loki-docker-driver)が存在します。これを docker の logging driver として使用することでコンテナの吐くログをうまくコンテナごとに label を貼りつつ loki に送ってくれます。
この plugin を入れればあとはconfigurationにのっとって設定をしていくだけになります。難しい部分が本当にひとつもなくサクッと導入をすることができました。
Loki と Grafana は公式の docker-compose.yamlをほとんどそのまま使用して立ち上げました。
と言うことで、図に表すとこんな感じになります (図をわかりやすくするために、Loki と Grafana を docker の外に出しています)
loki-docker-driver が loki に送ったログを Grafana から参照する形です。
このような形で閲覧できます。
ログの最大容量の制限
ここから少し細かい設定に関してみていきます
{
"log-driver": "loki",
"log-opts": {
"mode": "non-blocking",
"loki-url": "http://localhost:3100/loki/api/v1/push",
"loki-batch-size": "400",
"loki-retries": "2",
"loki-timeout": "1s",
"keep-file": "true",
"max-size": "5g"
}
}
上記が実際に使用している docker デーモンの設定ファイル(daemon.json
)です。
keep-file と言う設定が入っています。
This indicates the driver to keep json log files once the container is stopped. By default files are removed, this means you won’t be able to use docker logs once the container is stopped. https://grafana.com/docs/loki/latest/clients/docker-driver/configuration/
説明にもありますが、これによってdocker logs
コマンドが使用可能になります。docker logs
コマンドはホストに残される json file のデータをもとに実行されるので、logging driver を loki-docker-driver に変更してしまうとデフォルトだと json file が吐かれなくなりdocker logs
コマンドも使用不可能になります。
Grafana がいるとはいえ、docker logs
コマンドがないと不便なのでkeep-file
を有効にしています。
「いやいや、要件 3 の「ログ溜めすぎると容量がやばい」はどうなんねん」と思われるかと思いますが、その一つ下のmax-size
によって json-file の最大容量を制限しています。
次は Loki 自体の設定を見ていきます。
chunk_store_config:
max_look_back_period: 2184h
table_manager:
retention_deletes_enabled: true
retention_period: 2184h
上記はデフォルトから変更を入れた loki の設定です。ここでは Loki に残されるログデータの容量を制限するために、約 3 ヶ月より前のログに関しては delete するように指定しています。 これによって、Loki に送られたデータが容量を圧迫すると言う心配もありません。(Loki はログを gzip 圧縮して持ってくれるので容量を取りにくいとはいえ、塵も積もればと言いますしね)
ちなみに注意点として period に指定する値は 168h の倍数である必要があります。
Hey, Loki requires that retention (720h in your case) is divisible by the duration of each index table (168h by default). https://github.com/grafana/loki/issues/2151
Loki が死ぬと docker 全体が死ぬ問題
順調に見えた Loki の導入ですが、Loki のコンテナの再起動をしようとした時に事件は起こりました。
Loki が死ぬと、loki-docker-driver がログを送ろうとして retry を続けて docker デーモン自体がクソ重くなってしまうという問題です。
これによって一回 CAMPHOR-の全サービスが落ちました(深夜 2 時だったのが不幸中の幸いだった)
渾身のツイートも滑っていますね
このissueのせいで一瞬dockerデーモンごとドッカーンしました(真顔)https://t.co/I9o2hrq2hV
— さんぽし (@sanpo_shiho) November 27, 2020
こちらは先程のdaemon.json
の再掲です
{
"log-driver": "loki",
"log-opts": {
"mode": "non-blocking",
"loki-url": "http://localhost:3100/loki/api/v1/push",
"loki-batch-size": "400",
"loki-retries": "2",
"loki-timeout": "1s",
"keep-file": "true",
"max-size": "5g"
}
}
この問題の対処のために複数の設定を入れています
- mode: non-blocking にすることでコンテナの処理が中断されない
- loki-retries: リトライの回数を減らす(デフォルトは 10)
- loki-timeout: timeout までの時間を短くする(デフォルトは 10s)
これで上記の問題は抑えることができました。 しかし、ログの信頼性が代わりに落ちるので、この辺りはバランスを考えて設定すべきですね。
終わりに
今回は loki-docker-driver を使用した、コンテナロギングの構築の一例を紹介しました。
どの程度のアクセスがくるサービスのログなのか、ログを吐くプロセスがいくつ走っているのか等、状況によってログに求められるものが大きく変わってきます。 今回の構成では最後の issue に対処するためにログの信頼性が少し下がってしまう設定を入れています。しかしこれは現状の CAMPHOR-のサービス状況を考えたときに常に大量のログが吐かれているわけではないという状況も加味した上での判断です。
今回のこの一例がどなたかのお役に立てば幸いです、ここまで読んでいただきありがとうございました。