CORECの開発を担当しています。
『CORECを支える技術 ~チケット駆動編~』に続き、『CORECを支える技術』シリーズの第2弾としてCORECのログ監視について書きたいと思います。
旧ログ監視の問題と対策
当初、CORECのログ監視はログ集約にくっつける形で始まりました。
ログ集約はログファイルを一定時間ごとにバックアップする仕組みで、各サーバーのログをfluentdで集め、1時間ごとに圧縮しS3に格納しています。
ログ監視は、その集約されたログを社内の監視システムで取得し、エラーログがあった場合にメールとIRCで通知するというものです。
この仕組には大きく以下の2つの問題がありました。
- タイムラグ
ログの監視をできるタイミングがS3に圧縮ファイルがアップされた後なので、検知するまで1時間のタイムラグが発生する可能性がある。 - サービスレベル
ログ監視を社内のサーバーに配置していて、消防点検や停電または、サーバーメンテナンス時に監視できないなどサービスレベルにも問題がある。
これらの問題に対応するためfluentdに集めたタイミングでエラーログを抽出し、AmazonSNS経由でメール通知することにしました。
これにより、社内のサーバーも経由しなくなり、タイムラグの問題・サービスレベルの問題を同時に解消できました。
実施内容
この対応で実施したことは以下の4つです
- fluentdにプラグイン追加
- AmazonSNSに通知用Topic作成
- メールBody整形用のERB追加
- fluentdの設定ファイル記述
1. fluentdにプラグイン追加
以下の2つのプラグインを追加しました。
- fluent-plugin-sns
- Amazon SNS通知用
- fluent-plugin-rewrite-tag-filter
- エラーのフィルタ・レベルごとの分岐処理用
fluentdはrubyで動いているのでRubyistならお馴染みのgemコマンドでインストールできます。
この時fluentdが使っているrubyの環境を指定する必要があります。
特にfluentdにバンドルされたrubyを使用している場合は注意してください。
sudo /usr/lib/fluent/ruby/bin/gem install fluent-plugin-sns
sudo /usr/lib/fluent/ruby/bin/gem install fluent-plugin-rewrite-tag-filter
2. AmazonSNSに通知用Topic作成
Amazonの管理コンソールからSNSサービスを選択し、画面に従ってTopic及びSubscritionを作成します。
特に迷うことはないかと思いますが、注意するポイントは以下の2点です。
- メールアドレスを登録するとメールがくるのでクリックして認証すること
- 作成したTopicの権限設定を確認する
権限設定とは作成したTopicに対して、通知に利用するAWSアカウントがこのトピックに投稿する権限のことです。もし権限がない場合には、[ポリシーの編集]から許可を与えてください。
3. メールBody整形用のERB追加
CORECの場合、fluentdが受け取るレコードは改行が\nにエスケープされていて読みづらいため、テンプレートで改行コードに変換しました。
以下のコードをerbファイルとして保存しておきます。
これだけのコードですが受け取った側の負担がだいぶ違います。特にスタックトレースで威力を発揮します。
<%record.each do |k,v|%>
<%="#{k}:#{v.to_s.gsub(/\\n/,"\n")}" %>
<%end%>
4. fluentdの設定ファイル記述
それでは最後にメインとなるfluentdの設定ファイルの編集をします。
変更/追加のポイントは以下の3点です。
4-a. copyプラグインで処理を2系統に分岐する
4-b. rewrite_tag_filterプラグインでエラーレベルごとにフィルタリングして、新しいタグを設定する
4-c. snsプラグインで通知するためのmatchディレクティブを追加する
それぞれ簡単に説明します。
4-a. copyプラグインで処理を2系統に分岐する
fluentdは最初にマッチしたmatchディレクティブだけを実行しますので、同じレコードに対して複数の処理をしたい場合にはcopyプラグインを使用します。
※copyプラグインは標準のプラグインなのでインストールをする必要はありません。
matchディレクティブと同等の内容をstoreタグに書くことができ、同じレコードに対して処理を複数記述できるようになります。
今回の対応では従来のS3への送信する処理をcopyで分岐し、SNSに送信する処理を追加します。
<match tag.log>
type copy
<store>
従来の処理
</store>
<store>
追加したい処理
</store>
</match>
4-b. rewrite_tag_filterプラグインでエラーレベルごとにフィルタリングして、新しいタグを設定する
ただ単に前項の「追加したい処理」にSNS送信のディレクティブの内容を書いてしまうと、アプリケーションが出力したログすべて(エラーでないものも含めて)通知されてしまいます。
そこでログの内容によって処理を振り分けるにはrewrite_tag_filterプラグインを使用します。
レコードのキーを指定して、検索する文字列または正規表現を指定し、それにマッチした場合に新しいタグを指定する仕組みです。
今回はログレベルがFATALもしくはERRORの場合にシステム通知がしたかったので、ログレベルが格納されているキー(lebel)に対して、FATAL・ERRORそれぞれルールを作成しました。
複数のルールを設定するにはrewriteruleの後の数字をインクリメントして記述します。
<store>
type rewrite_tag_filter
rewriterule1 level FATAL corec.rails.sns_fatal
rewriterule2 level ERROR corec.rails.sns_error
</store>
4-c. snsプラグインで通知するためのmatchディレクティブを追加する
typeにsnsを指定して、内容にはAWSユーザーにはお馴染みのパラメータ群を設定していきます。
sns_body_templateのパスはtd-agentが参照可能な場所にERBを配置して指定してください。
<match corec.rails.sns_fatal>
type sns
sns_topic_name for_system_notice_topic
aws_key_id xxxxxxxxxx
aws_sec_key xxxxxxxxxx
sns_endpoint sns.ap-northeast-1.amazonaws.com
sns_subject [COREC ALERT]corec/rails
sns_body_template /etc/td-agent/erb/sns_body_template.erb
</match>
fluentd設定ファイルイメージ(全体)
変更前後の設定ファイルは以下のとおりです。
行はだいぶ増えていますが、それぞれの設定内容に難しいところはありませんでした。
- 設定ファイル:修正前
<match corec.rails>
S3に送信する設定(省略)
</match>
- 設定ファイル:修正後
<match corec.rails>
type copy
<store>
従来のS3に送信する設定(省略)
</store>
<store>
type rewrite_tag_filter
rewriterule1 level FATAL corec.rails.sns_fatal
rewriterule2 level ERROR corec.rails.sns_error
</store>
</match>
<match corec.rails.sns_fatal>
type sns
sns_topic_name for_system_notice_topic
aws_key_id xxxxxxxxxx
aws_sec_key xxxxxxxxxx
sns_endpoint sns.ap-northeast-1.amazonaws.com
sns_subject [COREC ALERT]corec/rails
sns_body_template /etc/td-agent/erb/sns_body_template.erb
</match>
<match corec.rails.sns_error>
(省略)
</match>
設定ファイル修正後、fluentdを再起動すれば反映されます。
※fluentdの連携をしている場合には送信元のfluentdも再起動することをおすすめします。
私が設定した時には送信側が受信側を見失ってログの送信が止まってしまいました。
新たな問題
前項までの対応により、旧ログ監視の問題は解消し、
リアルタイムで、かつサービスレベルの高い仕組みができました。
が、新たな問題がいくつか発生しました。
- フィルタリングが無くなったのでERROR・FATALのレベルのログが全部飛んでくる
- 異常が発生するとガンガンメールがくる
- IRCに通知されない
新たな問題 1. ERROR・FATALのレベルのログが全部飛んでくる
→ 対策: 随時チケット化してエラーログの根本原因を潰す!
実はログを検索してメールを飛ばす処理の中でホワイトリストでフィルタリングして、通知するものを絞っていました。
その仕組みが無くなったのでERROR・FATALのレベルのログが全部飛んでくるようになってしまいました。
fluentdでフィルタリングするという選択肢もありましたが、対応の必要のないFATALやERRORを出すシステム側を修正すべきということで、チケット化してエラーの根本対応を実施。
新たな問題 2. 異常が発生するとガンガンメールがくる
→ 対策: 覚悟しておく!
従来のログ監視は1時間に1回ログを検索してメールを飛ばす処理だったため過去1時間に何件エラーが発生していても1通の通知にまとまっていました。
それがリアルタイムでエラー1件毎に通知されるため、異常事態にはメールが大量に送信されてしまいます。
実際に外部ベンダー側にトラブルがあり、復旧までに4時間かかった際に断続的に300通送信されてきました。orz
しかし、異常が発生しているのだから通知はあるべきでそれを止めるのは本末転倒ということで受け入れることにしました。
新たな問題 3. IRCに通知されない
→ 対策:社内で導入を進めている新監視システムへ
メールと同時にIRCに通知して、業務時間中はメールよりも気づきやすいように対策していましたが、IRCは社内サーバーで稼働しているため利用できませんでした。
現状は通知のために旧監視システムと並行稼動をしていますが、実際にはリアルタイムに飛んでくるメールに十分に気づけています。
今後は社内で進めているSlackのサービスを利用した障害対応フローに移行する予定です。
AmazonSNSに通知先を増やすだけで対応できるはずです。
まとめ
-
fluentdにプラグインを追加することで簡単にAmazonSNS経由の通知を実現できました。
こういったプラグインが充実しているのもfluentdの魅力だと思います。(fluentdの導入については『fluentd(td-agent) の導入』を参照してください。) -
ログ監視も即時性・対障害性の高いシステムを構築して確実で迅速な検知が必要です!
ログ監視は最後の砦であり将来発生する障害の芽を発見するアンテナだと思っています。これからもCORECとともにCORECを支える運用システムもブラッシュアップしていきます。 -
無駄なログは技術負債。根本から除去しましょう!
無駄なログがたくさん通知されてくると、本来拾わなければいけないログを見落としてしまう可能性があります。そのために通知するログを絞ることは大切ですが、安易にフィルタリングをするのではなく、一つ一つ原因を突き止めて対応すべきところはプログラムで対応し、技術負債を残さないようにしたいですね。