この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、虎塚です。
Auto Scalingを使ってシステムを運用していると、インスタンスが増えたり減ったりした時に、チャットに通知したい場合があります。
今回は、Auto Scalingのイベントが発生したら、S3上にあらかじめ用意しておいた設定を読み込んで、ChatWorkの特定の部屋に通知を送るLambdaスクリプトを紹介します。
この仕組みの使い方
この仕組みの入力と出力を説明します。
この仕組みを利用するにあたって、あらかじめS3バケットに次のような設定情報を保存しておきます。
- Auto Scaling group名
- Auto Scaling groupのイベント種別
- メッセージ投稿先のChatWorkルームID
- 投稿するメッセージ
具体的には、次のようなJSONファイルを用意します。
sns.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キーは、環境に合わせて読み替えてください。
post-chat.py
#!/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) を作成します。ローカルにテキストファイルで次の信頼ポリシーを作成します。
AllowLambdaAssumeRole.json
{
"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への設置
前半に載せた設定ファイルを再掲します。
sns.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."
}
}
]
}
上の設定ファイルを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スクリプトをご紹介しました。
それでは、また。