EC2のステータスチェック失敗時に自動再起動を行いSlackに通知する

挨拶
最近、寒くなって炬燵を出すのが楽しみな yuhan です。
今回は、EC2のステータスチェックが失敗した時に、自動で再起動を行い、Slackに通知を行う実装を紹介したいと思います。
CloudWatch アラームと EventBridge、SNS、Chatbot を連携させて、Slack への通知を行います。折角なので、Chatbot のカスタム通知機能も活用して、自分好みな形で通知をしたいと思います。
前提
- AWSアカウントを作成済み
- EC2インスタンスを構築済み(SSH等で接続ができる)
- Slackのワークスペースがある
Chatbot の準備
まず、AWS と Slack のワークスペースを連携させる必要があります。
AWS にログインして AWS Chatbot のページにアクセスします。

チャットクライアントに Slack
を選択し、クライアントを設定
をクリックします。

Slack の認証ページにリダイレクトされるので、連携したいワークスペースであることを確認して 許可する
をクリックします。

Chatbot のページで正常に承認された旨のメッセージが表示されればOKです。

SNSトピックの作成
Amazon SNS のページを開いて、トピック
をクリックします。

トピックの作成
をクリックします。

スタンダード
を選択して、任意の名前を設定します。表示名は設定しなくても構いません。

一番下までスクロールして、トピックの作成
をクリックしてSNSトピックを作成します。

エラーなく作成されればOKです。

通知先のSlackチャンネルを設定する
AWS Chatbot のページに戻り、設定済みのワークスペースが左側に表示されているので、こちらをクリックします。

新しいチャネルを作成
をクリックします。

設定名
は任意の名前を設定します。後から見返した時に、何のために設定したものかわかりやすい名前がいいですね。

通知を飛ばしたいSlackチャンネルのタイプとチャンネル名を選択します。

アクセス許可については、デフォルトの設定で問題ありません。新しくロールを作成する場合は、わかりやすい名前を設定します。
※Chatbot のアクセス許可については、公式ドキュメントなどをご参照ください。今回はデフォルトで問題ありませんが、Chatbot 経由でAWSリソースに対して操作を行う場合は、適切に権限を設定する必要があります。

通知で、作成したSNSトピックを設定します。ここまでできたら、 設定
をクリックして、設定内容を保存します。

エラーなく完了すればOKです。

CloudWatch アラームを作成する
SNSとChatbotの準備ができたら、CloudWatchでアラームの設定を行います。
CloudWatch のページからもアラームの作成はできますが、今回はそこまで複雑な設定はしないので、EC2のページからアラームを作成します。
EC2のページを開いたら、インスタンス
をクリックします。

アラームを設定したいEC2インスタンスを選択した状態で、アクション
-> モニタリングとトラブルシューティング
-> CloudWatch アラームの管理
をクリックします。

アラームを作成
を選択します。通知は、今回はChatbotのカスタム通知を使うため、EventBridge経由で通知を行うため、ここでは無効化しておきます。

アラームアクションを有効化して、検知した際に再起動を行うように再起動
を選択します。

アラームのしきい値では、サンプリングするデータのタイプに ステータスチェックの失敗:インスタンス
を選択します。
アラーム名や説明は、任意でわかりやすいものを設定しましょう。
※インスタンスステータスチェックとシステムステータスチェックは別物です。システムステータスチェックに失敗する時は、AWS側の問題である可能性があります。詳しくはこちらをご参照ください。

サンプルや設定内容を確認して、問題がなければ 作成
をクリックします。

EventBridge のルールを作成する
Chatbot のカスタム通知を使うために、EventBridge 経由で通知を行うようにします。
EventBridge のページを開き、 ルール
をクリックします。

ルールを作成
をクリックします。

任意の名前を設定し、イベントパターンを持つルール
を選択します。

次へ
をクリックして、イベントパターンの構築へ進みます。
イベントソースには、AWSイベントまたは EventBridge パートナーイベント
を選択します。

サンプルイベントはオプションなので今回は設定しませんが、イベントパターンのサンプルが参照できるので、使うとテストなどがしやすく便利です。
作成のメソッドは パターンフォームを利用する
を選択します。

イベントパターンでは、イベントソースに AWS のサービス
を選択します。
AWSのサービスでは、CloudWatch
を選択し、イベントタイプは CloudWatch Alarm State Change
にします。

パターンを編集
をクリックします。

先ほど作成したCloudWatchアラームが、アラーム状態になった時だけ検知するように下記のように付け加えます。
{
"source": ["aws.cloudwatch"],
"detail-type": ["CloudWatch Alarm State Change"],
"resources": ["arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxx:alarm:test(i-xxxxxxxxxxxxxx)でステータスチェックに失敗しました。"],
"detail": {
"state": {
"value": ["ALARM"]
}
}
}

