こんにちは、エムスリーエンジニアの園田です。
AWS の AutoScaling で増減する EC2 インスタンスに対して CloudWatch Alarm を動的に設定したくなることありますよね?
AutoScalingGroup のメトリクスで AutoScalingGroup 内の平均 CPU 利用率などを監視することはできますが、
個々のインスタンスそれぞれに対してアラームを設定することは(今のところ)できません。
正確には、設定したとしても AutoScaling で新しく起動したインスタンスには設定されませんし、
スケールインで削除されたからといって自動でアラームの設定が削除されることもありません。
そこで、 CloudWatch Event Rule と Lambda を使って増減するインスタンスに対して動的にアラームを設定するのを試してみました。(何番煎じかわかりませんが・・・)
なお、この記事は エムスリーアドベントカレンダー ではないです。
監視シナリオ
今回は、以下のシナリオを例として実装してみます。
EC2 インスタンスのCPU使用率が 10 分間 80 % を超えていたら、SNSトピックに通知する。
イベントデータの JSON 確認
Lambda を実装する上で、CloudWatch Event からどういったイベントデータを受け取れるのかを確認しなければいけません。
イベントデータの JSON を確認するために、マネジメントコンソールで CloudWatch > イベント > ルール の ルールの作成
ボタンをポチります。ルールの作成画面で以下のように設定すると、サンプルイベントデータが表示されるので、コピっておきます。
表示されたサンプルイベントデータは以下の通りです。
{ "version": "0", "id": "3e3c153a-8339-4e30-8c35-687ebef853fe", "detail-type": "EC2 Instance Launch Successful", "source": "aws.autoscaling", "account": "123456789012", "time": "2015-11-11T21:31:47Z", "region": "us-east-1", "resources": [ "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:eb56d16b-bbf0-401d-b893-d5978ed4a025:autoScalingGroupName/sampleLuanchSucASG", "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" ], "detail": { "StatusCode": "InProgress", "AutoScalingGroupName": "sampleLuanchSucASG", "ActivityId": "9cabb81f-42de-417d-8aa7-ce16bf026590", "Details": { "Availability Zone": "us-east-1b", "Subnet ID": "subnet-95bfcebe" }, "RequestId": "9cabb81f-42de-417d-8aa7-ce16bf026590", "EndTime": "2015-11-11T21:31:47.208Z", "EC2InstanceId": "i-b188560f", "StartTime": "2015-11-11T21:31:13.671Z", "Cause": "At 2015-11-11T21:31:10Z a user request created an AutoScalingGroup changing the desired capacity from 0 to 1. At 2015-11-11T21:31:11Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1." } }
今回利用するのは、起動したのか削除されたのかを判別するための $.detail-type
と、インスタンスを特定するための $.detail.EC2InstanceId
だけです。
Lambda 関数の実装
これを参考に、Lambda を実装してみます。せっかくなので、最近使えるようになった Ruby で実装しました。
追加ライブラリを利用していないため、パッケージのアップロードは不要です。
require 'json' require 'aws-sdk' # 通知先の SNS トピックは Lambda の環境変数に設定しておく TOPIC_ARN = ENV['TOPIC_ARN'] # 実際にプロダクション利用する場合はイベントデータから受け取ったリージョンを指定してください EC2 = Aws::EC2::Resource.new CW = Aws::CloudWatch::Resource.new # サンプルイベントデータを参考に、eventから必要な値を取得して処理する def lambda_handler(event:, context:) instance_id = event['detail']['EC2InstanceId'] case event['detail-type'] # スケールアウトして新しくインスタンスが起動した場合 when 'EC2 Instance Launch Successful' on_launched(instance_id) # スケールインしてインスタンスが削除された場合 when 'EC2 Instance Terminate Successful' on_terminated(instance_id) end end def on_launched(instance_id) instance = EC2.instance(instance_id) return unless instance.exists? return if CW.alarm("ec2-#{instance.id}-cpu-util-notification").exists? # CloudWatch Alarm を作成 CW.client.put_metric_alarm({ alarm_name: "ec2-#{instance.id}-cpu-util-notification", alarm_description: "CPU使用率が 10 分間 80 % を超えていたら通知", # EC2 namespace: 'AWS/EC2', # インスタンスを指定 dimensions: [ { name: "InstanceId", value: instance.id } ], # CPU 使用率の平均 metric_name: 'CPUUtilization', statistic: "Average", # > 80 % threshold: 80, unit: 'Percent', comparison_operator: "GreaterThanThreshold", # 10 分間 (5 分 * 2 回) のうち 2 回 period: 300, evaluation_periods: 2, datapoints_to_alarm: 2, # データなしは無視 treat_missing_data: "ignore", # 閾値を超えたら警告通知 alarm_actions: [ TOPIC_ARN ], # 元に戻ったことも通知 ok_actions: [ TOPIC_ARN ], }) end # インスタンスが削除されたらアラームも削除 def on_terminated(instance_id) alarm = CW.alarm("ec2-#{instance_id}-cpu-util-notification") alarm.delete if alarm.exists? end
Lambda の実行ロールには以下の権限をつけておきます。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "cloudwatch:PutMetricAlarm", "cloudwatch:DeleteAlarms", "cloudwatch:DescribeAlarms" ], "Resource": "*" } ] }
サンプルイベントデータをテストイベントに設定して、インスタンスIDだけ実在するIDに変更して Lambda をテスト実行してみると、CloudWatch Alarm が作成されます。
aws cloudwatch describe-alarms --alarm-name-prefix ec2-
{ "MetricAlarms": [ { "AlarmName": "ec2-i-xxxxxxxxxxxxxxxx-cpu-util-notification", "AlarmArn": "arn:aws:cloudwatch:ap-northeast-1:999999999999:alarm:ec2-i-xxxxxxxxxxxxxxxx-cpu-util-notification", "AlarmDescription": "CPU使用率が 10 分間 80 % を超えていたら通知", "AlarmConfigurationUpdatedTimestamp": "2018-12-07T08:19:33.703Z", "ActionsEnabled": true, "OKActions": [ "arn:aws:sns:ap-northeast-1:999999999999:system-notification" ], "AlarmActions": [ "arn:aws:sns:ap-northeast-1:999999999999:system-notification" ], "InsufficientDataActions": [], "StateValue": "OK", "StateReason": "Threshold Crossed: 2 out of the last 2 datapoints [0.0 (07/12/18 08:14:00), 0.0 (07/12/18 08:09:00)] were not greater than the threshold (80.0) (minimum 2 datapoints for ALARM -> OK transition).", "StateReasonData": "{\"version\":\"1.0\",\"queryDate\":\"2018-12-07T08:19:34.301+0000\",\"startDate\":\"2018-12-07T08:09:00.000+0000\",\"unit\":\"Percent\",\"statistic\":\"Average\",\"period\":300,\"recentDatapoints\":[0.0,0.0],\"threshold\":80.0}", "StateUpdatedTimestamp": "2018-12-07T08:19:34.311Z", "MetricName": "CPUUtilization", "Namespace": "AWS/EC2", "Statistic": "Average", "Dimensions": [ { "Name": "InstanceId", "Value": "i-xxxxxxxxxxxxxxxx" } ], "Period": 300, "Unit": "Percent", "EvaluationPeriods": 2, "DatapointsToAlarm": 2, "Threshold": 80.0, "ComparisonOperator": "GreaterThanThreshold", "TreatMissingData": "ignore" } ] }
テストデータの detail-type
を EC2 Instance Terminate Successful
に変更して実行すれば、アラームが削除されることも確認できます。
CloudWatch Event Rule の作成
Lambda 関数ができたら、先ほど作りかけた CloudWatch Event Rule を作成します。
イベントタイプに EC2 Instance Launch Successful
と EC2 Instance Terminate Successful
を指定しています。
まとめ
CloudWatch Event Rule と Lambda の組み合わせで AutoScalingGroup で増減するインスタンスに動的にアラームを設定できました。今回は CPU使用率でしたが、 CloudWatch Agent と組み合わせればメモリやディスクの監視通知も可能ですし、ECS タスクでも同様のことができると思います。
2018/12/27 追記
この仕組みを利用した SAM (Serverless Application Model) を OSS として公開しました。 公開した SAM については後日またこのテックブログにポストしたいと思います。
エンジニア募集!!
エムスリーでは、共に医療 × テクノロジーの未来を切り拓いてくれる仲間を募集中です! AWS 以外にも GCP や Firebase などのクラウドも活用しています!興味がある方はカジュアル面談やTechtalkにおこしください!!