エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

Auto Scaling に対応した EC2 監視アラーム設定ツール

こんにちは、エムスリー 兼 QLife エンジニアの園田です。

昨日、小ネタで投稿した Auto-Scaling 配下の EC2 の CloudWatch Alarm 設定ツールを SAM の OSS として公開したので、その説明のポストとなります。

github.com

昨日の小ネタ記事はこちらです。
(小ネタ) AutoScaling で増減した EC2 インスタンスに動的に CloudWatch Alarm を設定 - エムスリーテックブログ

なぜ実装したか

エムスリーグループの QLife では、オンプレのインフラ環境を2018年の9月に AWS へ完全移行しました。

移行後の環境は ZABBIX 3 や Mackerel の監視が導入されていますが、新規サービスなどで AWS アカウントや VPC を追加した場合に、監視設定業務が ZABBIX のノウハウに依存し、属人化してしまいます。Mackerel などの外部サービスの場合、ホスト課金も増えてしまいます。

また、ZABBIX は Auto-Scaling に弱く、ホスト自動登録を行うためには(ノウハウ自体はありふれているが)標準機能にないハックが必要となります。

他の監視の仕組み( Prometheus など)も検討しましたが、どうせ監視システムの運用コストがかかるなら内製してしまえ!とカッとなって実装した次第であります。

また、運用監視の要件として、ホストごとの監視を必須とする場合(官公庁とかだとよくある)にもマッチするかと思われます。

ツール概要

S3 に監視定義用の JSON または YAML ファイルを置いて、Launch Template や Launch Configration のタグにそのファイルへの参照を設定します。 Auto-Scaling のインスタンス起動イベントを CloudWatch Event Rule で検知して Lambda を実行し、タグの値に紐付いた監視定義をインスタンスに対して適用(CloudWatch Alarm を作成)します。 インスタンス削除時も同様に、タグから定義を参照して当該アラームを削除します。

f:id:ryoheisonoda:20181226124017p:plain

説明だけだと意味不明だと思いますので、実際の監視設定手順を書きます。

監視定義 適用手順

1. リポジトリを clone

git clone https://github.com/QLife-Inc/ec2-auto-metric-alarms.git
cd ec2-auto-metric-alarms/

2. 監視定義ファイルを格納するバケットを作成

こちらは既存のバケットでも構いません。

aws s3api create-bucket \
  --bucket "your_bucket_name" \
  --acl private

3. 監視定義ファイルをアップロード

リポジトリの sample-definitions にサンプルがあるので、それを利用します。YAML を利用する場合は --content-type text/yaml を指定します。

aws s3api put-object \
  --bucket "your_bucket_name" \
  --key "your_prefix/cpu-util" \
  --body "sample-definitions/cpu-util.yml" \
  --content-type "text/yaml"

4. 監視を設定するインスタンスにタグを付与

AutoScalingGroup で利用する Launch Template や Launch Configuration の設定で EC2 インスタンスに付与するタグを追加します。 また、すでに起動済みのインスタンスに対して監視を適用する場合はインスタンスにも直接タグを付与する必要があります。 Auto-Scaling を介さずに動作確認を行う場合もインスタンスにタグを直接付与してください。

デフォルトのキーは AlarmDefinitionName です。この値に、先ほど S3 にアップロードしたオブジェクトのキー(ファイル名)を設定することで、インスタンスと監視定義を紐づけます。

f:id:ryoheisonoda:20181226101230p:plain

5. 通知先の SNS トピックを作成

緊急通知用と通常通知用の2つのトピックを作成します。トピック名はなんでも構いません。

aws sns create-topic --name "system-emergency-notification"
aws sns create-topic --name "system-notification"

トピックが作成できたら、適当なメールアドレスをサブスクリプションとして登録しておきます。

6. ツール(SAM)をデプロイ

sam コマンドがインストールされていない場合はインストールしてください。

pip install --user --upgrade aws-sam-cli # brew install aws-sam-cli

ソースをパッケージングします。sam package コマンドを実行すると、指定した S3 バケットに SAM のソースアーカイブがアップロードされます。このアップロードされたアーカイブから CloudFormation により Lambda 関数が作成されます。また、 template.yaml が CloudFormation テンプレートとしてコンパイルされます。

sam package --template-file "template.yaml" \
  --output-template-file "cloudformation.yml" \
  --s3-bucket "your_source_bucket" --s3-prefix "sam-sources"

sam deploy コマンドを実行します。このコマンドで、 package コマンドによって生成された cloudformation.yml から CloudFormation の Stack が作成されます。 コマンド実行時に CloudFormation のパラメータを渡す必要があります。 template.yamlParameters ディレクティブで Default が設定されているパラメータ以外は必須になります。

sam deploy --capabilities CAPABILITY_NAMED_IAM \
  --stack-name "your_stack_name" \
  --template-file "cloudformation.yml" \
  --parameter-overrides \
    DefinitionBucketName="your_bucket_name" \
    DefinitionObjectPrefix="your_prefix/" \
    FunctionName="ec2-auto-metric-alarms" \
    EmergencyTopicArn=${5.で作成した緊急通知先トピックのARN} \
    OrdinaryTopicArn=${5.で作成した通常通知先トピックのARN}

7. ツール実行