次へ進み、ターゲットの設定を行います。
ターゲットタイプは AWSのサービス
、ターゲットを選択では、SNSトピック
を選択して、これまでの手順で作成したSNSトピックを選択しましょう。

Chatbot の通知をカスタマイズするために、ここで追加設定を行います。
追加設定を開くと、ターゲット入力を設定という項目があるので、こちらを入力トランスフォーマー
にします。
入力トランスフォーマーを設定
というボタンが出てくるので、クリックします。

ポップアップが表示されるので、スクロールします。

入力パス、テンプレート、出力の例には、サンプルが記載されています。もし、どのようにカスタマイズするか悩んだ場合は、こちらを参考にしましょう。

それでは、実際に入力パスとテンプレートを設定して、通知内容をカスタマイズしていきます!
公式ドキュメントを参考にしながら設定を行います。
参考:https://docs.aws.amazon.com/chatbot/latest/adminguide/custom-notifs.html
今回、入力パスは下記のようにしました。
インスタンスIDやアカウントIDなど、通知の際に表示したい情報を定義しています。
{
"instance": "$.detail.configuration.metrics[0].metricStat.metric.dimensions.InstanceId",
"account": "$.account",
"metrics_name": "$.detail.configuration.metrics[0].metricStat.metric.name",
"alarm": "$.detail.alarmName",
"description": "$.detail.configuration.description",
"timestamp": "$.detail.state.timestamp",
"state": "$.detail.state.value"
}

テンプレートは、Chatbot カスタム通知用に、次の形式にする必要があります。
version
と source
は必須で、値も固定されています。
{
"version": "1.0",
"source": "custom",
"id": String,
"content": {
"textType": String,
"title": String,
"description": String,
"nextSteps": [ String, String, ... ],
"keywords": [ String, String, ... ]
}
}
今回は入力パスで取得した情報を使って、下記のようにしてみました。
マークダウン形式にして絵文字などを使う形にしています。
{
"version": "1.0",
"source": "custom",
"content": {
"textType": "client-markdown",
"title": "<state>:EC2インスタンス(<instance>)ステータスチェックアラート :rotating_light:",
"description": "<description>\nAccount: <account>\nInstance: `<instance>`\nMetrics: <metrics_name>\nAlarm: <alarm>\n`<timestamp>`",
"nextSteps": [
"インスタンスの状態を確認してください :woman-bowing:"
]
}
}

サンプルイベントを使用すると、出力をテストすることができます。
実際に想定した通りの形になるか、こちらで確認するといいでしょう。

確認
ボタンをクリックするとポップアップが閉じるので、再試行やデッドレターキューの設定をして、次へ進みます。
※今回はこちらについては特別な設定はしていません。

タグは必要に応じてつけましょう。

エラーなくルールが作成されれば完了です。

同じ手順の繰り返しになってしまうので、省略しますが、もう一つEventBridgeルールを作成して、アラームが回復した際の通知もカスタム通知で行うようにしました。

通知テスト
それでは動作確認をしていきましょう。
意図的にステータスチェック失敗を起こすために、NICを無効化します。
※万が一の時のために、実行する前にAMIを取得しておくことをお勧めします。
対象のインスタンスに接続して、下記のコマンドを実行します。
sudo ifdown eth0
ステータスチェックに失敗しています。

Slack に通知が届きました!

マークダウン形式で、絵文字もきちんと表示されてますね!
Chatbot のデフォルトの通知もいいですが、必要な情報だけ見やすい形に整形することができるカスタム通知は、色々な場面で重宝しそうです。
アラームのアクションで再起動を行うように設定しているため、自動的に回復します。回復通知も下記の通り、期待した形で通知されました。

注意点
こちらの確認方法では、EC2 インスタンスの NIC を無効化しています。設定によっては、再起動などをしても NIC が無効なままで EC2 に接続できないことがあります。
その場合は、AWS コンソールから追加でネットワークインターフェイスをアタッチしましょう。
もし、接続できなくなるリスクを避けたい場合は、AWS CLI を利用して、CloudWatch アラームのステータスをコマンドで変更して確認する方法もお勧めです。
まとめ
今回は、CloudWatch アラームの設定から Chatbot によるカスタム通知を紹介させていただきました。
Chatbot のカスタム通知機能は初めて使用しましたが、マークダウン形式で書くことができるのは便利ですね。
Slack で通知を行う際、必要な情報に絞る場合や見やすい形に内容を整形をしたい場合は、Webhook を利用して Lambda 関数を作成して対応することが多いと思います。
このカスタム通知機能を使えば、複雑なコードを書くことができなくても通知内容をカスタマイズすることができるため、比較的シンプルに実装することができます。
複雑さがない分、手早く設定できるのは嬉しいポイントですね!
今後の運用業務でも、有効的に活用していきたいと思います!