SlackチャンネルにEC2インスタンスが落ちたことを通知してくれるようにしてみた
背景
時折、開発環境のEC2インスタンスが勝手に落ちていることがあり、気が付いた人からの問い合わせでその都度手動でEC2インスタンスを再起動していました。
開発者達が開発に集中できるようにするために自動で通知してくれるような仕組みを構築しました。
実装一覧
- SlackチャンネルにEC2インスタンスがstoppedになったことを通知できるようにする
- stoppedになったら、自動で再起動できるようにする
- 上記の機能を9:00~21:00の間に限定できるようにする
9:00~21:00の間に制限を設けている理由ですが、開発環境ではコスト改善のために21時になったら、自動で開発環境のすべてのEC2インスタンスがshutdownされ、翌朝9時になったら自動で起動できるようにしてあります。
なので、仮に上記の機能が21時以降も有効になってしまうと、幾つものEC2インスタンスのステータス通知がSlackチャンネルを埋めるだけでなく、深夜早朝帯も開発環境が起動され続けるという恐れがあります。
使用ツール
- CloudWatch Event
- Lambda
- Incoming Webhook
手順
SlackからIncoming Webhookアプリをインストールする
何かしらのサービス情報をSlackのチャンネルに通知するためにはIncoming WebhookというSlackアプリを活用することで実現可能となります。
Lambda関数を作成する
新規でLambda関数を作成し、以下の関数を書きます。
from __future__ import print_function
from time import strptime, strftime
import os, json, boto3, urllib.request
#GMT表記
before = strptime('00:00:00', '%H:%M:%S')
after = strptime('11:59:00', '%H:%M:%S')
now = strptime(strftime('%H:%M:%S'), '%H:%M:%S')
print(now)
#自動起動/シャットダウンバッチに引っかからないようにJST9:00-JST21:00の間で処理を動かすようにしている
if (now >= before and now <= after):
#変数eventの中身がCloudWatch Eventから受け取ったEC2インスタンスのstop
def lambda_handler(event,context):
instances = [ event ]
region = 'ap-northeast-1'
ec2 = boto3.client('ec2', region_name=region)
#色付きメッセージ発行
attachments = {
'attachments': [{
# titleのリンクをクリックするとtitle_linkで設定したページへ飛びます。
'title': 'EC2が停止しました。',
'title_link': 'https://api.slack.com/docs/message-attachments',
'color': "warning",
}]
}
req = json.dumps(attachments).encode('ascii')
message_color = urllib.request.Request(os.environ['slackUrl'], req )
try:
response = urllib.request.urlopen(message_color)
response.read()
except Exception as e:
print(e)
#インスタンスの起動停止メッセージ
message_stop = {
'text': "<!here> EC2 Instance " + (event) + " stopped"
}
data = json.dumps(message_stop).encode('ascii')
req = urllib.request.Request(os.environ['slackUrl'], data )
try:
response = urllib.request.urlopen(req)
response.read()
print("Message posted: %s" % message_stop )
except Exception as e:
print(e)
#停止したインスタンスの再起動
ec2.start_instances(InstanceIds=instances)
message_restart = {
'text': "started your instances: " + str(instances)
}
data2 = json.dumps(message_restart).encode('ascii')
req = urllib.request.Request(os.environ['slackUrl'], data2 )
try:
response = urllib.request.urlopen(req)
response.read()
print("Message posted: %s" % message_restart )
except Exception as e:
print(e)
#自動起動/シャットダウンが走る9時前、21時以降は処理をさせない
else:
print("out time of function")
関数内で未定義の変数slackUrlはLambda側から取得したWebhookURLをセットしました。
CloudWatch EventでLambda関数が発動するルールを作成する
EC2インスタンスがstoppedになったら先ほど作成したLambda関数を起動できるようにルールを作成します。
この時、Lambda関数へ渡す入力内容を絞ることでSlackチャンネルに表示される内容を見やすいものに加工できます。
※全表示の場合(一部モザイク処理)
以上で、Slackチャンネルへ通知する手順は完了しました。最後にEC2インスタンスを停止させて、Slackチャンネルへ通知が届き、再起動ができるか確認します。
ちゃんと実装したかったことができるようになりました。
所感
AWSとSlackの連携方法として、CloudWatch AlarmからLambdaを飛ばしてSlackチャンネルへアラーム内容を連携する方法が一般的で、検索すると多くの記事が出てきてLambda関数にもそれ用のテンプレートがあります。
また、最近AWSとSlackが、提携を発表しそれぞれのサービスの結びつきが今後ますます強くなっていくと思われます。
最近ですと、AWS Chatbotで簡単にAWSサービスの内容をSlackへ連携しやすくなりましたが、まだ提供できるサービスが少ないためCloudWatch Eventでも対応できるようになりましたら検証してみたいと思います。
2021年12月25日追記
AWS Chatbotの機能も大分増えていきAmazon EventBridgeを使ってSlackへ連携したほうが簡単に通知できそうです。
LT資料
この記事を基にしてLTを行ないましたので資料を置いておきます。
Discussion