SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

ログとファイルとローテーションと ~アーキテクトコラム~

アーキテクチャ設計を勉強しよう

みなさんこんにちは。NRIデジタルの松村です。夏も終わりが近づいてきて、少しずつ涼しくなってきましたね。季節の変わり目は風邪をひきやすいですよね。みなさまもご自愛ください。うちの子も、おもらしが増えてきました。危険ですね。

そうなんです。変わり目は注意が必要です。ということで、今回はシステムの「変わり目」として昔からある、ログローテーションを取り上げてみたいと思います(強引ですか? 強引です! いいんです!)。

なぜ今更ログローテーションなのか、という話ですが、ちゃんと理由があります。アーキテクチャ設計というのは、とかく大規模になりやすいです。前回のテーマであるマイクロサービスなどはその典型で、まじめに考えると、データベースの種類、分割から、チームや組織・会社の文化的側面まで数多くの要素を考えて設計/選定する必要があります。ただ、すでにプロフェッショナルなアーキテクトであればこういう問題に取り組むべきかもしれませんが、これからアーキテクチャ設計の勉強を進めていく人がいきなり取り組むには問題が複雑すぎます。それではアーキテクチャ設計を行う練習はできないのか?と考えた一つのアプローチが、「小さくて独立性のある問題を取り上げる」です。これは、レガシーで手になじんだテーマが切り出しやすく、いくつか候補を持っているのですが、その中でも取り扱いやすいテーマとしてログローテーションがあります。

そういうわけで、今回はログローテーションをテーマとして、アーキテクチャを考える練習をしていきたいと思います。はじめに、考えてほしいポイントについてテーマを提示します。できれば、後続を読み進める前に少し考えてみてください。その後、私の考え方を書いていきます。注意してほしいのは、これは解説ではありません。あくまで一つの考え方であり、その考え方自体を疑うことも、アーキテクトとして重要なことです。ということで、内容ももちろんコンテンツの一つではありますが、どちらかというと考え方のプロセスを重視して読んでみてください。そうすることで、他のテーマでも考える力が身に着けられます(というようになってもらえるように頑張ります!)。
今回は前回より短くまとめられると思いますので1)気のせいでしたね。すみません。。。、最後までお付き合い頂けますと幸いです。

ということで、お題はこちら!

ばばん。

  • ログローテートのパターンとして何があるのか
  • 何に着目してパターンを選定するべきか
  • PaaS/SaaSを扱う上で、考え方に影響はあるか

この3点です。シンプルですね。
ちなみに、今回の記事は「学習」に焦点を当てているため、かなり遠回りをしていきます。答えだけ知りたい方は最後から読んで下さい。

準備はいいか!?

どうでしょうか。考えはまとまりましたでしょうか。

それではそれぞれの考え方に移り……ません! ダメです! ダメですよー。考えるだけではダメです。ダメなんです。大事なので何度も書きました。

何がダメか。考えるだけではダメです。絶対に調べてください。インターネット全盛のこのご時世、知識幅では絶対に自分よりGoogle先生を始めとした先生方には勝てません。自分の中でプラクティスまで昇華できているような場合を除き、関連する情報をどれだけ調査できるのかということが重要になります。つまり知識ですね。
最近の教育では「知識より知恵をつける」ということが盛んに叫ばれていますが、知己である大学の先生曰く、「知識がない知恵は役に立たない」です。知恵とは知識を活用する術であり、十分な知識を使って初めて生きるということです。本稿は考え方、つまり知恵を引き上げることを主旨としていますが、適切な成果を出すためには知識の収集も同様に重要、ということです。

少々横道にそれましたが、ということでまずは、知識の集め方からです。

ここからが本番。

知識を集める

歯に衣着せぬ言い方をすれば、とにかくGoogle先生(を初めとした検索エンジン)に聞きまくって調べるわけですが、ここでも人により結構差が出ます。参考までに、私が何をどれくらい調べるか、というご紹介をしたいと思います。検索ワードは赤色にしておきますので、そこだけピックアップしてもよいです。

