どうも、バックエンドエンジニアのサトウリョウスケです ✌︎('ω')✌︎
突然ですがワテクシ、 Rails アプリはそこそこ書ける方なんですが、インフラはからきしだったりします。 フルスタックじゃないエンジニアが許されるのは小学生までだよねー、というちょっと懐かしみのある煽りが社内のからも聞こえてきそうなので、最近インフラを少しずつ触るようにしています。触ってみるとインフラも色々楽しいですね ✌︎('ω')✌︎
とはいえ、インフラエンジニアレベル 1 の自分があれこれやっても障害に繋がるだけなので、まずはお手軽な雑用タスクから始めることにしました。
さて、少し話は変わって、最近、弊社サービスで Sidekiq プロセスのファイルディスクリプタが急騰してサーバーがダウンする、という障害が何度か発生しました。
現象としては Sidekiq プロセスのファイルディスクリプタが上限値 (初期値は 1024
) に達すると、サーバーの CPU 使用率が 100 % 付近まで達してしまう、というものだったので、ファイルディスクリプタの上限を 65536
まで上げる、というワークアラウンドな対応で凌いでいました。
現在は解決済み(この原因については、別途記事にします)ですが、当時はなぜファイルディスクリプタの数が上昇し続けてしまうのか原因が全くわからず、チームのエンジニアは安眠できない日々が続いていました。とりあえず Sidekiq のプロセスをリスタートすればファイルディスクリプタの数は一旦リセットされるので、監視してやばくなったらアラートを飛ばして再起動させる、という方法で一旦は凌ます。また、継続して監視することで、原因の解明にも繋がるかもしれません。
弊社のサービスでは監視に mackerel を利用しているので、カスタムメトリックを使ってお手軽に監視させよう、ということになりました。 mackerel のカスタムメトリックの投稿方法は こちらの記事 にまとまっています。 要点だけ抜粋しますと、 mackerel-agent の設定ファイル に 以下の書式で標準出力を実行するコマンドを記述 すれば OK です。
設定ファイルで指定するコマンドは、標準出力の各行に以下のフォーマットの出力をすることが期待されます(
\t
はタブ文字です):
{metric name}\t{metric value}\t{epoch seconds}
そして以下がファイルディスクリプタを監視するための設定です(完成品がレンジから出てくるパティーン)
[plugin.metrics.file_descriptor_count] command = ''' echo -e "file_descriptor.sidekiq\\t$(sudo ls /proc/$(pgrep -f -u {user_name} sidekiq | head -1)/fd/ | wc -l)\\t$(date -u +%s)" '''
標準出力されれば OK なので、 echo
で任意の文字列を出力するような方法でも実現可能です。内部で pgrep -f -u {user_name} sidekiq
と書いて、プロセス ID を取得していますが、 -u
でプロセスを実行しているユーザーを指定しないと、 pgrep
のプロセス ID を取得してしまうケースがあるので注意が必要です。(mackerel-agent は root
で実行される)
同じ要領で、他にも以下のように書けば puma
のファイルディスクリプタも取ることができます。
[plugin.metrics.file_descriptor_count] command = ''' echo -e "file_descriptor.puma\\t$(sudo ls /proc/$(pgrep -f -u {user_name} puma.sock | head -1)/fd/ | wc -l)\\t$(date -u +%s)" echo -e "file_descriptor.puma_cluster_worker\\t$(sudo ls /proc/$(pgrep -f -u {user_name} 'puma: cluster worker' | head -1)/fd/ | wc -l)\\t$(date -u +%s)" '''
ちなみにこの記事を書くときに、もしやと思って調べてみたら、 mackerel-agent-plugins
の中に 任意のプロセスのファイルディスクリプタを監視する奴 がありました 😓
試していませんが、こっちを使った方が良いと思います 😇
そんな感じで監視できたグラフが以下になります。
ファイルディスクリプタ数がファイナルファンタジーの HP みたいになってますね。。。
とりあえず 10,000
を超えたらアラートを飛ばすように設定しましたが、デプロイする度にリセットされるので、結局アラートが飛ぶことも障害が発生することもなく、問題は解決しました。
本稿では echo
を使ったワンライナーでカスタムメトリックを投稿する方法を紹介しました。
mackerel-agent-plugins
に載っていないけどワークアラウンドでとりあえず監視させたい、という時は便利だと思うので、どこかでご活用ください 🙏