1. HOME
  2. テックブログ
  3. AWSの障害情報を検知する

AWSの障害情報を検知する

当社も幾つかのサービスをAWS上で運用していますが、幸運なことにAWS自体の障害による影響を受けたことがありません。

が、今後そのような事態に陥る可能性はゼロとは言えませんし、CloudWatchなどでは検知できないものもあるでしょう。

そこで、AWSのサービス自体の障害を検知できるような仕組みを作ってみようと思います。

どうやって検知して通知する?

まず目をつけたのはコレです。

ここには「サービスごと」のRSSが揃っているので、RSSリーダーとかに食わせてやれば検知できそう。

よし。じゃあ、サービスを1つずつ選んで…って大量すぎてやってられないですね。しかもRSSリーダーなんて常に開いて見ているわけでもないし。

ということで他をあたってみます。

いろいろ漁っていると、json形式で提供されているという情報が見つかりました。

これ1つに全サービスの障害情報が入ってくるようです。

うん、これを使って進めていきましょう。

定期的にjsonを取得&解析して障害が発生していたら通知するって感じですかね。pythonで組んでLambdaに置いてEventBridgeから定期的に実行させましょう。当社は社内連絡用としてChatworkを利用しているので通知先はそこにします。

pythonのスクリプトを書いてLambda上で実行する

まずはLambdaの関数を作成します。「一から作成」で、関数名は適宜(ここでは「CheckAWSHealth」としました)、ランタイムはPython3.Xを選択します。

次は、Lambdaの環境変数を設定します。ChatworkのAPI関連の情報やjsonのURLなどは環境変数に入れておいた方がよさそうです。

1時間毎に実行させようと思うので、jsonを解析する時に3600秒以内のデータを対象とするようにCHECK_TERMという環境変数も入れておきます。

キー
JSON_URL https://status.aws.amazon.com/data.json
CHECK_TERM 3600
CHATWORK_API https://api.chatwork.com/v2/
CHATWORK_ROOM ※Chatworkのroom_idを指定
CHATWORK_TOKEN ※Chatwork APIのトークンを指定

ではコードのページでプログラムを書いていきます。

とりあえず、こんな感じで作ってみました。検知の対象を東京リージョンとグローバルのサービスのみに限定しているので、他のリージョンのサービスも利用している場合は修正が必要です。

