14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

メタップスAdvent Calendar 2020

Day 12

CloudWatchLogsとLambdaを使ったエラーログ監視をしてみる

Posted at

はじめに

エラーログ。。簡単にキーワードでひっかけてSlack通知させたいなぁ。。
そんなお悩みありませんか?
AWS上で運用されているかた必見!
かーんたんに(そしてわりと安く)ログ監視できちゃう方法をお披露目したいと思います!

構成

ScreenShot_2020-12-12_05.41.33.png

やり方

  1. CloudWatchAgentをEC2に導入する
  2. Lambdaの処理を書く
  3. ロググループにサブスクリプションフィルターを設定する

1. CloudWatchAgentをEC2に導入する

ポリシーが必要になるので、以下を参考にアタッチしてください。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-iam-roles-for-cloudwatch-agent.html#create-iam-roles-for-cloudwatch-agent-roles

インストールはAmazonLinux2を利用してる場合以下で一発導入可能。

$ sudo yum install amazon-cloudwatch-agent

その他のサポート対象OSをお使いの場合はこちらを参考に導入してみてください!
サポート対象OSについては以下をご確認ください。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html

導入が済んだら、設定ファイルを書いて起動してみましょう!
起動方法はOSによって異なるので、OSに合わせた起動方法で起動してください。
設定例

{
  "agent": {
    "run_as_user": "root"
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/hoge_service/fuga.log",
            "log_group_name": "/aws/ec2/hoge_service/fuga.log",
            "log_stream_name": "{instance_id}"
          }
        ]
      }
    }
  }
}

log_stream_nameはinstance_idを使うと手っ取り早くEC2毎にログストリームをわけてくれてオススメ設定です。
複数サーバーで使う場合は同じストリームにしないようにした方が良いでしょうね。
(理由は。。試せばわかりますw)
サーバー毎に監視したいエラーメッセージなどが異なる場合はlog_group_nameをサーバー毎にわける必要があります。
例: サーバーAではHogeErrorを監視する必要があって、サーバーBではFugaErrorを監視する必要がある場合は、サーバーAとサーバーBでlog_group_nameを分ける必要がある。

CloudWatchAgentはこの他にも色々な事ができて便利なので、気になる方は試してみてください。

2. Lambdaの処理を書く

CloudWatchLogsからトリガーされてSlack通知をするちょうど良いサンプルがPythonであったので、Pythonにしてます。
Lambdaがサポートしているお好きな言語で書くと良いと思います。
尚、Slackへの通知はIncoming webhookを想定して書いてます。
Incoming webhookの使い方は各自お調べください。
ログストリーム名をEC2のインスタンスIDとしているのでAWSコンソール上のEC2詳細画面へのリンクを生成して通知メッセージに入れてます。(オススメ!!)
またロググループへの遷移もできるようにしているので、前後のログ状況が通知後すぐに確認できます。(オススメ!!)

import base64
import requests, json
import urllib
import zlib
import datetime
import os
import boto3
import pprint
from botocore.exceptions import ClientError

print('Loading function')


def lambda_handler(event, context):
    data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS)
    data_json = json.loads(data)
    WEB_HOOK_URL = "https://hooks.slack.com/services/hoge/fuga/XXXXXXX"
    WIKI_LINK_TITLE = "<https://hoge.fuga/wiki/HogeError|WIKI>"

    for log in data_json["logEvents"]:
        log_json = json.loads(json.dumps(log, ensure_ascii=False))
        #print(log_json)

        LOG_GROUP_LINK_TITLE = "<https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#logsV2:log-groups/log-group/%s/log-events/%s|%s>" % (urllib.parse.quote_plus(data_json['logGroup']),data_json['logStream'],data_json['logGroup'])
        INSTANCE_ID_LINK_TITLE = "<https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#InstanceDetails:instanceId={0}|{0}>".format(data_json['logStream'])

        print("LOG_GROUP : %s" % data_json['logGroup'])
        print("STREAM_NAME : %s" % data_json['logStream'])
    
        try:
        
            message = u':no_entry:HOGEエラーが発生しました。\nサーバーの状態を確認して%sを参考に対応してください。\nロググループ名:%s\nインスタンスID:%s\nエラーメッセージ:\n```\n%s\n```' % (WIKI_LINK_TITLE,LOG_GROUP_LINK_TITLE,INSTANCE_ID_LINK_TITLE,log_json['message'])

            requests.post(WEB_HOOK_URL, data = json.dumps({
                'channel': u'#XXXXXXX',
                'text': message,  #通知内容
                'username': u'Alart From AWS lambda'  #ユーザー名
            }))
    
        except Exception as e:
            print(e)

    print('End function')