SAM のデプロイが成功すると Lambda 関数が作成されているはずですので、Lambda のコンソールから直接実行してみます。 このツールの挙動として、Auto-Scaling 以外のイベントデータを受け取った場合、現在起動中の監視定義タグが設定されているすべてのインスタンスに対して監視定義を更新するため、テストイベントデータは空の JSON で問題ありません。 作成された Lambda 関数の画面から、「テストイベントの設定」を開いて、空のイベントデータを設定します。

f:id:ryoheisonoda:20181226105019p:plain

作成したテストイベントを選択して「テスト」ボタンを押して正常終了すれば、4. でタグを設定したインスタンスに対する CloudWatch Alarm が生成されていることが確認できます。

f:id:ryoheisonoda:20181226110426p:plain

以上が簡単な監視定義の適用手順となります。

監視定義ファイルの解説

README にもモデルのスキーマについて説明がありますが、より詳しい説明を書きます。

このツールで利用する監視定義ファイルに記載する監視定義は CloudWatch の PutMetricAlarm API のリクエストモデルを拡張したモデルで、 JSON または YAML で定義します。

先ほどの手順でサンプルとして利用した cpu-util の監視定義ファイルは以下のようになっています。

---
_cpu_util: &cpu_util
  namespace: AWS/EC2
  dimensions:
  - name: InstanceId
    value: <%= instance.id %>
  metric_name: CPUUtilization
  statistic: Average
  threshold: 90
  unit: Percent
  comparison_operator: GreaterThanThreshold
  period: 300
  treat_missing_data: ignore

name: cpu-util
description: EC2 の CPU 監視テンプレート
alarm_definitions:
- <<: *cpu_util
  alarm_id: notice-cpu-util
  notification_type: ordinary
  alarm_name: ec2-<%= instance.id %>-notice-cpu-util
  alarm_description: <%= instance.name %> CPU使用率が 10 分間 90 % を超えていたら通知
  evaluation_periods: 2
  datapoints_to_alarm: 2
- <<: *cpu_util
  alarm_id: emergency-cpu-util
  notification_type: emergency
  alarm_name: ec2-<%= instance.id %>-emergency-cpu-util
  alarm_description: <%= instance.name %> CPU使用率が 30 分間 90 % を超えていたら通知
  evaluation_periods: 6
  datapoints_to_alarm: 6

よく見ると、 <%= instance.id %> などが登場して、 ERB のテンプレートになっていることがわかります。 この instanceAws::EC2::Instance のラッパーインスタンスで、 Lambda 実行時に埋め込まれるため、同じ監視定義でも AlarmName が重複することなく、複数の EC2 インスタンスの監視定義を作成できるようにしています(CloudWatch Alarm は Region と AlarmName で一意になる)。

監視定義の再利用

EC2 の監視といっても、そのインスタンスの用途によって監視内容は様々です。 例えば全ての EC2 で監視するメトリクス、ElasticBeanstalk のインスタンスで監視するメトリクス、ECS Cluster のインスタンスで監視するメトリクスなど、同じ EC2 というサービスでも異なるメトリクスに対する複数の監視定義が必要になることが多々あります。 それらについて、共通の監視定義を毎回実装するのは運用上避けたいので、他の監視定義をincludeすることで定義の継承を可能としています。

例えば、前述の CPU 監視とは別に、EC2 ステータスチェックの監視テンプレートがあるとします。

---
name: ec2-status
description: EC2 の ステータス 監視テンプレート
alarm_definitions:
- alarm_id: notice-status-check
  notification_type: ordinary
  alarm_name: ec2-<%= instance.id %>-notice-status-check
  alarm_description: <%= instance.name %> ステータスチェックが失敗したら通知
  namespace: AWS/EC2
  dimensions:
  - name: InstanceId
    value: <%= instance.id %>
  metric_name: StatusCheckFailed
  statistic: Maximum
  threshold: 1
  comparison_operator: GreaterThanThreshold
  period: 300
  evaluation_periods: 1
  datapoints_to_alarm: 1
  treat_missing_data: ignore

CPU 監視とステータス監視はすべての EC2 インスタンスで利用したい、というユースケースの場合、以下のようにアップロード済みの監視定義を継承することができます。

---
name: ec2-default
description: EC2 の共通監視テンプレート
include_definitions: [ cpu-util, ec2-status ]

ステージング環境など、緊急通知を送信したくないインスタンスに対しては以下のように緊急通知だけを除外した定義を利用できます。

---
name: ec2-default-without-emergency
description: EC2 の共通監視テンプレート(緊急通知除外)
include_definitions: [ ec2-default ]
ignore_alarm_ids: [ emergency-cpu-util ]

このように監視要件に合わせた監視定義の ERB テンプレートを監視対象となるサーバロールごとに実装して、 S3 にアップロードしてタグで参照してあげれば OK です。

エンジニア募集!!

QLife では共にチーム一丸となってより良いものづくりにこだわれる仲間を募集中です! 小さいサービスが多いので新しい AWS のサービスの利用にも非常に積極的に取り組んでいます! カジュアル面談も行っていますので、興味がある方は entry@qlife.co.jp に「カジュアル面談希望」とメールをください!

www.qlife.co.jp

エムスリーでもエンジニアを随時募集しています!共に医療 × テクノロジーの未来を切り拓いてくれる仲間を募集中です! AWS 以外にも GCP や Firebase などのクラウドも活用しています!興味がある方はカジュアル面談やTechtalkにおこしください!!

jobs.m3.com