まずは、鉄板のログローテートからですね。もちろんログローテーションでもOKです。Googleはすごいのでどちらで検索しても大して結果は変わりません。こういうところはさっさと甘えて、どちらで検索するか悩む3秒はごみ箱に捨てましょう。ちなみに、「知ってるよ!」という場合でも、念のため検索してTop10くらいをパラっと見るのがお勧めです。理由は2つあります。
1つは、知った気になっていることを気づくため。以前後輩がコード内に「appry」みたいなのを書いてて、「それは applyだろ! 検索しろよ!」と指摘すると、「まさか間違ってると思いませんでした。」という回答が返ってきまして。愕然としつつも、確かに、間違ってるかも、と露程も思わなければ検索しないな、と。――閑話休題。要は、自分の認知外の気づけるような癖をつけておくことが大事、という話です。特に、アーキテクチャ検討などの、後段への影響が大きいものについては慎重になってしかるべきです。
もう1つは、知らない間に新しい技術が出ている可能性があるからです。さすがに今回私が調べた時にはそんなことはありませんでしたが2)だからこのテーマを選んだわけですよね、IT界隈は異常に時間が進むのが早いです。昔苦労していたことが簡単にできるようになる、ということは往々にしてあります。それを知らずに車輪の再発明をすることはないですね。

さて結果です。(将来変わっているかもしれませんが)大体が logrotate に関する記事だったのではないでしょうか。ここから、logrotate がデファクトであることが推測できます。ちなみに、ざっと読むとわかりますが、全てLinux(UNIX)を対象としていますね。Windowsはどこにいったのでしょうか。こういった「適切な疑問」を抱けるように心がけましょう。知識の探索は、問題の設定と調査を繰り返すことで深まります。ということで、ログローテート windows で検索しますね。すると、Windows環境ではデファクトがなく、バッチなどで個別にやっていそうだ、ということがわかります3)当然、後述するようなプロダクトがローテーションする場合は別です。OSとして機能/サービスを持っているか、という話です。。こうやって、自分が扱っている問題の概観を把握していきます。

とはいえ、基礎知識ばかり集めていても仕方ないので、そろそろお題にたどり着きましょう。
念のため、軽く「ログローテートとは」を補足しておきますと、OSやプロダクトが出力するログは出し続けると1ファイルが肥大化してディスクを圧迫したり開けなくなったりするので、適当なタイミングで切って世代管理/旧版削除をする、という機能です。

ログローテートのパターン

どんなパターンがあるのか、というのは、ログを取り扱った経験から出てくるのが望ましくはありますが、わからない場合は順番に調べていきます。

調べる方法はいくつかあります。今時点の情報から調べ方を列挙すると、

  • 無作為にGoogleを漁るログローテート パターン、とか、ログローテート 種類、とかのように、キーワードを組み合わせて探索するのがよいです。Googleであれば、末尾にスペースを入れるとよく検索されている候補が出てきますので、そのあたりからあたっていってもよいです。私はうまく当たらない場合は類義語や関連する言葉を入れてみることが多いです。例えば「パターン」だと、種類方法場所(ローテートしたものをどこに置くのか、という意味)、プロセス(誰がやるか)、主体(同左)、などです。4)まぁ、あたってみて全滅したわけですが。このアプローチで当たるときもあります。
    後は、何ページか読んでいく中でキーワードを fine tuning していきます。その領域でよく使われる用語、というのはありまして、何種類か読んでいると、よく使われる単語がわかってきます。こういうところは検索するときに合わせるとあたりやすいです。逆に使ってはいけない用語も見分けていきます。例えば、ログローテート パターン、で検索すると、「重複するパターンを登録してはいけない」といった記事が出てきます。ここでいうパターンは、設定内容のことを表しています。つまり、異なる意味を含んでしまっているので、適切な用語の選択ではなかったということですね。
    また、大量に検索結果がある場合は、上から順番に読んでいっても似たような内容が続くことが多いです。この場合は、キーワードを追加することで深堀し、違う角度から追加情報を探します。
  • logrotateのオプションを漁る。代表的なプロダクトは、多くのユースケースを含む場合が多いです。そのため、代表的なプロダクトをよく調べ、それを汎用的な観点で理解することでパターン化する(=汎化)、という方法です。
    この際は、できるだけ原典に近いドキュメントをあたりましょう。logrotate だと、 man5)Linuxコマンドに標準で付属しているマニュアル。man logrotate のようにコマンドプロンプトで叩くことで確認できる。Redhatの資格試験でも使用可能なくらい、標準。 の内容か、redhatなどのLinuxディストリビューション、およびLinuxマニュアル提供サイトなどが提供する説明がいいでしょう。日本語ドキュメントがないからQiitaなどで補完することはOKですが、古かったり間違っていたりすることもしばしばありますので、必ず原典はあたる、という癖はつけましょう。
  • ログを出力する対象からあたる。よくある /var/log/messages から始まり、OS系、Web/APサーバ系、DB系、セキュリティ製品系など、サーバ上で動きそうなものを色々あたります。正確には、対象としているプロジェクト/サービスが利用するものを一式あたるというアプローチです。広い視野は必要ですが、使いもしないものを深堀し始めると時間がいくらあっても足りなくなります。このあたりの、どういう調査にどのくらい時間をかけるべきか、というのは徐々に感覚を身に着けていく必要があります。

