以下の内容はhttps://techblog.techfirm.co.jp/entry/ec2-longrun-cloudwatch-alarmより取得しました。


CloudWatch標準メトリクスだけでEC2の長時間稼働を検知する

みなさんEC2インスタンスを運用していて、うっかり停止するのを忘れてしまうことってありませんか?

  • 一時的な検証のために必要になってしばらく起動させていたけど、検証期間が終わっても停止していなかった
  • サービスでの利用開始はまだ先なので停止しておきたいけど、ちょくちょく起動して設定修正するので、停止し忘れていないか心配になる
  • 必要になった時だけ起動してtoolを実行する運用が徹底されない
  • 自動停止の設定をしてあるが、停止スケジュール後も利用したくて手動で起動してそのまま放置

EC2インスタンスの自動停止の仕組みを導入するほどではないけど、止め忘れてしまったことには気づきたい、しかもなるべく簡単に。
今回は、CloudWatch標準メトリクスを使って、長時間起動し続けているEC2インスタンスをお手軽に検知する方法を紹介します。

CloudWatchメトリクスのデータ不足を利用する

EC2の標準メトリクスには、インスタンスが何時間起動しているかを示す、Uptimeのようなメトリクスはありません。
実現方法を検索すると、CloudWatchエージェントをインストールしてカスタムメトリクスを作成したり、
LambdaやDynamoDBを駆使して、監視システムを構築する方法の紹介記事がヒットしました。

なるべく手間をかけず、一時的に監視しておきたいだけだったので、CloudWatch標準メトリクスだけで何とかならないか...
考えた結果、インスタンスが起動しているときだけCloudWatchメトリクスの値が記録される ことを利用することにしました。

インスタンス停止中のCloudWatchメトリクス

このとおり、インスタンスの起動中は、値が記録され、停止中は値がありません。
CloudWatchアラームではデータ不足となる期間です。
メトリクスの値が欠落なく連続して存在している期間を取得できれば、インスタンスの連続稼働時間として検知できそうです。

たとえば、インスタンスが24時間以上連続で稼働していることを検知するCloudWatchアラームは次のような設定となります。

  • メトリクス名(Metric Name): 任意のEC2の標準メトリクス
  • しきい値(Threshold): 0.0以上
  • 期間(Period):600
  • 評価期間(Evaluation Periods):144
    • 24h(86400s)を期間(600s)で割ると144回評価をおこなうことになります
  • アラームを実行するデータポイント(Datapoints to Alarm):144
    • 評価期間すべてのデータポイントでメトリクスの値がある場合ALARMとなります
  • 欠落データの処理(Missing data treatment):欠落データを適正として処理(notBreaching
    • データが存在しない期間は停止中なので正常とします

10分間隔でチェックをして、24h(144回)連続でメトリクスの値が存在した場合にアラート状態となります。
あとは、アラートアクションでSNSトピックスにメッセージ送信したり、EC2アクションで停止する設定を行ってください。

必要な設定は以上となります。
以降はアラームの使い勝手を改善したパターンを紹介します。

数式を使ったアラームで検知時間を短縮する

前述のような条件でCloudWatchアラームを作成した場合、状態がALARMからOKに戻るのに、期間(Period)以上の時間が必要になります。
これは、しばらく欠落データが続いた場合に、直近に存在しているデータポイントの値を使って評価し続けるためです。
検証を行ったときは、インスタンスを停止してから30分ほどたってから、ようやくアラームの状態が正常にもどるような状況でした。

データが欠落した場合のアラーム状態の評価方法

インスタンスの停止をスムーズに通知するためには、データがあれば1、欠落データなら0というように、データ欠損がないグラフに変換します。
CloudWatchメトリクスで、対象インスタンスの任意のメトリクスをグラフ表示させたら、次のようなMetric Mathを使った数式を追加してください。

FILL(IF(m1 >= 0, 1, 0),0)
※m1は対象インスタンスの標準メトリクスを指定したグラフ
  • FILL関数で対象のメトリクスの値が欠損している場合0で埋める
  • IF関数で対象のメトリクスの値が0以上なら1、それ以外は0に変換する

この数式で作成されたグラフをCloudWatchアラームに設定すれば、インスタンスを停止した次の評価のタイミングでアラーム状態がOKに戻るようになります。

数式を使ったCloudWatchアラーム

注意点は、数式を使ったメトリクスに基づいたCloudWatchアラームでは、EC2アクションを選択することができません。 このアラームを使ってインスタンスを自動停止したい場合は、EventBridgeルールでアラームのステータス変化を監視して、インスタンス停止アクションを実行してください。

Terraformモジュールで簡単に設定

毎回AWSマネジメントコンソールからアラーム設定するのは手間なので、Terraformのモジュールにして、簡単に登録できるようにしました。

modules/EC2-UptimeCheck
├── main.tf
├── output.tf
└── variable.tf

パラメータ

Name Description Type Default Required
instance_id インスタンスID string N/A yes
instance_name アラーム名につける文字列
省略した場合instance_idが使われます
string "variable undefined" no
sns_topic_arn アラーム・OKアクションに指定するSNSトピックス string N/A yes
uptime_threshold 稼働時間のしきい値 number 86400 no
check_period 監視間隔 number 600 no

main.tf

locals {
  instance_name = var.instance_name == "variable undefined" ? var.instance_id : var.instance_name
}

resource "aws_cloudwatch_metric_alarm" "EC2Uptime_check" {
  alarm_name  = "EC2UptimeCheck_${local.instance_name}"
  alarm_description  = "Uptime_check"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "${var.uptime_threshold / var.check_period}"
  datapoints_to_alarm = "${var.uptime_threshold / var.check_period}"
  threshold          = "1"
  treat_missing_data = "notBreaching"

  metric_query {
    id = "m1"
    metric {
      metric_name = "StatusCheckFailed"
      namespace   = "AWS/EC2"
      dimensions = {
        InstanceId = var.instance_id
      }
      period             = var.check_period
      stat               = "Maximum"
    }
  }
  metric_query {
    id          = "e1"
    expression  = "FILL(IF(m1 >= 0, 1, 0),0)"
    label       = "IsRunning"
    return_data = "true"
  }

  alarm_actions = [
    var.sns_topic_arn
  ]
  ok_actions = [
    var.sns_topic_arn
  ]
}

variable.tf

variable "instance_id" {
  description = "インスタンスid"
  type = string
}

variable "instance_name" {
  description = "インスタンス名"
  type        = string
  default = "variable undefined"
}

variable "sns_topic_arn" {
  description = "SNSトピックスARN"
  type = string
}

variable "uptime_threshold" {
  description = "稼働時間閾値(sec)"
  type = number
  default = 86400 # 1day
}

variable "check_period" {
  description = "検証間隔(sec)"
  type = number
  default = 600
}

output.tf

output "EC2UptimeCheck_alarm" {
  value = aws_cloudwatch_metric_alarm.EC2Uptime_check.id
}

まとめ

EC2インスタンスの止め忘れをとにかく簡単に検知したい方は、今回紹介した方法をぜひお試しください。
検知するだけでなく、スケジュールで自動停止したい方は、本ブログでも方法を紹介していますので、そちらもチェックしてみてください。



参考サイト

CloudWatch メトリクスでの数式の使用
メトリクス数式に基づく CloudWatch アラームの作成




以上の内容はhttps://techblog.techfirm.co.jp/entry/ec2-longrun-cloudwatch-alarmより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14