requestsライブラリが外部パッケージとなっているため、pipなどでダウンロードしてzipで固めてuploadする必要があります。
以下Mac上で対応した例です。
最後にlambda_function.pyを忘れずに入れてディレクトリごとzipで固めてuploadすれば幸せになれます。

$ mkdir for_lambda
$ cd for_lambda/
$ pip3.7 install requests -t .
Collecting requests
  Downloading https://files.pythonhosted.org/packages/39/fc/f91eac5a39a65f75a7adb58eac7fa78871ea9872283fb9c44e6545998134/requests-2.25.0-py2.py3-none-any.whl (61kB)
     |████████████████████████████████| 61kB 7.1MB/s 
Collecting chardet<4,>=3.0.2
  Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting urllib3<1.27,>=1.21.1
  Downloading https://files.pythonhosted.org/packages/f5/71/45d36a8df68f3ebb098d6861b2c017f3d094538c0fb98fa61d4dc43e69b9/urllib3-1.26.2-py2.py3-none-any.whl (136kB)
     |████████████████████████████████| 143kB 9.9MB/s 
Collecting certifi>=2017.4.17
  Downloading https://files.pythonhosted.org/packages/5e/a0/5f06e1e1d463903cf0c0eebeb751791119ed7a4b3737fdc9a77f1cdfb51f/certifi-2020.12.5-py2.py3-none-any.whl (147kB)
     |████████████████████████████████| 153kB 10.2MB/s 
Collecting idna<3,>=2.5
  Using cached https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl
Installing collected packages: chardet, urllib3, certifi, idna, requests
Successfully installed certifi-2020.12.5 chardet-3.0.4 idna-2.10 requests-2.25.0 urllib3-1.26.2
$ ll
total 0
drwxr-xr-x   3 hoge_fuga  1189740132    96 12 12 06:58 bin
drwxr-xr-x   7 hoge_fuga  1189740132   224 12 12 06:58 certifi
drwxr-xr-x   8 hoge_fuga  1189740132   256 12 12 06:58 certifi-2020.12.5.dist-info
drwxr-xr-x  43 hoge_fuga  1189740132  1376 12 12 06:58 chardet
drwxr-xr-x  10 hoge_fuga  1189740132   320 12 12 06:58 chardet-3.0.4.dist-info
drwxr-xr-x  11 hoge_fuga  1189740132   352 12 12 06:58 idna
drwxr-xr-x   8 hoge_fuga  1189740132   256 12 12 06:58 idna-2.10.dist-info
drwxr-xr-x  21 hoge_fuga  1189740132   672 12 12 06:58 requests
drwxr-xr-x   8 hoge_fuga  1189740132   256 12 12 06:58 requests-2.25.0.dist-info
drwxr-xr-x  17 hoge_fuga  1189740132   544 12 12 06:58 urllib3
drwxr-xr-x   8 hoge_fuga  1189740132   256 12 12 06:58 urllib3-1.26.2.dist-info
$ touch lambda_function.py 

3. ロググループにサブスクリプションフィルターを設定する

CloudWatchAgentの設定がうまくいってログが転送され出すと、CloudWatchLogsに設定ファイルに設定したロググループ名でロググループが追加されていると思います。
今すぐAWSコンソールを開いて確認してみましょう!
必要に応じてログの保持期間などを変更しておきましょう。
ロググループの一覧からでも詳細からでも構いませんので、アクションからLambda サブスクリプションフィルターを作成を選択します。
以下を参考に設定してみましょう。
サブスクリプションフィルター設定例.png
一つのロググループに対して最大で二つのサブスクリプションフィルターが作成できます。(サポートにて確認済み)
また、面倒ですが、一度登録したサブスクリプションフィルターはLambda側でのみ削除できます。
AWS CLIを使えばこの限りではないと思います。(そこまでは調べてないです。。)
うまく連携できるとLambda側で以下のように確認できます。

連携確認.png
ここまでできれば、Slack通知されるはずです!
できてなかったら、Lambdaの実行ログなどを確認してトラブルシューティング頑張ってください!

おわりに

比較的コーディング少なめでログの監視ができるものです。
ElasticSearch版のサブスクリプションフィルターもあるようなので、より高度なログ解析のできるようです。
機会があれば、チャレンジしてみたいものです^^

14
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?