1つめはやってみた結果としていいものが出てきませんでしたので、2つめと3つめを確認していきます。

logrotateのオプション

まずはman を確認しましょう。Webでもありますが、手元にLinux環境がある場合は直接読んでも構いません。著名なものになると日本語訳があったりもするので、そういうものを使用しても構いません。

そして、代表的なパターンをピックアップしていきます。ここは多少恣意的になりますがご容赦ください。

  • compress 圧縮するかどうか
  • copytruncate デフォルトでは move 後に新しいファイルを create するが、そうではなく現在のログファイルをcopyした後にtruncate(中身を削除)する
  • daily, maxsize など ローテートするタイミング制御を日時やサイズなどで指定
  • prerotate, postrotate ローテート処理前後で任意の処理を挟む

詳細な設計を行う際にはもっと細かく各要素のチェックを行いますが、アーキテクチャ設計としておおまかな方式を決める際にはこれくらいの大枠で設計ポイントを確認できれば十分です。

ログ出力対象

これはシステムにより異なるポイントですが、ここでは1点だけ。例えば、Javaのプログラムがログ出力するときのローテートがどうなっているのかを調べましょう。java ログ ローテート とかで調べますね。すると、log4jに関連する記事が多く出てくるかと思います。内容をざっと読んで頂ければわかりますが、Javaのloggerであるlog4jがログのローテートまで対応することもできます。

つまり、log4jのように、ログの出力側でローテートする、というパターンもあるということがわかります。

まとめ

上記をまとめると、パターンとしては以下のものがありそうです。重要そうな観点順に並べなおしています。

  1. ローテートはログ出力側(例:log4j)で行うか、外部(例:logrotate)で行うか
  2. move -> create か、copy -> truncate か
  3. ローテートのタイミングおよび前後での処理
  4. ローテートされたログの圧縮要否

なお、実業務ではよく一緒にバックアップの検討がされます。が、本記事の対象はあくまでログローテートとして、バックアップへの言及はしないことにします。

何に着目してパターンを選定するか

※そろそろ調査方法に関する詳説は省略していきます。基本的なアプローチは同じですので、適宜自分で調べながら読み進めてください。

全部だと冗長になるので、パターンの上から2つについての検討をしていきたいと思います。基本的には、メリット/デメリットになることを整理し、評価することになります。

ローテートはログ出力側で行うか、外部で行うか

まず、アーキテクチャの基本的な考え方として、複雑度を下げるというものがあります。これは、できるだけ単一の機能に絞った方がよい、という観点と、一方で分割しすぎると協調が大変になるので適切な単位で切り分けましょう、という観点の2つがあります。そういう意味では、ログの出力とローテートを分けることは一見適切な分割に見えますが、ログファイルというリソースを共有することになりますので、処理の主体を分割すると、その間に協調が必要になります。つまり、出力中はローテートしないようにしよう6)厳密には、出力中にローテートしないようにするか、出力中にローテートしても問題がない作りにする、ということですね。そうなると考慮点が増えますので、一緒にした方が複雑度が下がりそうです。

では、別にすると何かいいことがあるかというと、いいことがあるわけではありません。せいぜい、ローテートの設定などをまとめることができるというものですが、誰がログを出力しているのかを理解せずにローテートだけ理解しても仕方ないので、それほどのメリットではないでしょう。しかし、いくつかの観点から、分けてもいい理由であればあります。

  • ログ出力側にローテート機能がない
    ログの出力だけであれば、機能的にはすごくシンプルです。極端な話、標準出力に出してOSの機能でファイルに出してもよいわけです。一方で、ローテートはそれなりに気を使うことがあります。logrotate のオプションの多さを見ればわかるでしょう。そのため、ローテート機能がないプロセス/プロダクトは結構多く、この場合は選択肢がありません。
  • move -> create 方式の採用と、プロセス再起動(もしくはログの出力リセット)を組み合わせることで、ログ出力に影響を出さない形でローテートできる
    move -> create の部分で詳説しますが、特定の条件を満たす場合にはログ出力に影響を出さない形で切り替えることができます。そのため、前述のデメリットがなくなる形となり、どちらでもよいということになります。

