はい、オッケーです。じゃあ行きますね。 普段仕事で AWS 使っていて、クラウドウォッチログスとかで、ログをリアルタイムに見るとかをしたりするんですけど、
今回はこれの中身とかの話をしていけたらなと思ってます。 はい、そうですね。仕組み的には、まずクラウドでも各マシンが物理的に動いているので、
そういうマシンでログファイルを書き込んで、 その内容を別のサーバーに送って、それでそれが我々が見ているという形になっています。
一昔前だとフレントDっていうソフトウェアがこういうのによく使われていたし、 クラウドの内部でもフレントDみたいにログを収集して別のサーバーに送るっていう仕組みが動いてるんだよね。
なので、今回ファイルのそういう扱いとかの話をいろいろしていければなと思っています。
はい、よろしくお願いします。フルエントDって言葉が出てきたんであれなんですけど、 フルエントD自体は僕も仕事で前使ってましたね。
Dockerとかで動かす場合でもサイドカーみたいな形でフルエントDのデーマを立てて、 ログを収集するみたいな感じで使ってたりしましたね。
そうですね、今でもフレントDは全然有用なソフトウェアなんですけど、 フレントD使ってないっていう人も最近は増えてきたかなと思うんですけど、
実際にはクラウドの内部でそういう処理って動いてる? そうじゃないと我々管理界面で見れないから。
だから実はすごく身近な処理なんですよね。
じゃあ今回はそのフルエントDを足掛かりにみたいな感じですかね、 ファイルのリラックスがどうやってファイル扱っていくのかみたいなところ。
その辺の話もしていきます。 じゃあまずはどこから行きましょう。
そうですね、まあ一昔前だとバーログなんちゃらみたいなところに、 あのいろんなアプリケーションがログを書き込んでいて、
そのファイルをフレントDでファイルを監視して、そのファイルを転送して、 っていう構成は割と一般的でしたと。
はい、一般的でしたね。例えばあれですよね、 EC2でアプリケーションサーバーを立てた時にバーログに測れるログファイルをローテートしつつ、
細かいローテートで他の保存先のサーバーに送るみたいなことをフレントDで書くみたいなところが一般的ですかね。
フレントDって本当にいろんな使い方できたんで、そういう使い方もあるし、本当に工夫次第でいろんなことができました。
今でもこのフレントDだったりとか、あとフレントビットっていうもうちょっと軽量な仕組みもあるんですけど、こういうのもありますよというところで、
今回はフレントDの内部実装にすごい深入りするというよりは、そのログ収集のファイルの読み書きとかその辺の話を中心にしていければなと思っているので、
その辺の話をじゃあしていきます。
まず以前からちょいちょい話していると思うんですけど、ファイルの読み書きって物理的にハードディスクとかディスクに対して書き込みしないといけないんで、
Linuxカーネルしかできないんですよ。
アプリケーション側から直接操作することはできないってことですね。
そう、直接できないと。なのでもちろんこのシステムコールを呼び出しますと。
今回はオープンというシステムコールをまず呼び出す必要があります。
ファイルを開くってことですね。
ファイル名を指定してオープンっていうのをやると、ファイルディスクリプターっていうのがもらえるんですね。
このファイルディスクリプターは本当に10番とか11番とか数字が返ってきます。
基本的にアプリケーションからはこのファイルディスクリプターに対してリードしたりとかライトしたりとかっていうのをしていくと、
実際にディスクに書き込みが走るみたいな感じになります。
ファイルディスクリプターで返ってくる番号っていうのはLinuxカーネル側が管理しているファイルの番号なんですかね。
そうですね。基本的には順番に、0012は予約されているんですけど、そこから先ですね。
3番以降がユーザーが使えるようなファイルディスクリプターってことなんですかね。
基本的にその辺がそういう数字が返ってくるっていう感じですね。
このファイルディスクリプターで重要なのはファイル名は入ってないっていう点ですね。
ファイル名は入ってない。実ファイル名はファイルディスクリプターには関係がないっていう。
そうですね。ファイルディスクリプターの中には実はファイル名の情報はなくて、
入っているのはこの後もちょっと話すんですけど、ファイルポジションっていうやつと、
どこまで読み込んだかとかどこまで書き込んだかみたいなファイルポジションの情報と、
あと何回か以前の回とかでも話してると思うんですけど、
フラグスっていう読み込むのか書き込むのかとかそういう設定をするやつとか。
あと、アイノードって言って、これファイルの実体がどこにあるのかっていう住所みたいな感じですね。
ディスク上のどこにあるのかの住所が入っている。それがファイルディスクリプターです。
このアイノードっていうのはファイルのパスとはまた違うってこと?
そうですね。
これどう違うんですかね?
これは本当に住所って感じですね。アイノードは番号で、
そのディスクのどこにあるかっていう物理的にどこにあるのかっていうのがアイノードですね。
ディスクの中に割り当てられた番号がまた別であって、そこを指す、住所を指す番号ってことですね。
1番とか2番とか。
なんかちょっと違うかもしれないけど、そのドメインとIPアドレスみたいな。
ドメインがファイル名で、アイノードがIPアドレスみたいな、そんなイメージでいいかなと思います。
今回の話で言うとファイルパスがドメイン名で、アイノードが実体のIPアドレスって感じですね。
なんとなくわかりやすい例ですね。
で、ファイルにはその現在位置、さっきのポジションってやつがありますと。
どこまで書き込んでるか、どこまで読み込んでるかっていうようなポジション。
そうですね。なので例えばリード、ポジションが100ってなってて、リードするとその100バイト目の続きから読み込みが始めます。
バイトなんですね、そこのポジションは。
そうですね。っていう感じで動いているという感じですね。
で、これがちょっと罠っぽいことが実はあって、リードするとファイルポジションがずれるんですよ、読み込んだ分。
そうですよね、だってどこまでのインデックスなので、ファイルポジションはどこまで行ったかみたいな話なので、もちろん読み込んだらずれてもらわないと困ることになりますけどね。
読み込んで、また読み込んだらさらっと続き読み込めるって感じになるから、そうじゃないと困るんですけど。
困りますね。
けどこれってよく考えてほしくて、読み込んでるけどファイルポジションは変更してるからファイルポジションに対して書き込みが走ってるんだよね。
なるほど、ファイルポジションの上書きをしているってことですよね。
そうですね。
ファイルディスクリプターという構造体の中のファイルポジションを上書きするという処理が入ってるってことですね。
そうですね、それがリードって書き込みしなそうじゃないですか。
確かに、なるほど、ファイルディスクリプターに対してリードを行うとライトもしてるってことですね。
そう、実はファイルポジションを書き込みが走っていると。
ちょっと、はい、理解しました。
これ意外と罠で、平行プログラミングで適当にリードしまくってるとファイルポジションがずれちゃうので結構めちゃくちゃになる可能性があり得る。
なるほど、平行で同じファイルディスクリプターを共通で平行でいじってる場合、ポジションがずれてしまうから処理が変な感じになっちゃうんですね。
そう、可能性があるってことですね。
可能性ある、確かに。しかもそれがリードであったとしても起き得る。
リードだから大丈夫でしょうと思っていると、実はリードって書き込み処理があるので、平行に呼び出しちゃダメだよねっていう話になる。
ちょっと面倒くさいですね。これ解決するには、平行でやっている場合は別々のファイルディスクリプターでやる必要性がある?
いや、それよりも別のやつが良くて、GoだったらReadAtとかWriteAtっていうAPIがあって、そのポジションを指定するっていうやつがあります。
もう直指定すると。
そう、直指定する。ファイルポジションの続きから読むんじゃなくて、ポジションのここを読むっていう指定ができますと。
オープンしてリードするときに指定できるんですね。
オープンしたやつ、ファイルディスクリプターに対してReadAtとかWriteAtでそこに対して書き込み、ピンポイントで書き込みとか読み込みができる。
なので基本的に平行プログラミングで使いたい場合、平行で呼び出したい場合はReadAtとかWriteAtみたいなポジションを指定するAPIで呼び出した方がいいですね。
なるほど。ちなみにGoであったとしてもここはあれですね、LinuxカーネルのシステムコールだからGoは別に再実装してるわけではないですね。
ここは普通にシステムコール呼んでるときですね。
そう、普通にシステムコールの話なんで、Goでも同じですね。
もちろんこれは別の言語だったらちょっと違うみたいなところ。
基本あんまないですよね。ファイルの読み書きってシステムコール経由でやるもんなんで、Linuxカーネルの世界なんですよ。
なのでどの言語を使っても基本同じだと思いますね。
じゃあ同じことが今説明した同じことが他の言語でもできる。
基本的に入れるはずですね。
じゃあ割とシンプルですね、使用としては。
リードする場合はファイルポジションが書き込みされちゃうので、平行プログラミングする場合はリードアットとかライトアットすべきと。
自家指定してしまえっていうようなところですね。
覚えておいてほしいところ。これ意外と罠になるんでたまにやると。
結構めんどくさいですもんねそこについて。
そうですね、ちょっと注意点ですね。
ただ今回最初に話した通りフレントDの話を最初してたと思うんですけど、
フレントDだとログを1回読み込んでその処理をして送るとか処理した後にまたファイルを読み込んでその続きを読み込んでっていう風にロジックとしてはなっていくので、
基本的にはずっとリードしていく感じになりますね。フレントDの場合は。
フレントDの場合は1行ずつに処理を挟めるからってことですかね。
フレントD側がファイルを読み込んでそれに対して何らか処理してそれをまた違うところに流すみたいないう処理をするので基本はリードだよねっていう。
そうですね、リードアットじゃなくて基本はリードし続ける形になるかなと思います。
これで複数プロセスに対してログを書き込んだらどうなるのかっていうのを今度は考えてみたくて。
この時に今までもずっと話してきたんですけどオープンの時にフラグっていうのを渡せると。
この時にオーアペンドっていうのを渡せるんですよ。
オーアペンドっていうのを渡すと追記しかしないってやつになるんですね。
モードとして追記しかできないモードになるんですね。
そうですね、これつけないと普通にログファイルとかだと上書きとかもできちゃうんで、ログファイルって上書きされたら困るじゃないですか。
消えちゃうんで困りますね。
消えちゃうんで、だからログファイルとかを使う場合はオーアペンドを常に使うべきなんですよ。
なのでオーアペンドつけておくと書き込んだ時に常に最後に追記されるっていう形になるので。
必ずファイルの末尾に最新行が入るっていう。
そうですね、なのでそのオーアペンドで開いたファイルをどんどんアプリケーション書き込んでいって、それをフレントDとかが読み込みに行くっていうのが理想というか、そうやらないとしんどいと思うので、多分それ以
外の実装は基本ないんじゃないかなと思いますね。
そういったシンプルな仕組みになってるっていうことですよね、オーアペンドを使って書き込んで。
気をつけてほしいのは、Writeというシステムコールでファイル書き込みできるんですけど、フレントDが読み込むログって基本的に1行を書いて開業して、また1行を書いて開業してっていう風になってることが多い
と思うんですけど、
そのWriteシステムコールをその1行丸々でWriteシステムコールを読んでほしいんですね。
これ何でかっていうと、単純に例えば文字1回途中までWriteして、そしたらまた途中までWriteして開業してみたいなWriteで全部別々にやってたら、他の処理と混ざっちゃう可能性があると。
平行で動いているプログラムが間に挟まる可能性があるってこと?
そうするとログファイルがめちゃくちゃになっちゃうんで、必ず1行のログを書き込むのに1回のWriteのシステムコールでバシッと書き込んでほしい。
開業までしっかり書いてるようにっていうところですね。
っていう風にやらないといけないと。
ちなみにちょっとここで疑問があるんですけど、1回のWriteのシステムコールで書き込むは分かるんですけど、それがほぼほぼ同時に発生したらどうなるんですかね。
ファイルの方はロックされてるんですかね。1回AというWriteとBというWriteがほぼほぼ同時になったら、
1回Aの方でファイルをロックする。書き込み終わったらBの方が走るみたいな感じになるんですかね。
だからオーバーペンドつけてほしいんですよ。
オーバーペンドつけないと、オーバーペンドつけてればLinux Kernel側がその辺を考慮してちゃんと順番にやってくれるんですね。
逆にオーバーペンドつけないと、そのアプリケーション側でちゃんとロック取って1個1個書き込まないとそういう処理にはならないし、
あと例えばそのPHPみたいにプロセスが複数個あるみたいなやつだとそんな強調してできないんですよ書き込みが。
なのでオーバーペンドをつけてほしいって言ったのはそういう理由ですね。
なるほど。じゃあオーバーペンドをつければLinux Kernel側がいい感じのマネジメントしてくれるんですね。
Linux Kernel側がシーケンシャルにちゃんと書き込むはずなんですけど、これも例外があって、
なるほど。
でかいと、具体的に言うと4KBくらいの4KBよりも大きいやつを一気にライトしようとするとLinuxKernelの内部でバラバラにされちゃう可能性があって、
分割して書き込みが走る場合があるってことですかね。
そうです。分割しちゃう可能性がある。
なるほど。
なのでこの4KBより大きいとかそういう場合はもしかしたら混ざるかもしれない。オーバーペンドつけてても。
なるほど。ちょっとログとかの場合読めないですよね。ぶっちゃけここら辺の。
そうですね。これについてはまあ起こるかもねっていうぐらいではあるんですけど。
なるほど。確実ではないけどっていう。
可能性として起こり得る。けどまあそんなすごいでかいやつはまあまあ書き込まないでねっていう感じではあります。
一行一行のログが普通にアプリケーション側からもそんなに額化するような形で出さないでねっていうのが大前提あるっていうところですね。
そうですね。そんなでかいの書き込まないでねっていう。
一行として読める感じにしろよっていう感じですね。
そうですね。そういうふうにしてれば基本混ざることはない。
ただやっぱりこれ結構自分昔疑問だったんですよね。
具体的に言うとそのPHPのアプリケーションサーバー見ててアクセスログとかそういうログファイルをTLFとかで開くじゃないですか。
そうすると絶対混ざんないんですよね。一行一行。
綺麗にちゃんと出てくる。
けどPHPってプロセス全部バラバラなんですよね。
これなんで混ざんないんだろうなって思ってたんですよ。
それでいろいろ調べていったら今言ったようなことが分かってきたっていうところですね。
ちゃんとオーアペンド使ってるから大丈夫なんだっていうようなところがあった。
いるからLinux Kernelがちゃんとシーケンシャルにちゃんと処理してくれてるから大丈夫なんだっていうことを調べてわかったって感じですね。
便利ですね。オーアペンド。
オーアペンドめちゃくちゃ便利なんでログファイル使うときは必ず使うようにしましょうってところですね。
その代わりなんかオーバーヘッドあったりするんですかね。やっぱロックしてるから書き込みが若干ブロックされるんで詰まるとか。
そういうのはあるけりっていうかな。
正直CPUよりもディスクなんでこの手のやつって詰まるとしたら。
ただディスクもログについては詰まりにくい。
なんでかっていうとハードディスクとかでもシーケンシャルに書き込みとか読み込みはそんな遅くないんですよ。
ディスクで遅いのってランダムにリードとかライトすると遅いんですよね。
なるほどアクセスが散ってると遅いけど連続してればそこまで遅くない。
そうハードディスクでも連続してる分にはそんなに遅くない。
だからオーアペンドつけてるログファイルみたいなのは基本そんな遅くなんないです。
じゃあそんなに問題ないですね。
ログファイルに関してはそんな問題ない。
連続であれば。
シンケンシャルリードとかって確か言うんだけど、連続した書き込みとか読み込みはそんなに遅くないです。
じゃあ問題ないんですねそこに関しては。
基本的には。
それよりもオーアペンドちゃんとつけて欠損がないようにするとかそっちの方が重要ですね。
ここちゃんとしてなかったらそもそも破綻するからってことですね。
めちゃくちゃ重要ですね。ログファイルに書き込む処理を何らか書く場合は。
次なんですけどさっきファイルディスクリプターに実はファイル名ないよって話したと思うんですけど、
その辺の話が出てくるんですけど、
ファイルって書き込み続けられないんですよ。
書き込み続けられない。
そのディスクが余っちゃうからです単純に。
フルディスクになってしまうからってことですね。
ディスクがフルディスクになっちゃうので書き込み続けるっていうのは無理で、
ログローテートって言われる処理。
たまにログファイルをファイル名変えて、
古いログファイルはZzip圧縮するなり消すなりなんかしないとディスクが埋まっちゃうと。
そうですね。
じゃあこれログローテートってどうやるのっていうところで、
例えばファイルMVするじゃないですか。
ファイルをアクセスログっていうファイルがあったら、
アクセスログ.tmpとかそういった形でファイル名変えるみたいなことですかね。
そうですね。ログローテートだったら.1とか数字がつくと思うんですけど、
そういう風にMV例えばするじゃないですか。
はい。
それだけでいいんですかっていうところですね。
なるほど。
そのファイル名をMVの場合はあれですかね。
ファイル名を変えて、ただその場合新しい、
iノードとかは変わってないんですかね。
iノードは変わらないです。
変わらないんですね。
じゃあファイル名もファイルディスクリプターに入ってないってことは、
MVしたとしても同じところに書き込み続けるってことですね。
そうです。なのでファイルディスクリプターで開くじゃない、オープンするじゃないですか。
でそれ書き込みしてます。でファイルMVされました。
けどファイルディスクリプターのiノードの情報は何も変わってないので、
そのまんま古い方に書き込み続けちゃうんですよ。
意味ないですね。
意味ないし、むしろこれって事故を誘発しちゃうんですよね。
ファイル名が変わっちゃってるから。
ファイル名変わってるから、その想定してないファイルに対してずっと書き込み走っちゃう。
ログとかだったらロストすることにもなるし、ディスクが余っちゃうから事故にもなるし、
これもう100があって一理なしみたいな、やばい状況になるんですよこれって。
確かに。一番最初アプリケーション側からオープンしたファイル名と実体が違うファイル名になっちゃってるんですよね。
違うファイル名にずっと書き込んじゃってる。
でこれやばいじゃんっていう話で。
事故になる絶対。
でこれどうすんのっていう話で、これは物によるとしか言いようがないんですけど、
これはちょっと面倒くさいんでまた後で話すんですけど、その前にフレントDの話をちょっとすると、
フレントDも同じなんですよね。フレントDもリードしてるわけじゃないですか。
でファイル名MVされましたけど読み込みはしてるからフリフォをずっと読んじゃうんですよね。
iノードで読み込んでるからですよね。
そうですね。なのでそれどうするのっていうところで、
これはさっきからiノードの話してるんですけど、要はファイル名だけだと分からないと。
でiノードを見ないとそのファイルの実体分からないんですよ。
分からない。
なのでファイルをオープンしてみて、iノードを見て、iノードが一緒だったら同じ。
でファイルをオープンしてみて、iノードが違ってたらログローテードされてるって判定できるんですよね。
実体が変わってるからファイルが擦り替わってるよねっていうのが分かるってことですよね、iノードを見ることで。
そうですね。
なのでそのファイル名を、例えばバーログ、例えばアクセスログみたいなファイルをprintdが監視してたとして、
で.1っていうのにムーブされましたと。
でprintdは数秒に1回とかの頻度でバーログアクセスログをオープンし直してるんですね。
オープンし直してファイルディスクリプターでiノードを見て、今自分が読み込んでるファイルのiノードナンバーと同じかどうかっていうのを確認してるんですよ、実は。
なるほどチェックしてるんですね毎回。
定期的にチェックして違ってたらログローテッドされてるじゃんって言って新しいファイルに切り替えるっていう処理が実はprintdやってます。
っていうことはその仕組みを聞くと思うのが、ファイルに書き込み続けますと、
ログを書き込んでますとアクセス.ログで、でその時にムーブしますとmvでアクセス.ログ.1にしますと、
その時その瞬間ですねこのmvをした瞬間はもしかしたらprintdはアクセス.ログ.1を読んでる可能性があるってことですかね。
一瞬だけじゃなくて数秒間ぐらい読み込んでますよ。
なるほどじゃあちょっとバッファーがあるんですね。
そうですねけどそれは逆に一瞬で切り替えない方が良くて、まだprintdが処理してない可能性があるじゃないですか。
ムーブされた直後って。だからprintdはprintdで、mvされたからすぐ読み込みやめていいってわけじゃなくて、
mvされてもまだ読み込んでないやつは全部処理しきってから次のファイル読み込みに行かないといけないんですよ。
ファイルの中に欠損があるといけないからですね。
欠損あると事故になっちゃうからちゃんと処理しきってから次のファイル行かないといけない。
で言うとじゃあiノードが切り替わったっていうだけじゃなくてファイルの末尾も見てます?終端も。
基本的にファイル名変わったら数秒後に読み込み切ったら新しい本になるので、その後もファイルの書き込みが実はありましたっていうのはprintdで気づけないですね。
なるほどじゃあ終端までは見てないってことですね。
終端までっていうかその基本書き込みが走んないやつなんですよ。ログローデされたら。
で確か何秒間見るみたいな設定確か5秒とか10秒とかデフォルトがそんぐらいだった気がするんですけど、その設定で変えられるんですよここって。
でその設定、例えば5秒だとして5秒間はフリーファイル見ますと。でその後に書き込みが走っててもprintd気づくことはないですね。
まあそこ以降は諦めるというか。
諦めるっていうかそこに書き込んでる時点で何かおかしいんで。何かおかしいって話になりますね。
あの書き込み側がおかしいよねっていう。
そう書き込んでるアプリケーションがおかしい。
なるほど確かに。ファイル名が変わってるのにちゃんと切り替わってないよねっていう。
そういう話ですね。
理解しました。
一応iノードナンバーって話してるんですけど、実はデバイスIDっていうのもあって、そもそもディスクが別だったらたまたまiノードナンバー一緒ってケースがあり得るんですよ。
ディスク違うから絶対違うんだけどファイルとしては。たまたま一緒になる可能性があるので、一応厳密に見たいんだったらiノードナンバーだけじゃなくてデバイスIDも見る必要があります。
デバイスIDとiノードナンバーの突合というか組み合わせで。
両方ともあってたらこれは同じファイルだねってなる。
で実はGoだとOS.sameファイルっていうそのものズバリな関数があって、デバイスIDとiノードナンバー見てくれます。
じゃあOS.sameファイルっていうAPI呼ぶと引数で2つのファイルの情報ですかね、パスを取って。
OS.statってやつで返ってくるやつなんですけど、それ渡すとファイルディスクリプター入ってるのでそこを見てデバイスIDとiノードナンバー一致しているかっていうのを確認してくれます。
じゃあこれ使えばローテイトしているかどうかみたいなところの判定で使えると。
ができると。でそれはfrontdも内部でやってますね、同じ処理を。
便利ですね、sameファイル。
そうOS.sameファイルっていうそのものズバリなファイル名で、ファイル名じゃない関数があるのでそれを使うとこう場合はできます。
そういった形でログローテーションしているかどうかというかファイルが切り替わったかどうかっていうところが判定できると。
そうですね、frontdはそれを定期的にログファイル開いてやっているかどうかってちゃんと確認してくれます。
そういう処理がないとfrontdみたいなアプリケーションは動かせないってことですね。
一つのファイルにずっと監視することになっちゃうってことですよ。
それは無理なんで。
無理ですね。
じゃあfrontdの読み込み部分はこんな感じなんですけど、
じゃあ書き込み側、さっきちょっとごまかしたんですけど、書き込み側どうするのかっていうところですね。
書き込み側もあれですよね、同じファイルにずっと書き込むことになっちゃいますもんね、iノードが一緒だったら。
ファイルディスクリプターが一緒で、かつiノードが一緒だったらもうずっと。
同じファイル書き込んじゃう。
同じ問題がありますよね。そっちもなんか切り替えないといけない。
そう、書き込み側が切り替わらないとまずいことになるんですよ。
じゃあまず一番単純な、自分が一番単純だと思ってるやつはPHPですね。
PHPはどうやってるかっていうと、PHPってリクエストのたんびにプロセスが起動して、
処理をPHPのコードを実行して、レスポンス返したらもうそのプロセス死ぬみたいなアーキテクチャになってるので、
リクエストきました、でファイルオープンしました、でライトしました、でクローズしましたっていうのを毎回毎回やるんですよPHPって。
前リクエストごとにすると。
そう、なんでレスポンス返した後クローズされるので、
でリクエストきました、でライト、リクエスト切ってオープンしました、でその後にログローテッドされました、
でライトしました、で古いファイルに書き込んじゃうんです、この場合。
けどレスポンス返したら次来たリクエストは新しいログファイルに対して書き込みが走るから、
要はレスポンス返した後であれば大丈夫でしょうっていう、そのちゃんとログローテッドされるよね。
なるほど、リクエストがあった後、Aっていうリクエストがあった後、MVがログのローテッドが走って、
別のファイルに切り替わったとしても次のリクエストでは、次のリクエストでオープンされるファイルは新しい方でオープンされるから問題ないってことですね。
そうですね。
PHPの場合は。
かなり自然というか。
考えることは一番少ないと思います。
ただやっぱりリクエスト来てすごい、例えば何秒も処理に時間かかっちゃうとかだと、
その数秒後にログファイル書き込んだりとかするので、
そういうすごい処理に時間かかっちゃって、たまにログファイル書き込むの遅れることあるみたいな状況だったら、
さっき言ったフレントDのタイムアウトの時間選べるの設定できるので、ちょっと長めに指定しておくとかそういう工夫はした方がいいかもしれないですね。
長い処理の間ファイルディスクリプターを握ってしまってるからですよね。
そうですね。
古いiノードで。
フレントDがもう見なくていいやと思った後に書き込みが走る可能性があるのであれば、その辺の設定は変更した方が良い。
っていうのがPHPの場合ですかね。
この話でもう一個大事な話があって、
ファイルをMVするじゃないですか。
その後ファイル無くなってるんですよMVした後って。
その瞬間無くなってますよね。
無くなってるじゃないですか。
アクセス.ログっていうのをアクセス.ログ1にMVしたらアクセス.ログというものはないですよね。
ないですよね。
じゃあそれ誰が作るのって話で、
これもオープンのフラグを使うのを自分はお勧めしていますと。
オープンのフラグでOCREATEっていうやつがあるんですよ。
OCREATEっていうのをつけておくとファイルが無かったときは自動で空のファイル作ってくれます。
なのでログファイルはOCREATEとOPENDとあとLite-onlyのやつの3つをつけるのを自分はお勧めしてますと。
じゃあアプリケーション側がログファイルが無かったら作るようにするオプションを、モードをつけておくべきってことですね。
そうですね。自分はそう思ってます。
それをつけておけばログローテートはMVするだけ。
アプリケーション側は無かったら勝手に作る。
ってやると自分は一番事故が少ないと思ってます。
シンプルですね。
ログローテートはNOCREATEっていう指定をしておくとログファイル作らないのでこういう指定ができます。
なんですけど、なんでこんな話するかっていうと実はね、クリエイトすることもできるんですよログローテートで。
MVしたときに先に使うとか。
MVした後にログローテートにファイルを作らせるっていうオプションが一応ある。
うん。
けど自分はこれはね、使わない方がいいと思っていて。
なんでかっていうとログローテートのソースコードを読むとOEXCLっていうオプションを指定してログファイル開いてるんですよ。
このOEXCLっていうのが結構厄介で、
競合?すでに同じファイルがあるとエラーになるんですよこれって。
なるほど。もうすでにEXISTだとファイルが存在するとエラーになってしまう。
そうですね。ちゃんとログローテートがログファイル作るって責務を持たせるんだったらログローテートとしてはちゃんと厳格にやりたいんですよね。
なるほど。じゃあどちらが責務を持つかなんですね。
誰がログファイルを作るっていう責務を持つかっていうのでログローテートに任せるんだったらログローテート以外がファイル絶対作っちゃダメなんですよ。
けどPHPでAUCREATEって付けちゃうとPHPにリクエストが来たら作っちゃうんですよね。
どっちが先に処理走るか分かんないってことですよね。アプリケーション側がファイルを作るのかそれともログローテート側がファイルを作るのか。
やっぱりそのMVした瞬間にファイルが消えるのでログローテートがそのファイルを作るまでのその一瞬の間にPHPがログファイルを作っちゃう可能性があると。
こういう処理が起こるとなんかエラーになる可能性がありますと。
なのでログローテートでクリエイトさせると結構危険なので自分は辞めた方が良い、そのノークリエイトを指定した方が良いと思っていますと。
なるほど。ちょっとここでログローテートについてちょっと補足というかちょっと聞いてみたいんですけど、
このMVするのはログローテートの責務とか役割なんですかね。ログローテートっていうものがやってることなんですかね。
別にログローテート使わなくてもいいんですよね。Linuxだとログローテートっていうのがシステム仕組みが入ってて、
ログローテートでこのバーログアクセスログのログローテートの設定とか書けるんですよ。
けど別にこれを使わなくても良くて、例えばクーロンでファイルをMVするっていう処理を毎回動かすとかっていうのをやれば別にいいんですけど、
ログローテートってすごい機能がすごく多くて、例えばその何個までログファイル残しておくっていう設定もできるし、
あとそのGZIP圧縮を自動でやる設定もできるし、ログファイルでやりたい処理っていうのが設定で書けるんですよ。
すごいシンプルに書けるってことなんですね。やろうと思えばクーロンでシェルスクリプトとか登録しておけば似たようなことはできるけど、
全然できるけど、ログローテート書いた方が特にGZIP圧縮するとかそういう複雑なことやるんだったら、
ログローテートにした方が楽。
理解できました。そもそもLinuxの中にログローテートっていうような機能が入ってるから、
デフォルトで入ってるんで、
そっち使った方がいいよねっていう。
そうですね。
理解です。で、そのログローテートを使う場合はノークリエイトにした方が安全だよねっていう。
安全というかそのアプリケーション側でオークレートつけてればですけどね。
ログファイルを開くときにオークレートつけてるんだったらログローテートでノークレートってつけといた方が自分はお勧め。
ちゃんとどっちがファイル作成するっけっていうのを普通に開発者が決めろよっていうようなとこですね。
そうですね。なのでこの辺は誰がそのファイルを作るのかっていうのを考えないといけない。
重要ですね。
すごい今聞いてて思ったのが、さっきの例だとPHPですごいシンプルだったじゃないですか。
1リクエストで毎回オープンしてファイルディスクリプターを取り回す、新しく作ってもう一回書き込むみたいな。
これファイルディスクリプターを使い回す場合ってどうなるんですかね。
そうそこなんですよ。PHPみたいなアプリケーションって実は結構珍しくて、
例えばエンジンXでアクセスログとかって保存できるんですけど、
エンジンXはそれなんですよ。ログファイルをオープンしてそのファイルディスクリプターを保持し続けてログファイルを書き込むっていう風になるんですね。
普通に考えたらそっちの方がリソース活用としていいですよね。
そうです。
正直PHPのやり方は普通にCPU無駄です。
なんか不合的プログラミングみたいな感じ。
毎回オープンするから結構無駄してますリソース的には。
ファイルディスクリプター使いました方がスピードが出ますと。
ただ、じゃあiノードの切り替えどうするんですかっていう話になるってことですね。
そうです。
実際興味ある人やってみてほしいんですけど、エンジンXでアクセスログとか適当にMVして、
もう一回リクエストすると本当にフリーファイルに対して書き込みが走り続けちゃいます。
じゃあ特にそこに対するケアっていうのはないってことですね。
エンジンXの。
エンジンX側には自動でやるって機能はない。
自動でやりたい場合はさっき言ったフレントDみたいに定期的にオープンして、
iノード比較してっていう処理を書かない限り無理です。
っていうことをエンジンX側はもちろんやってないってことですかね。
もちろんやってないし、そんなのやってるのは基本的にフレントDぐらいですよ正直。
なんでやらないですと。
じゃあどうすんのって話なんですけど、
エンジンXに関しては、USR1っていうシグナルを送ると、
ログをリオープンしてくれるっていう機能があります。
これはエンジンX側の機能であります。
さっきから言ってるログローテートってやつの設定で、
ポストローテートってやつがあって、ローテートした後にシェルスクリプトで処理かけるんですよ。
フックみたいな処理がかけるんですね、そこで。
なのでローテートした後に、このエンジンXのプロセスに対してUSR1を送るっていう処理を書いておけばログローテート上に。
まずログローテートがファイルを芽吹いします。
その部分の処理が終わったら、
USR1シグナルをエンジンXに対して送ってくれるので、
エンジンXがログファイルをオープンし直して、
それでログローテートの処理が完了する。
ってことはこのUSR1のシグナルをエンジンXがサポートしてるっていうのは、
こういうログローテートの機能を考慮した上でこういう処理をしてるってことですね。
ログローテートで指定するためにUSR1シグナルに送るとログをリオープンするっていう機能がついてるんですよ。
ここでちょっと考えたいのが、じゃあエンジンXが毎回ファイルディスクリエーターをリオープンし続けるっていうのは、
そもそもパフォーマンス上良くないからそういうことはしたくない。
エンジンXみたいなことをするとパフォーマンス落ちちゃいます。
なんだけどリオープンしないといけないタイミングがあるから、
このシグナルをサポートしてUSR1来たらリオープンしましょうねっていうようなことをしてるってことですね。
そうです。
理解です。いや確かに。これがないとそもそも無理ですね。
そうですね。で、デビアン、例えばデビアンでアプトインストールでエンジンXとかってやると、
さっきノークリエイトがお勧めって言ったんですけど、
実はデビアンのアプトで入るやつとかだとログローテートでクリエイトされています。ログファイルが。
じゃあエンジンX側ではクリエイトしない?
用にされていて、でもあのさっきの問題は発生しなくて、何でかっていうとログローテートがUSR1シグナルを送るまではログファイルをオープンしないんで、
エンジンXは。だからさっき言った競合の問題っていうのは発生しないし、
あとこれのメリットはそのログローテートでクリエイトするときに権限を指定できるんですよ。
ログローテートが。
ファイルの権限を指定できる。
そうですね。どのユーザーで作るのとかパーミッションとかそういうのログローテートの設定として書けると。
なのでエンジンXで開くと基本的にエンジンXの実行ユーザーでログファイルってできちゃうので、
その辺の指定がログローテートでログファイル作ってからエンジンXが書き込みし始めるっていう風にやると、
その辺のログファイルの権限とかも設定できるので、
エンジンXのアプトで入るやつはそういう設定になってたりしますね。
なんか倫理化になってますね。
ポストローテートでフック処理でエンジンXにUSR1を送った瞬間にはもうすでにファイルができてるってことですもんね。
瞬間っていうかその前ですね。
ポストローテート前にはクリエイトしてるからってことですね。
処理が終わってポストローテートが呼ばれて終わりって感じなんで。
すごいよくできてますね。
これやると綺麗にいくと。
すごく綺麗。
パフォーマンスを出したいんだったらこういう形にするべきですね。
なのでこれは結構おすすめのやり方になりますね。
もう一個やる方法としたら、さっき言ったフレントDの方法ですね。
定期的にリオープンして、同じファイルじゃなかったらローテートするみたいなのを自力で書くっていう方法もなくはないけど、
めんどくさいから大体の人は多分シグナル送るの方がいいんじゃないかなと思っています。自分は。
めんどくさいしパフォーマンス上もよろしくないですね。
そうですね。バックグラウンドで動かしてれば無視していいと思うんですけど、
処理として書かないといけないからめんどくさいですね。
アプリケーションカバーでわざわざそれをやるのはめんどくさい気もするんで。
で、まあまあ大体そんな感じなんですけど、
実は他の方法もあったりします。
これがiNotifyってやつなんですけど、
iNotifyっていうのを使うと変更イベントみたいなのを取れるんですよ。
なんでこのファイルがムーブされたとかファイルがライトされたとか、
そういう情報が全部イベントとして取れるようになるのがiNotifyってやつです。
イベントがストリームみたいな形で流れてくるイメージですかね。
そうですそうです。
ファイルの変更履歴みたいなところが。
変更履歴っていうか変更されたら変更されたよって情報がやってくるって感じですね。
じゃあそれをキャッチすれば同じようなことができる。
そうです。
新しく作成されたよ、MVされたよっていうのをキャッチして、
ファイルをリオープンしてファイルディスクリプター作り直すみたいな。
そんなことができるってことですね。
できますね。で、FrentDは実はiNotify対応してます。
なのでそのiNotifyでも実は取得することができる。
それ何かどっち使えばいいんですかね。
これがね、基本的に自分の意見としてはiNotify使わない方がいいと思っていて、
実はiNotify途中からiNotify無効にするってオプションが出てきたんですよ。
これは何でかというとiNotify無効にしたい人が結構いたからなんですよね。
何で無効にしたいんですかね。
これがイベントを絞る方法がないんですよ。
全員イベント来ちゃいますね、iNotify設定しちゃうと。
いっぱい来すぎると情報が。
そうですね、ライトも全部来ちゃうから、
ファイルのMVだけ情報欲しいとかできなくて、
何か書き込みとかあったら全部来ちゃうんですよ、イベントとして。
だからファイルのライトがすごいたくさんあるやつだと、
ものすごいイベント量発生しちゃって、
それだけでCPU使用率結構上がっちゃうんですね。
なるほど。
でも逆にほとんど例えばアクセスがないですみたいなサービスもあるじゃないですか、絵の中。
そういうサービスだとフレントDからすると、
フレントDが定期的に読み込みに行くんだけど、
いくら読んでもどうせログファイルが書き込まれてないから何もすることないんですよね。
無駄だよってことですね。
そうなると無駄じゃないですか。
そういうアクセスがすごい少ないんだったら、
iNotifyで何かイベント来たらやる方がいいですよ、実は。
効率、リソース的な効率は確かにいいですよね。
そう、だからログファイルが大量に書き込まれているケースだと、
iNotifyは無効にした方が良い。
だってどうせ読み込みに行ったらあるんだから、書き込みが。
だからiNotifyは正直邪魔なんですよね。
なるほど。
じゃあそのアクセス頻度というか書き込み頻度で使い分ける方がいいかもなという。
自分はそう思ってて、やっぱりアクセスが多いところはiNotify無効にしといた方が、
マジで結構使うんでCPU、iNotify有効にしていると。
なので基本的には、PrintDの用途だったらiNotifyを無効にした方が良いと自分は思ってますね。
アクセスがそこまでないサービスであったとしても、
別にiNotifyを絶対使わないといけないわけじゃないですもんね。
ではなくて、結局数秒に1回くらい読み込み引いたりとかファイル確認したりしてるから、
要はその数秒のラグすら許したくないんだったらiNotify使うしかないんだけど、
現実的にそういうシステムがないと思うので、
PrintDに関しては自分はiNotify使うメリットはあんまりないんじゃないかなと思ってますね。
別にイベントで見る必要性はないよね。
ないよねと自分は思っていますと。
けど、iNotifyが便利なケースってあって、
それこそ多分普段我々やることあるんじゃないかなと思うんですけど、
例えばフロントエンドの開発とかでファイル変更したら自動でリスタートするみたいな仕組み。
いわゆるホットリロードって呼ばれる仕組み。
最近のフレームワークとか結構あるんですけど、
これ実装しようとするとiNotify使わないとかなり厳しいはずなんですよね。
イベントが飛んできてアップデートが例えばJavaScriptの上書きがあったよっていうのを検知して、
即座にそのiNotifyで検知した上でビルドしてホットリロードするみたいな仕組みですよね。
そういう仕組みはやっぱりiNotifyじゃないとかなり作るの大変なんですよ。
ずっと1秒に1回ぐらいウォッチしないといけないって感じですよね。
ファイルを読み込みに行って変更されてるかとかそういう情報を取ろうとすると前のファイルの情報も必要だし、
なんかすげえ大変そうなんですけど、iNotify使っておくとそのファイル変更されたらイベントでそして取れるので、
ホットリロードみたいな仕組みを実装したいんだったらiNotify使うと結構簡単に作れる。
確かに。あとはソースコードを書き換えるみたいな手で書くだったりいう場合は、
iNotifyのイベント全部出るって言っても現実的なところだよね。
そんな大量には来ない。ログファイルレベルでは来ないんで全然。大した話じゃない。
だからiNotifyは基本的にそういうファイル、我々が手でファイル書き換えるとか、
開発で使うだったら絶対iNotifyが便利なんですよ。
だけどフレントDみたいにログファイルの読み書きっていう観点だとiNotifyは自分はそんなに便利じゃないと思ってます。
ちょっとマッチしないですね。
ファイルのムーブの情報も取れるんで便利ではあるんですけど、
フレントDで使うのはちょっとナンセンスかなと自分は思ってますね。
LinuxKernel側で、OS側でフィルタリングできればよかったねっていうような感じですかね。
そうですね。iNotifyでムーブだけ取れるように設定できる、もしできたら使った方がいいねって自分言ってたかもしれないですけど、
全部来ちゃうんでCPU使用率やばいねっていう感じになります。
もしフィルタリングしたとしてもCPU使用率が高くなるんだったら意味ないですけどね。
そうですね。たられ場の話なんで。
そういうのがiNotifyとしてあるっていう感じですね。
iNotifyの利点と欠点というか、ログの出世だったらちょっと使う価値あんまりないかもねっていうようなところですね。
そうですね。フレントDの話を、自分の経験の話をちょっとすると追加で、
フレントDのバグで当時、ログローテートすると新しいファイルを読み込まずに止まるっていうバグがあったんですよ。
それはフレントD側の読み込みが止まってしまうと、新しいログローテートしてしまうと、
フレントDがなぜか処理が止まっちゃう。
致命的なんですけど。
致命的なんですけど、その処理が止まっちゃうってバグがあって、
これが当時いろいろ調べてたら、新しいファイルをタッチするとなぜか治るっていう。
なるほど。
見つけたんですよ、回避策。
1回タッチすればいいってことですね。
1回タッチすれば治る。そこにたどり着くまでに結構いろいろあったんですけど、それも見つけて、
じゃあクーロンでタッチすればいいじゃんってなったんですよ。
ご利用し解決ですけど。
これで事件がなんと起こってしまって、
タッチするユーザーがルートだったんですよ。
ログローテートでムーブするじゃないですか。
アプリケーションからもオークリエイトするんで、
アプリケーションが最初にオークリエイトできれば大丈夫なんですよ。
アプリケーションの権限でできるんですけど、
クーロン側で動いているタッチが先に動いちゃうと、ルートができちゃうと。
ルートはアプリケーションが書き込めができないと。
タッチでファイルをクリエイトしてしまったってことですね。
ルートユーザーのタイミングで、だから最初は大丈夫だったんですよ。
ある日突然バーンって書き込めができなくなった。
そのルートがすごくいいタイミングでルートがタッチしちゃったんですね。
絶妙のタイミングでタッチしちゃうと、
アプリケーション側が書き込みできなくて死ぬっていう障害が実際起こったことがあります。
それプロダクションで起きたんですか。
プロダクションで起こりましたね。
結構騒ぎになったんですが、そういうことも昔ありましたね。
原因ちょっとわかりづらくないですか、調査するとき。
まあそうですね、なんで起こったんだろうみたいなのは最初混乱しましたけど、
まあまあ解ければそうだよねっていう。
ちなみにフルエントリー側のバグの原因は何だったか。
当時はわかんなかったんですけど、一応修正っぽい、本当にそれで直るかわかんないですけど、
修正っぽいプリリクエストはマージされているので、今は大丈夫かなと思いつつって感じですね。
発生原因みたいなところは掴めてたんですかね、タッチしないといけないっていうのを
全部がそうだったらフルエントリーすごく使いづらいかなと思うんですけど、
どういう条件でタッチしないと書き込みができないみたいな。
その辺もね、なんか自分はよくわかってなくて、そのフルエントリーの変更とかも見たんですけど、
結構その辺のロジック複雑で、このイベント取ってみたいなそういうロジック動いてて、
ちょっとね自分もよくわかんなかったです、そう言うんなんですけど。
当時はよくわかなかったけど、現時点では大丈夫そう。
多分直ってんじゃないかなとは思います。ただそういう当時バグがあって、
タッチで回避しようとしたら権限問題でそういう事故だったりとかもしたので、
そのアプリケーションはやっぱりその自分で自作したアプリケーションで、
自分でログファイルをどんどん書き込んで、その書き込んだログファイルをフルエントリーに読み込ませてっていうプログラムだったので、
結構この辺今話したような話を結構真面目にいろいろ考えてやってたってことですね。
だからアプリケーション側でオークリエイトドアペンドでファイルをオープンしてファイル書き込んでみたいなのを
いろいろ考えてやってたってことですね。
なるほど。そこでタッチでの障害が発生したと。
そうですね。
あれですかね、今考えるとクーロンでタッチする前にファイルがあるかどうかを存在確認しておけば、
まだ大丈夫だったんですかね。
単純にクーロンの権限をアプリケーションと同じにしときゃよかったんですよ。
それはそうですね。
それができてればよかったし、実際そういう設定にしたんですけど。
その後。
そうですね。なのでそういうふうにやってれば、一旦大丈夫かなっていうところですね。
なので結構ログファイルって結構いろいろ考えないといけないし、事故ることもあるんですよね。
なのでこの辺は結構真面目に自分は今まで考えてきましたかね。
しかも結構あれですね、今回聞いてみて思ったのがその低レイヤーというか、
やっぱりリラックスカーネル上の仕組みみたいなのをめちゃくちゃ使ってるわけじゃないですか。
もちろんファイルディスクリプターをそのままファイル書き込みみたいないうところが主な機能なので、
ログファイルの書き込みとか読み込みなんて。
なのでここら辺のシステムコールだったりとかの知識がないとちょっと詰まりがちですね。
ここカスタマイズしたいだったりとか深く踏み込んでちゃんと実装するみたいなことをやろうとすると。
そうですねそう思うし結構自分疑問だったんですよね。これログファイル混ざんないのかなとか。
これ本当にこれでライトするだけでもやっぱりオーペンドとかもそもそも自分知らなかったんで、
ライトするときになんか消えたり上書きしたりしないのかなとか。
作るときにいろいろ疑問が出たんですよ。このアプリケーション作るとき。
でいろいろ調べてオーペンドってのつければ追記しかしないから大丈夫だとか、
今回話したような話がいろいろ出てきてやっと作れたみたいな感じでしたね当時。
っていうのも今だったら現代だったらもうあれですかねGCPでいうクラウドロギングだったりとか
AWSでいうクラウドウォッチログスだったりとかが結構隠蔽してやってくれているみたいなところが。
どういう処理をしたいか次第ではあるんですけど、やっぱログ収集するだけだったら
そのクラウドサービスうまく使う方法が今だったらいいよねとは思いますけどね。
例えばCDNでログファイルをS3とかGCSにプットしてもらうってできるじゃないですか簡単に。
でイベントトリガーでラムダ呼び出してそのラムダでS3のファイルとかそういうのを読み込んでログ処理するとかってやれば
以前フレンドDで頑張ってたような処理も結構かけたりとかもするし、
今だったらクラウドサービスを組み合わせてうまいことやる処理を考えた方がいいんじゃないかなと思いつつ、
そういうのがあるから今回話したような内容を知らなくていいよねとは自分は思わなくて、
やっぱりクラウドの裏側ではそういう処理動いてS3とかそういうところにファイル保存されてるわけだから、
結局やってるんですよね。CDN事業者とかクラウド事業者って。
やっぱりそういうのをちゃんと理解した上で使った方がいいと自分は思ってますね。
そういったのを理解した上で使うと何か問題が起きたときにどうなってクラウドウォッチログとかが動いてるのかみたいなところが若干見える上で対策っていうところが立てやすくなるんじゃないかなというところ。
そうですね。何か事故った時に見通しが多分違うだろうし、
あとファイルのフラグ数の設定とかもいろいろ知っておくと何かあった時に多分使えることあると思うので、
こういうのあるんだなって知ってるだけでも違うかなとは思ってますね。
いいですね。っていうところで結構いい時間になってきたんで、最後何かあればってところなんですけどどうです?
そうですね、何か自分結構この辺のファイルの読み書きって結構ずっとやってきたことなので、結構みんなもいろいろ試してみたら面白いんじゃないかなと思ってますね。
実験してみて、自分でリラックスサーバー立てて実験してみて、ちょっと体験してみてどうなるのかなっていうのを。
自分も昔結構やったんですよ。何かファイルの書き込みを動かして、自分でMVしてもフリーファイルに書き込みが走るとか、
実際やってみて本当じゃんみたいな、そういうの昔やったんですよ実際に。
なのでそういうのも実際やってみるといろいろ面白いと思いますね。
面白そうですね。実験をするっていうのはいいですね。
そう実験するとログローテッドの有難味というかログローテッド処理っていろいろやらないといけないんだなっていうのは肌でわかると思うんで。
いいですね。おすすめですね。
ということで今回はここまででしたいなと思います。
イヤウヨルドのOSSでは毎回一つのOSSを取り上げてそれについてガタついた編ということで、
技術的なところも深掘りしながら話をしていく番組になっています。
今後もその名の通りイヤウヨルドのOSSを取り入れていけたらなと思ってますので、ぜひお聞きのプラットフォームでフォローと高評価の方お願いします。
またこのOSSについても取り上げてほしいとかありましたらコメントやXなどで教えていただけると嬉しいです。
番組についての感想もお待ちしておりますので、ハッシュタグイヤウヨルドのOSSなどつけてくれると見つけやすくなります。よろしくお願いします。
それでは今回もありがとうございました。
ありがとうございました。