あと、例外処理とかが全く考慮されてないので入れなきゃいけないですね (^^;

import os
import requests
import re
import time
import datetime
import pprint
 
def lambda_handler(event, context):
    url = os.environ['JSON_URL']
    check_term = int(os.environ['CHECK_TERM'])
    chatwork_api = os.environ['CHATWORK_API']
    chatwork_token = os.environ['CHATWORK_TOKEN']
    chatwork_room = os.environ['CHATWORK_ROOM']
 
    time_limit = int(time.time()) - check_term - 10
    time_limit_str = datetime.datetime.fromtimestamp(time_limit).strftime("%Y/%m/%d %H:%M:%S")
 
    # データ取得
    json = requests.get(url).json()
 
    alerts = []
    categories = ['archive', 'current']
    for category in categories:
        for archive in json[category]:
            # 東京リージョンのサービス or グローバルサービスのみ
            if re.compile('Tokyo').search(archive['service_name']) or re.compile('[^)]$').search(archive['service_name']):
                # 更新日時が1時間以内のみ
                alert_time = int(archive['date'])
                if alert_time > time_limit:
                    alerts.append(archive)
 
    # アラートが1件以上あったらChatworkへ投稿
    alert_count = len(alerts)
    if alert_count >= 1:
        # メッセージの整形
        message = ''
        for alert in alerts:
            message += '[code]{0}[/code]'.format(pprint.pformat(alert, indent=4))
        subject = 'AWS alerts from {0}'.format(time_limit_str)
        chatwork_message = '[info][title]{0}[/title]{1}[/info]'.format(subject, message)
        # Chatwork APIのURL
        chatwork_url = '{0}rooms/{1}/messages'.format(chatwork_api, chatwork_room)
        # Post
        response = requests.post(
            chatwork_url,
            headers = {'X-ChatworkToken': chatwork_token},
            data = {'body': chatwork_message}
        )

では、テストしてみましょう。

エラーになってしまいますね。

どうやらrequestsライブラリが無いようなのでLayerで追加しましょう。

pipがインストールされている環境でアップロードするzipを作成します。ディレクトリ名を「python」としておくと、importする際のパス指定が不要になるそうです。

$ mkdir python
$ pip install -t python/ requests
Collecting requests
  Using cached requests-2.26.0-py2.py3-none-any.whl (62 kB)
Collecting urllib3<1.27,>=1.21.1
  Using cached urllib3-1.26.7-py2.py3-none-any.whl (138 kB)
Collecting certifi>=2017.4.17
  Using cached certifi-2021.10.8-py2.py3-none-any.whl (149 kB)
Collecting charset-normalizer~=2.0.0
  Using cached charset_normalizer-2.0.8-py3-none-any.whl (39 kB)
Collecting idna<4,>=2.5
  Using cached idna-3.3-py3-none-any.whl (61 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
Successfully installed certifi-2021.10.8 charset-normalizer-2.0.8 idna-3.3 requests-2.26.0 urllib3-1.26.7
$ zip -r layer.zip python
  adding: python/ (stored 0%)
  adding: python/charset_normalizer-2.0.8.dist-info/ (stored 0%)
  adding: python/charset_normalizer-2.0.8.dist-info/WHEEL (stored 0%)
  adding: python/charset_normalizer-2.0.8.dist-info/INSTALLER (stored 0%)
  adding: python/charset_normalizer-2.0.8.dist-info/top_level.txt (stored 0%)
  .
  .

コンソールからレイヤーを作成して、先程のzipをアップロードします。

作成できたら、関数のページで「レイヤーの追加」から作成したレイヤーを選択します。これでrequestsライブラリが使えるようになりました。

大丈夫ですね。

さて、これでエラーは出なくなりましたが、実際に直近1時間以内にAWSで障害が発生していないとChatworkにPOSTされず内容が確認できません。都合よく障害が発生してくれたらいいんですが(いや、発生しちゃいけません)、そういう訳にもいかないので、環境変数「CHECK_TERM」の値を増やしてテストしてみましょう。

来ました。マネージメントコンソールのエラー発生率が増加していたようです。summaryにRESOLVEDと入っているので既に解決済みですね。テストが終わったら環境変数を元に戻しておきましょう。

Lambda関数を定期的に実行する

EventBridgeでルールを作成します。

パターンはスケジュールにして固定速度ごとで1時間とし、ターゲットはLambda関数で先ほど作成したCheckAWSHealthを選択します。

これで定期的(今回は1時間ごと)に実行されるようになりました。

実はまだ課題が…

  • 1つの障害で作成されるjsonのオブジェクトは1つで、状況に進展があったらdescriptionに追記されるようです。さらにdateの値は更新されないので、今のスクリプトでは障害発生時はキャッチできますが、その後の進展まではつかむことができません。この辺りの改良は必要ですね。
  • Lambda自体が障害でダウンしてしまうとアウトですね。複数のリージョンのLambdaで動かすようにすると補完できそうですが、単純に増やしてしまうとLambda以外の障害で通知がダブってしまいます。
  • ChatworkにPOSTされたdateの値がUNIX時間のままなので不親切ですね。人間が見やすいように変換してあげましょう。(UNIX時間を見ただけで日時が分かる人は別です)
  • Lambdaが実行されるたびにCloudWatchログが出力されるので適宜削除するようにしましょう。この記事を書いている時点では、5GBまでなら無料枠で利用できます。

以上、まだまだ課題は残っていますが、障害発生の検知だけはできそうですね。

ファブリカコミュニケーションズで働いてみませんか?

あったらいいな、をカタチに。人々を幸せにする革新的なサービスを、私たちと一緒に創っていくメンバーを募集しています。

ファブリカコミュニケーションズの社員は「全員がクリエイター」。アイデアの発信に社歴や部署の垣根はありません。

“自分から発信できる人に、どんどんチャンスが与えられる“そんな環境で活躍してみませんか?ご興味のある方は、以下の採用ページをご覧ください。

◎ 新卒採用の方はこちら
◎ キャリア採用の方はこちら

この記事を書いた人

きむら
プロダクト開発本部 インフラチーム
きむら

おすすめの記事