ただ、いずれにせよ、ログ出力側でローテートできるにもかかわらず積極的に外部で行うメリットはありません。よほど設計の統一性を重視する場合を除き、ログ出力側で対応した方がよいでしょう。よって、外部でローテートする場合は、ログ出力側では対応できない場合となります。

move -> create か、copy -> truncate か

logrotate の設定を見る限り、デフォルトでは move -> create となっており、copy -> truncate の方が特殊であること、そのコメントに「このオプションは、何らかのプログラムに対し、ログファイルのクローズを要求できないために、前のログファイルに永久に書き込まれてしまう(追記されてしまう)場合に便利です。ファイルのコピーと切り捨ての間に、非常に短い時間差があるので、一部のログデータが失われる可能性があることに注意してください。」7)日本語訳ページより引用させて頂きましたとあることから、 move -> create では「ログファイルのクローズを要求する」ことが必要であることがわかります。

ログファイルのクローズを要求する、とは何を意味しているのでしょうか。これは、ファイルを出力する、というのはどのように実現されているのかを理解する必要があります。
まず、ファイルに書き込む際にはプロセスがファイルをオープンします。ファイルをオープンするというのは、Linux的に言うと、対象となるinodeにひもづいたfd(ファイルディスクリプタ)を取得する、ということです(ということを linux file open inodeとか file descriptor inode とかで調べておきましょう8)ちなみにこの検索ワードを入れられるようになるためには、Linuxではどのようにファイルを取り扱うのか、という(浅い)カーネルレベルの知識が必要になります。これは linux ファイル 仕組み、とかで調べます。)。この後、ファイルをクローズするまではこのfdを使い、ファイルの読み書き(通常はログの場合は書き込みのみ)を行います。つまり、クローズして再オープンするまで、プロセスはファイルの場所や名前を意識しません。
一方で、move は inode番号をそのままに場所を変更します。つまりファイルの実体には影響を与えません(これは move inode とかで調べましょう。正直、このくらいまで単語が出そろえば、後は順番に調べていけば最後まで分かります。難しいのは、そこ(この場合は「ファイルに書き込む」ことに複数の段階があり、ローテートに影響を及ぼすこと)に気づくことと、とっかかりとなる単語(この場合はinodeやファイルディスクリプタなど)を手に入れることです。)。そのため、書き込んでいる最中(=fdごしに特定のinodeのファイルへ書き込む)に、move(=inodeに対するハードリンクの変更)をしてもログの書き込みに影響はないということです。
しかし、ファイルシステム側から見ると、moveしているので異なる名前のファイルになっています。これが続くと以下のような問題が発生します。

  • ローテートした意味がない(ローテート後ファイルが使用されない)
  • ファイル名で監視をかけるなどしていた場合に、ローテート後のログが監視されない

そのため、ローテート後に新しいファイルへログ出力先を変更する必要があります。これは、上述の通り「クローズして再オープンする」ことで実現できます。
どのようにクローズと再オープンをするかはプロダクトにより異なります。例えば、apache HTTP serverとかだと、apachectl graceful 9)ローテートだけの観点であれば、当然 restart や reload でも構いません。違いは調べてください。とかをしたりしますね。このようにプロセスを再起動するパターンから、設定だけを読み直すパターン、特定のシグナル10)UNIX系の、実行中のプロセスやスレッドに通信する手段のこと。を送ることで切り替えるパターンなどがあります。

ただし、このように切り替える方法を必ずしももっておらず、かといって出力元プロセスを単純に再起動できない場合があります。サービス提供を続けるためには絶対に落とせない場合です。このような場合に、copy -> truncate を行うことで、元プロセスへ影響を及ぼすことなくログローテートを行うことができます。しかし、これは逆に言うと元プロセスがログの切り替えタイミングを知ることができない、ということです。そのため、以下のような弊害が発生する場合があります。

  • ログが1行の途中で切り替わる(truncateするタイミングがログ出力中だと途中の文字で切れます)
  • 上記に伴い、監視がきかない/想定外の引っ掛かり方をする
  • 欠落/重複する場合がある
  • 一時的に容量が2倍になる(copyするため)

まとめ

  1. ログ出力元のプロセスでログローテートができる場合(例:java log4j)は、原則としてログ出力元で実施
  2. ログ出力元のプロセスが何らかの方法でログ切り替えを実施できる場合は、move -> create 方式で外部ローテート
  3. 1. 2. いずれも不可能な場合は copy -> truncate 方式で外部ローテート

PaaS/SaaSを扱う場合

