CloudWatch LogsのLambdaによるログ監視

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

渡辺です。

2016年11月4日追記。

Lambdaのランタイムで利用していたNode.js v0.10 が近いうちに使えなくなります。 Node.js 4.3対応版にアップデートしました。 既存のLambdaが動かなくなることはないと思いますが、今後利用する場合はこちらを利用ください。 なお、全面的にYAMLで書き直したため、可読性が高まっております。

2016年11月22日追記。

上記の通り、RutimeをNode.js 4.3にアップデートしましたが、 v0.10版のCFnスタックをアップデートする場合、アップデートに失敗します。 PublishToSNSWhenCloudWatchLogsのPhysical IDが変更になることが原因です(Runtime変更時、Lambdaは置換されれます)。 この事象は回避できないため、スタックを新しく作り直すか、CloudWatch LogsのLambda Streamを一度削除してアップデート後に再登録してください。 新しく作り直し、動作確認の上で旧バージョンのスタックを消す手順をお勧めします。

-ここまで-

最近お気に入りのサービスのひとつがCloudWatch Logsです。 マネジメントコンソールから各サーバのログを集中的に確認出来るため、サーバ数が横に増えていくAWS環境では必要不可欠なサービスではないでしょうか?

そして、ログが集中管理できるようになれば、ログを監視したいという要求が生まれるのは必然と言えるでしょう。

メトリクスフィルタによるログ監視

2016/3時点、CloudWatch Logsでは詳細なログ監視ができません。 ですが、CloudWatch Logsにはメトリクスフィルタという機能があります。 これは特定文字列が含まれているログを集計する機能です。 例えば、「Error」という文字列が含まれるログがどの程度発生しているかを集計できます。

このメトリクスにはアラームを作成することができます。 したがって、ログに特定文字列が含まれている時にメール通知は可能です(CloudWatch LogsでAmazonLinux上のApacheエラーログを監視する)。

ところが、このメトリクスフィルタによるメール通知には2つの課題があります。

柔軟な検知ができない

メトリクスフィルタで正規表現が使えればベストなのですが、現在は単純な文字列マッチしかできません。 このため、「ERRORまたはErrorまたはerrorが含まれている場合」などといった検知には工夫が必要です(CloudWatch Logsで大文字と小文字を区別せずにキーワードを検知する)。

ログの内容がメール本文に含まれない

メトリクスフィルタによるアラームでは、ログの内容がメール本文に含まれません。 条件を満たした時にメール通知されますが、「ERRORが含まれるログを検知した」ことしか解らないのです。 このため、実際にどんなログが出力されたのかはコンソールにログインして確認する必要があります。 これは発生頻度が高く重要度の低い「ERROR」が発生する時に狼少年問題を引き起こしてしまいます。

CloudWatch LogsサブスクリプションからLambdaを呼び出すlogs2sns

メトリクスフィルタによるログ監視はあくまで簡易的なログ監視です。 もう少し柔軟なことをやるならばLambdaで処理をすることが必要となります。 具体的には、CloudWatch Logsのサブスクリプションフィルタを利用して、Lambdaを呼び出します。 Lambdaでは自由に処理ができるため、整形してSNSにpublishすればメール通知すれば良いことになります。

というわけで、メトリクスフィルタによるログ監視による2つの課題を解決した仕組みlogs2snsを作成しました。 logs2snsは、CloudWatch Logsからのログを処理し、正規表現でマッチしたならば、SNS経由でのメール通知を行うLambda関数です。 もちろん、メール本文には検知したログが記載されます。

CloudFormationでスタックを作成する

logs2snsはCloudFormationのテンプレートで提供しています。 テンプレートをダウンロードしたならば、AWSマネジメントコンソールにログインし、スタックを作成してください。 スタック名は自由に入力しましょう(例: logs2sns)。

logs2sns

FilterPatternを設定する

1つ目のパラメータFilterPatternはフィルターのための正規表現です。 Lambdaがnodejsで実行されるので、nodejsで有効な正規表現としてください。

例えば、大文字小文字を区別せずにErrorを検知するならば、「/Error/i」となります。 ErrorとWarnの両方を検知するならば、「/Error|Warn/」となります。

NotifyMailAddressを設定する

通知先のメールアドレスを設定してください。 CloudFormationの制約により、スタックを作成後にこのパラメータを更新することが出来ない点に注意してください。 もし、通知先のメールアドレスを変更したい場合は、SNS Topicを直接更新するか、スタック自体を作り直す必要があります。

IAM Role作成の警告を理解して同意する

logs2snsのテンプレートではIAM Roleを作成するため、これに同意しなければスタックは作成出来ません。

IAM Roleを作成するということは、悪意のあるCloudFormationを実行した場合に、アカウントを乗っ取られる可能性があるということです。 IAM Roleを操作しないテンプレートであれば、最悪でも巨大なリソースを作られるだけで済みますが、IAM Roleに操作することを許可するということは、気がつかずに管理者権限を見知らぬ第三者に与える可能性もあるということを理解してください。

