BeeX Technical BlogAWS Health からの通知を使って運用マニュアルタスクを減らしたい

那須です。
普段は個人のブログで記事を書いていますが、会社のブログにも記事を書いていくことにしました。

AWS で EC2 インスタンスを運用してるとたまに出てくるリタイアメント通知。
特に何も設定しなければルートアカウントのメールアドレスに通知がきます。
それで気づける運用にしている方はそれで全然問題ないのですが、そうじゃない人って意外とたくさんいるんじゃないでしょうか?(私もそうです
メールで通知されてもその後のアクションにつながらないので、Slack で通知しつつ同時に Lambda で自動処理してる方もいそうですね。

というわけで、今回は EC2 インスタンスのリタイアメント通知をメールからその他の何かに変えて運用でのマニュアル作業を減らしたい方のためにその流れを書きます。

何を使うのか?

CloudWatch Events から AWS Health の通知をモニタリングして、検知したら Lambda に投げる仕組みです。
ドキュメントは下記に公開されていますので読んでみてください。

lambda を使ってリタイアメント通知が来たらすぐに EC2 インスタンスを再起動したりできますよ。
弊社がよく扱っている SAP システムの場合はいきなり再起動されると大変なことになりますので、下記のような工夫をすれば再起動時刻の調整ができますね。
(今どき毎日再起動するシステムはあまり無いとは思いますが、ひとつの例ということで。

  • ・Cloud Automator 等を使って毎日 restart=yes タグがついている EC2 インスタンスを特定の時刻で再起動するようスケジュール
  • ・Lambda 関数で上記タグをリタイアメント対象の EC2 インスタンスにつける
  • ・指定した時刻で再起動される
  • ・restart=yes タグは毎日削除されるようにしておく

今回はお手軽に Slack に通知する仕組みだけ作ってみましょう。

Slack 通知する Lambda 関数

Python 3.7 で動くコードをサクッと作ってみました。
業務で使う場合はエラーハンドリングとかも考慮して作りましょう。

from logging import getLogger, INFO
import os
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
from base64 import b64decode

logger = getLogger()
logger.setLevel(INFO)


def decrypt_text_by_kms(encrypted_text):
    return boto3.client('kms').decrypt(CiphertextBlob=b64decode(encrypted_text))['Plaintext'].decode('utf-8')


def notify_slack(title, eventTypeCode, eventStartTime, eventDescription, affectedInstance, color):
    slack_url = decrypt_text_by_kms(os.getenv('SlackUrl'))
    attachments_json = [
        {
            "color": color,
            "title": title,
            "fields": [
                {
                    "title": "eventTypeCode",
                    "value": eventTypeCode,
                    "short": False
                },
                {
                    "title": "eventStartTime",
                    "value": eventStartTime,
                    "short": True
                },
                {
                    "title": "affectedInstance",
                    "value": affectedInstance,
                    "short": True
                },
                {
                    "title": "eventDescription",
                    "value": eventDescription,
                    "short": False
                }
            ]
        }
    ]
    slack_message = {
        'attachments': attachments_json
    }
    req = Request(slack_url, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted")
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)


def lambda_handler(event, context):
    eventTypeCode = event['detail']['eventTypeCode']
    eventStartTime = event['detail']['startTime']
    eventDescription = event['detail']['eventDescription'][0]['latestDescription']
    affectedInstance = event['detail']['affectedEntities'][0]['entityValue']
    notify_slack('スケジュールされたイベントが見つかりました!',
                 eventTypeCode,
                 eventStartTime,
                 eventDescription,
                 affectedInstance,
                 'warning'
                 )

このコードで NotifyHealth_Scheduled という名前の Lambda 関数を作成します( Lambda 関数の作り方は省略します
環境変数 SlackUrl に incoming webhook の URL を指定しましょう。
関係者以外の方にコンソールを見られる可能性があると思いますので、暗号化キーに KMS を指定して環境変数を暗号化するのが吉です。
IAM ロールは CloudWatch Logs へロギングする権限だけあれば十分です。

CloudWatch Events のルール追加

Lambda 関数の作成はコンソールからやりましたが、今回は CloudFormation テンプレートを作ってそれを使ってCloudWatch Events のルールを作成してみましょう。

CloudFormation テンプレートの内容はこんな感じです。
もし複数のアクションを実行したい場合は、 Targets のところに複数並べて書けば OK です。

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
        "LambdaFunctionName": {
            "Type": "String",
            "Default": "NotifyHealth_Scheduled"
        }
    },
    "Resources": {
        "scheduledChange": {
            "Type": "AWS::Events::Rule",
            "Properties": {
                "Description": "CloudWatchEvents Rule to detect EC2 scheduled change",
                "EventPattern": {
                    "source": [
                        "aws.health"
                    ],
                    "detail-type": [
                        "AWS Health Event"
                    ],
                    "detail": {
                        "service": [
                            "EC2"
                        ],
                        "eventTypeCategory": [
                            "scheduledChange"
                        ]
                    }
                },
                "Name": "DetectEC2ScheduledChange",
                "State": "ENABLED",
                "Targets": [
                    {
                        "Id": "LambdaId1",
                        "Arn": {
                            "Fn::Sub": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunctionName}"
                        }
                    }
                ]
            }
        }
    }
}

これを使って CloudFormation コンソールでスタックを作成しましょう。
スタックの作成を押します。

CloudFormation テンプレートを指定します。

スタック名は何でもいいので入力しましょう。Lambda 関数名は先程作成した関数名を入れます。

ステータスが CREATE_COMPLETE になっていれば、CloudWatch Events のルールができていると思います。

CloudWatch Events のルールも作成されていますね。

残念ながら上記を設定してからリタイアメント通知が来なかったので、結果のサンプルを載せておきますね。

まとめ

クラウドの運用ってこれまでの運用とは違っていろいろなことができます。
人がやらないといけなかったことが簡単に自動化できるのもその 1 つですね。
単純にサーバが仮想化された、データセンターが拡張された、というような考え方は捨ててクラウドのいいところを使い倒していきましょう!

関連サービス:クラウド・SAPシステム運用保守サービス

クラウド・SAPシステム運用保守サービス

日々変わり続けるクラウドの最新技術やトレンドを取り込み、貴社に最適な基幹クラウド向け運用保守サービスを提供します。

詳細を見る

BeeX Technical Blogについてのお問い合わせ

BeeX Technical Blogのエントリにご質問が御座いましたらお気軽にお問合せください。

お電話でのお問い合わせ

☎ 03-6214-2830

受付時間 平日9:30〜18:00

フォームでのお問い合わせ

お問い合わせフォーム