最後に、最近ではPaaSやSaaSなどのサーバレスな仕組みが浸透してきていますので、簡単に触れて締めくくりとします。
PaaS/SaaSの場合で一番大きい点は、多くの場合でローカルディスクが存在しないということです。つまり、外部のサービスでログを収集することになります。これは、近年のパブリッククラウド基盤であれば標準のログ基盤が用意されているほか、3rd partyの監視サービスなども保有しています。これらのサービスは、多くの場合、httpsでログを受け付けます。

ログローテートという観点では、外部サービスは最強の選択肢になります。なにしろ、ローテートは必要ありません(!)。関心ごとの分離、というやつです。サービス側が必要に応じて勝手にやってくれます。
では、それで万事解決かというと、そんなことはありません。

  • ログの出力主体と記録場所が遠くなる。そのため、記録されていないログが出るリスクが上がる(実際に飛ぶこともしばしばあります)
  • ログ基盤にはよく分散系の仕組みが利用される。これらは Eventual Consistency の考え方に基づく場合が多く、出力したばかりのログは安定的に見ることができない(ログとして受け付けてはいるが表示されていない、という事象が起きる)
  • 実は永続化されずに消えていく、バーストしたら自動的に落とす、ログフォーマットを丁寧に設計しないと検索できずに事実上使えなくなる、などの問題を含む場合があり、サービス仕様を事前に確認・検証しておくべき
  • 逆に、誰でもログが見れる/誰でもログを書き込める、など、セキュリティ的な考慮を追加で実施しなければいけない場合もある

など、簡単に列挙するだけでも考慮点は多くあります。

ここで「アーキテクチャ設計として」重要なのは、「だからログはファイルで持った方がよい」ではなく、「世の中には銀の弾丸などないので、何を使うにせよメリット/デメリットがあると思って評価する」ことです。
大抵の新規サービス/プロダクトは、今までの問題を解決するために作られてきています。ログ系サービスの場合は、PaaS/SaaSなどのサーバレスや、その前のAuto Scalingなどのペットと家畜の文脈によりサーバは勝手に死んだり上がってきたりするという際にローカルディスクにログを保存するのでは困る、という問題を解決しにいっています。これは逆説的に言うと、対象としている問題解決する、ということです。従来の、一定量使用されてきたソリューションというのは、大抵よくできています。それでも解決しなかった問題を解決するためには、何かを犠牲にします。何が犠牲になったのか? それは本当に犠牲になってよいのか? 犠牲にできない場合、代替手段としてどう守るのか? といったことを考慮できて初めて、適切なアーキテクチャ設計ができるようになるでしょう。

さいごに

本当にすみませんでした。結局文量は変わりませんでした。特に反省はしていませんが、次からは希望的観測をしないよう心がけます。。。

今回はレガシーなテーマではありますが、アーキテクチャ設計を学んでいくためにはどう考えていくべきか、という観点から深堀をしてみました。いかがだったでしょうか。個人的には、こういう「考え方」にフォーカスしながら演習していくような記事はあまり見たことがないなと思っていましたので、もしお役に立てたようであれば幸いです。

NRIデジタルでは普段からこのようなディスカッションを行いつつ、よりよいシステム開発、ひいてはビジネスの発展に向けて日々活動しています。ご興味を持って話を聞きたい方、JOINしてみたい方はぜひご連絡ください

また、直接的な設計や構築・開発から一歩引いたアーキテクチャなどの考え方を社内で伝えていきたい、浸透させたいといったお話にもご相談に乗れるかと思います。遠慮なくお声がけください。

それでは今回はここまでになります。次回をお楽しみに。

 

References   [ + ]

1. 気のせいでしたね。すみません。。。
2. だからこのテーマを選んだわけですよね
3. 当然、後述するようなプロダクトがローテーションする場合は別です。OSとして機能/サービスを持っているか、という話です。
4. まぁ、あたってみて全滅したわけですが。このアプローチで当たるときもあります。
5. Linuxコマンドに標準で付属しているマニュアル。man logrotate のようにコマンドプロンプトで叩くことで確認できる。Redhatの資格試験でも使用可能なくらい、標準。
6. 厳密には、出力中にローテートしないようにするか、出力中にローテートしても問題がない作りにする
7. 日本語訳ページより引用させて頂きました
8. ちなみにこの検索ワードを入れられるようになるためには、Linuxではどのようにファイルを取り扱うのか、という(浅い)カーネルレベルの知識が必要になります。これは linux ファイル 仕組み、とかで調べます。
9. ローテートだけの観点であれば、当然 restart や reload でも構いません。違いは調べてください。
10. UNIX系の、実行中のプロセスやスレッドに通信する手段のこと。