logs2sns2

SNSサブスクリプションを確認する

スタックを作成するとパラメータで指定したメールアドレスに「AWS Notification - Subscription Confirmation」というサブジェクトでメールが届きます。 これはCloudFormationで作成したSNS Topicからの確認メールです。 リンクをクリックし、メールアドレスの存在確認を行いましょう。

CloudWatch Logsのサブスクリプションを作成する

スタックが完成したならば、CloudWatch Logsのロググループにサブスクリプションを作成し、Lambdaに流し込みます。

CloudWatch Logsから対象のロググループを選択し、アクションから「Lambdaサービスへのストリーミングの開始」を選択してください。

logs2sns3

Lambda 関数にはCloudFormationで作成されたLambda関数を指定します。

logs2sns4

ログ形式とフィルタの設定では、サブスクリプションのフィルタ設定を行います。 ログの形式は「その他」で良いでしょう。

logs2sns5

「サブスクリプションフィルタのパターン」でログを絞り込めれば、Lambdaのコール回数も絞り込めるのですが、メトリクスフィルタと同様のパターンしか指定できません。 Lambda側でマッチングを行うならばパターンは空(すべてのログをLambdaに転送)します。

メール通知を確認する

マッチしたログが検知されると「Notify CloudWatch logs event. 」というサブジェクトでメールが通知されます。 本文はこんな感じです。

NotifyAt: Mon Mar 07 2016 23:48:09 GMT+0000 (UTC)
Log: /Apache/error_log - i-XXXXXXXX
Filter: LambdaStream_log2sns-errors-PublishToSNSWhenCloudWatchLogsEvent-XXXXXXXXXXXXX
Messages:
[Mon Mar 07 23:48:02 2016] [error] [client XXX.XXX.XXX.XXX] Directory index forbidden by Options directive: /var/www/html/

ちゃんとメール本文にログが記載されていますね!

logs2snsの応用

応用トピックです。

通知先の追加・変更

SNSによる通知先を追加・変更したい場合は、SNSTopicのサブスクリプションを手動で設定してください。 これは、CloudFormationの制約から、通知先のアドレス変更を伴うアップデートができないためです。 手動でSNSTopicを更新した後は、CloudFormationは利用しないことをお勧めします。

フィルタパターンの更新

マッチする文字列を変更する場合は、CloudFormationのスタックを更新(パラメータのみのアップデート)で対応することができます。 またはLambdaからコードを直接編集してください。

Lambda関数のカスタマイズ

ご自由にどうぞ。

免責事項

このように、logs2snsはお手軽にログ監視を行うCloudFormationのテンプレートです。 しかし、使い方を間違えると大惨事になりますので、十分に理解した上でご利用願います。

IAM Roleを作成するテンプレートであることを理解する

既に説明済みですが、logs2snsはIAM Roleを作成します。 悪意のあるRoleは作成していませんが、Lambdaへ付与するIAM RoleとLambda関数への権限付与を行っています。 この危険性を理解した上でご利用ください。 万が一利用者の環境に問題が生じても、社内規約などから問題になったとしても責任は負いません。

Lambdaの高額請求に警戒する

logs2snsで発生するAWSの利用料金はLambdaのコール回数はログの量と頻度に強く依存します。 このため、大量のデバッグログが出力される場合、Lambdaの利用料金がどのくらいになるか見当がつきません。 実運用で使う場合は、数日運用してから利用料金(コール回数)の確認を行ってください。 どれだけAWSから利用請求を受けた場合でも責任は負いません。

可能な限りログの部分でログを絞り込んでおくことをお勧めします。 ほとんどのプログラミング言語のロギングフレームワークでは、ERROR以上のログを別ファイルに出力するなどのことは可能なはずです。 また、監視対象のログは出力レベルを抑制しておくことも重要です。

Lambda実行エラーなどもありえることを理解する

logs2snsは、メトリクスフィルタによるログ監視に比べればログ監視として必要な機能を備えています。 しかし、信頼性が高い仕組みとは言えません

ログへの書き込みから検知までの間になんらかの障害が発生し、通知まで行えない可能性もある点を理解願います。 信頼性や即時性を強く求める場合は、有償のログ監視システムなどを検討することをお勧めします。

まとめ

というわけで、最近問い合わせが多いため、AWSで完結するログ監視ソリューションを作ってみました。

  • オープンソースまたはAWSで利用できる
  • 正規表現でログをマッチングできる
  • ログを検知した時にメール通知できる
  • メール本文にログの内容が含まれる

と、最低限の要件は満たしているかと思います。 あくまで自己責任利用となりますが、導入支援の希望がありましたらば、お気軽にお問い合わせください。