Auto Scalingイベントの発生をLambdaでChatWorkに通知する
こんにちは、虎塚です。
Auto Scalingを使ってシステムを運用していると、インスタンスが増えたり減ったりした時に、チャットに通知したい場合があります。
今回は、Auto Scalingのイベントが発生したら、S3上にあらかじめ用意しておいた設定を読み込んで、ChatWorkの特定の部屋に通知を送るLambdaスクリプトを紹介します。
この仕組みの使い方
この仕組みの入力と出力を説明します。
この仕組みを利用するにあたって、あらかじめS3バケットに次のような設定情報を保存しておきます。
- Auto Scaling group名
- Auto Scaling groupのイベント種別
- メッセージ投稿先のChatWorkルームID
- 投稿するメッセージ
具体的には、次のようなJSONファイルを用意します。
{ "actions": [ { "autoScalingGroupName": "test-asg", "notificationType": "autoscaling:EC2_INSTANCE_TERMINATE", "actionDetail": { "roomId": "12345678", "message": "an instance was terminated in an Auto Scaling group." } }, { "autoScalingGroupName": "test-asg", "notificationType": "autoscaling:EC2_INSTANCE_LAUNCH", "actionDetail": { "roomId": "12345678", "message": "an instance was launched in an Auto Scaling group." } } ] }
上のファイルには、Auto Scaling group (test-asg) でEC2のterminateまたはlaunchイベントが起きたら、ChatWorkのルーム (12345678) にメッセージを投稿するための設定を記述しています。
設定ファイルを保存するS3バケットは、test170313-bucket、設定ファイルのオブジェクトキーは、{AWS_ACCOUNT_ID}/sns.jsonとします。(S3バケット名は、全アカウントで一意でなければならないため、取得できる名前に読み替えてください)
実際にAuto Scalingのイベントが起きると、ChatWorkに次のようなメッセージが投稿されます。
環境構築と動作確認の手順を、以降で説明します。
仕組みの構築手順
1. S3バケットの作成
設定ファイルを保存するS3バケット (test170313-bucket) を作成します。
aws s3api create-bucket \ --bucket test170313-bucket \ --create-bucket-configuration LocationConstraint=ap-northeast-1
2. Lambda関数の作成
2.1. Pythonスクリプトの作成
ローカルで次のPythonスクリプトを作成します。S3バケット名とChatWorkのAPIキーは、環境に合わせて読み替えてください。
#!/usr/bin/env python # -*- encoding: utf-8 -*- from __future__ import print_function import boto3 import json import logging import requests S3_BUCKET = 'test170313-bucket' CHAT_APIKEY = '12345678901234567890123456789012' CHAT_ENDPOINT = 'https://api.chatwork.com/v2' logger = logging.getLogger() logger.setLevel(logging.INFO) def create_message(data): message = '\nAccount: {}, AutoScaling group: {}, instance: {}' return message.format( data['AccountId'], data['AutoScalingGroupName'], data['EC2InstanceId']) def get_setting(account): key = account + '/sns.json' obj = boto3.resource('s3').Object(S3_BUCKET, key) resp = obj.get() body = resp['Body'].read() return json.loads(body.decode('utf-8')) def select_action(setting, data): for action in setting['actions']: if action['notificationType'] == data['Event'] and action['autoScalingGroupName'] == data['AutoScalingGroupName']: return action['actionDetail'] def post_chat(action, data): body = '[info]' + action['message'] + create_message(data) + '[/info]' post_message_url = '{}/rooms/{}/messages'.format(CHAT_ENDPOINT, action['roomId']) headers = { 'X-ChatWorkToken': CHAT_APIKEY } params = { 'body': body } resp = requests.post(post_message_url, headers=headers, params=params) logger.info(resp.content) def lambda_handler(event, context): for record in event['Records']: data = json.loads(record['Sns']['Message']) logger.info(data) setting = get_setting(data['AccountId']) action = select_action(setting, data) post_chat(action, data)
2.2. デプロイパッケージの作成
デプロイパッケージ (post_chat.zip) を作成します。作業用にAmazonLinuxインスタンスを1つ起動し、ステップ2.1で作成したpost_chat.pyをscpで$HOMEにコピーします。
scp -i ~/.ssh/key.pem ./post_chat.py ec2-user@XXX.XXX.XXX.XXX:
インスタンスにSSHログインして、virtualenvで作業します。
cd /tmp/work/ . venv/bin/activate
スクリプトに必要なライブラリをインストールします。
pip install boto3 pip install requests
関連ファイルをzipにパッケージします。
cd $VIRTUAL_ENV/lib/python2.7/site-packages zip -r9 ~/post_chat.zip * cd ~ zip -g post_chat.zip post_chat.py
インスタンスからログアウトし、作成したzipファイルをローカルにコピーします。
scp -i ~/.ssh/key.pem ec2-user@XXX.XXX.XXX.XXX:post_chat.zip .
2.3. Lambda実行ロールの作成
まず、Lambdaの実行ロール (chat-notify-role) を作成します。ローカルにテキストファイルで次の信頼ポリシーを作成します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
上の信頼ポリシーを指定して、IAMロール (chat-notify-role) を作成します。
aws iam create-role \ --role-name chat-notify-role \ --assume-role-policy-document file://AllowLambdaAssumeRole.json
次に、Lambdaの実行ロールにアクセスポリシーをつけます。
# Lambdaの実行ログの出力を許可します。一般的なLambdaの実行ロールの権限です。 aws iam attach-role-policy \ --role-name chat-notify-role \ --policy-arn arn:aws:iam::aws:policy/AWSLambdaExecute # S3の読み取りを許可します。 aws iam attach-role-policy \ --role-name chat-notify-role \ --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
2.4. デプロイパッケージのアップロード
ステップ2.2のデプロイパッケージとステップ2.3のIAMロールを指定して、Lambda関数 (post_chat) を作成します。
aws lambda create-function \ --function-name post_chat \ --runtime python2.7 \ --role arn:aws:iam::123456789012:role/chat-notify-role \ --handler post_chat.lambda_handler \ --zip-file fileb://post_chat.zip
動作確認
構築した仕組みを実際に使ってみましょう。
1. テスト用のAuto Scaling環境構築
Launch Configuration (test-lc) とAuto Scaling group (test-asg) を作成します。
aws autoscaling create-launch-configuration \ --launch-configuration-name test-lc \ --image-id ami-56d4ad31 \ # latest AmazonLinux AMI --key-name KEY_PAIR_NAME \ --security-groups default \ --instance-type t2.micro \ --iam-instance-profile INSTANCE_ROLE_NAME aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name test-asg \ --availability-zones ap-northeast-1c \ --launch-configuration-name test-lc \ --min-size 1 \ --max-size 1
ここでは、現時点で最新のAmazonLinuxインスタンスが1つだけ起動するAuto Scaling groupを作成しました。
Auto Scaling groupのイベント通知を配信するためのSNSトピック (test-topic) を作成します。
aws sns create-topic --name test-topic
実行結果の戻り値のTopicArn (仮にarn:aws:sns:ap-northeast-1:123456789012:test-topic) を後で使うため控えておきます。
Auto Scaling group (test-asg) 配下で、インスタンスが起動または終了した場合に、SNSトピック (test-topic) に通知を配信するように設定します。
aws autoscaling put-notification-configuration \ --auto-scaling-group-name test-asg \ --topic-arn arn:aws:sns:ap-northeast-1:123456789012:test-topic \ --notification-types "autoscaling:EC2_INSTANCE_TERMINATE" "autoscaling:EC2_INSTANCE_LAUNCH"
2. SNSトピックとLambda関数の連携
ステップ1のSNSトピックを、仕組み構築で作成したLambda関数 (post_chat) にサブスクライブさせます。
事前にLambda関数 (post_chat) のARNを確認します。
aws lambda get-function \ --function-name post_chat \ | jq '.Configuration.FunctionArn' "arn:aws:lambda:ap-northeast-1:123456789012:function:post_chat"
SNSトピック (test-topic) をLambda関数 (post_chat) でサブスクライブします。
aws sns subscribe \ --topic-arn arn:aws:sns:ap-northeast-1:123456789012:test-topic \ --protocol lambda \ --notification-endpoint arn:aws:lambda:ap-northeast-1:123456789012:function:post_chat
Lambda関数 (post_chat) に権限を追加して、SNS通知を受け取れるようにします。
aws lambda add-permission \ --function-name post_chat \ --statement-id AllowSnsSubscribe \ --action "lambda:InvokeFunction" \ --principal sns.amazonaws.com \ --source-arn arn:aws:sns:ap-northeast-1:123456789012:test-topic
3. 設定ファイルの作成とS3への設置
前半に載せた設定ファイルを再掲します。
{ "actions": [ { "autoScalingGroupName": "test-asg", "notificationType": "autoscaling:EC2_INSTANCE_TERMINATE", "actionDetail": { "roomId": "12345678", "message": "an instance was terminated in an Auto Scaling group." } }, { "autoScalingGroupName": "test-asg", "notificationType": "autoscaling:EC2_INSTANCE_LAUNCH", "actionDetail": { "roomId": "12345678", "message": "an instance was launched in an Auto Scaling group." } } ] }
上の設定ファイルをS3バケット (test170313-bucket) に保存します。
aws s3api put-object \ --body sns.json \ --bucket test170313-bucket \ --key 123456789012/sns.json
キーの一部の123456789012は、AWSアカウントIDです。
4. 通知の確認
Auto Scaling group配下のインスタンスを手動で削除して、ChatWorkに通知がくることを確認しましょう。
Auto Scaling group (test-asg) 内で起動しているインスタンスのIDを確認します。出力内容は一例です。
aws autoscaling describe-auto-scaling-groups \ --auto-scaling-group-names test-asg \ | jq '.AutoScalingGroups[] \ | .AutoScalingGroupName, .Instances[].InstanceId' "test-asg" "i-0a9e3c4b44d996d56"
仮に上のような値が取れた場合、次のようなコマンドでインスタンスをterminateします。
aws ec2 terminate-instances --instance-ids i-0a9e3c4b44d996d56
インスタンスが削除されると、Auto Scaling groupはヘルスチェックの失敗を検知して、新しいインスタンスを起動します。数分待つと、チャットにインスタンスの削除と起動の通知が送られてきます。通知例を再掲します。
発展
次のクロスアカウント設定と組み合わせると、Auto Scaling groupが他のAWSアカウントにある場合も、SNS通知をLambda関数で受け取ることができます。
また、今回は設定ファイルをS3に直接置きましたが、API Gateway経由で設定をPOSTできるように拡張すれば、さらに便利に使えるかもしれません。
おわりに
運用自動化の一例として、Auto Scalingイベントの発生をChatWorkに通知するLambdaスクリプトをご紹介しました。
それでは、また。