CloudWatch Agent を試す

CloudWatch の EC2 のメトリクスだとメモリ使用率やディスク使用率が取れないので別途 Amazon CloudWatch Monitoring Scripts でカスタムメトリクスとして取得したりしていましたが、今日日は CloudWatch Agent を使えばいいだろうので素振り。

残骸はこちら

IAM ロール

EC2 インスタンスで CloudWatch Agent を実行するためには IAM ロールをインスタンスプロファイルで付与する必要があります。ポリシーは AWS 管理の arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy で OK です。

注意点として、CloudWatch Agent は設定ファイルをを SSM Parameter store から取得することができるのですが、このポリシーを使う場合は AmazonCloudWatch- という名前で始まるパラメータ名で設定ファイルを保存する必要があります(ポリシーでそのように制限されている)。

CloudWatch Agent のセットアップ

手作業での手順は次のとおり。これに相当する手順を Terraform で EC2 のユーザーデータに入れてます。

# amazon-cloudwatch-agent のインストール
sudo yum install https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm

# 設定ファイルを作成
sudo vim /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json

# 設定ファイルをインポートしてエージェントを開始
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 \
    -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s

ユーザーガイドでは SSM (AWS Systems Manager) で CloudWatch Agent をセットアップしていますが、そうはせずに Terraform で EC2 インスタンスのユーザーデータに必要な情報を書き込んで cloud-init でセットアップしました。

設定ファイル

前述の手作業の手順の最後のコマンドは、amazon-cloudwatch-agent.json を TOML 形式の設定ファイルに変換して amazon-cloudwatch-agent.toml に保存した後にエージェントを開始します。

はじめから amazon-cloudwatch-agent.toml を作成して配置すればいいような気もしますが amazon-cloudwatch-agent.jsonamazon-cloudwatch-agent.toml とを比べてみると単にフォーマットが変換されただけではなくいろいろ違いがあるので、設定を容易にするために変換をかますようになっているのかもしれません。

また、amazon-cloudwatch-agent.toml には InstanceId がベタ書きされているので(CloudWatch Logs へのログ転送を設定しているなら)、あらかじめ CloudWatch Agent がセットアップされた AMI を作成して使う場合でも amazon-cloudwatch-agent-ctl コマンドは必要です。

設定ファイルの書き方は下記を参考にします。

なお、amazon-cloudwatch-agent-config-wizard という TUI のウィザード形式で設定ファイルを作成するツールがあるので、このツールが作成したファイルを編集して使用すると良いかもしれません。

収集できるメトリクス

収集できるメトリクスの一覧は次のとおりです。

また、procstat で特定のプロセスのメトリクスを取得したりもできます。

これら以外に StatsDcollectd が収集したメトリクスを CloudWatch に記録することもできるようです。

メトリクスに固有のディメンジョン

メトリクスによっては固有のディメンジョンが設けられていることがあります。

例えば cpu であればコアごとまたはトータルを示すディメンジョンが、disk なら、デバイス・ファイルシステム・マウントポイント、などがディメンジョンになります。

要するに、CPU はコアごとのメトリクスまたはトータルのメトリクスが、ディスクならデバイス&ファイルシステム&マウントポイントごとのメトリクスが記録される、ということです。

メトリクス(のセクション)ごとに詳細な設定が出来ることもあります。例えば disk の場合、対象となるマウントポイント、除外するファイルシステム、デバイスをディメジョンに含めるか、などが指定できます。

{
  "metrics": {
    "metrics_collected": {
      "disk": {
        "resources": [
            "/"
        ],
        "measurement": [
          "disk_used_percent"
        ],
        "ignore_file_system_types": [
          "rootfs",
          "tmpfs",
          "devtmpfs"
        ],
        "drop_device": true
      }
    }
  }
}

append_dimensions

前述のディメンジョンとは別に amazon-cloudwatch-agent.jsonmetrics.append_dimensions で追加のディメンジョンが指定できます。ただし、下記の4つのいずれかしか指定できないようです。

{
  "metrics": {
    "append_dimensions": {
      "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
      "ImageId": "${aws:ImageId}",
      "InstanceId": "${aws:InstanceId}",
      "InstanceType": "${aws:InstanceType}"
    }
  }
}

キーと値のセットはこれに一致する必要があります(値が存在する意味がないような・・謎い)。

指定した項目がメトリクスのディメンジョンに追加されます。全部指定すればひとつのメトリクスにこれら4つのディメンジョンがすべて追加されます。それぞれのディメンジョンを持つ別のメトリクスになるというわけではありません。

aggregation_dimensions

aggregation_dimensions でメトリクスを集約するディメンジョンを指定できます。例えば AutoScalingGroupName で集約すれば AutoScalingGroupName のみをディメンジョンに持つメトリクスを記録できます。要するに AutoScalingGroupName ごとに集約したメトリクスになります。

AutoScaling の条件として使うなら AutoScalingGroupName で集約する必要がありますね。

ログ

メトリクスだけではなく、ログファイルを監視して CloudWatch Logs に送ることもできます。

さいごに

セットアップも簡単なのでリソース監視に CloudWatch をメインに据えるならとりあえず使っとけば良いと思います。


CloudWatch Agent とは直接関係無いですが・・・複数のディメンジョンを持つメトリクスに対して一部のディメンジョンでのみ集計した結果に対して CloudWatch Alarm を設定したりはできないんですね。

例えば AutoScalingGroupName と InstanceId の2つのディメンジョンを持つメトリクスに対して、AutoScalingGroupName を条件に複数の InstanceId を合計したメトリクスに対して CloudWatch Alarm を設定することができません。

Metric Math で近いことならできそうですが・・あらかじめ集計する複数のメトリクスをリストしておく必要があるので AutoScaling でインスタンスが増えたり減ったりする状況ではダメそうです。

ので、やりたければ AutoScalingGroupName だけをディメンジョンに持つメトリクスを別に記録する必要があります。

AWS/EC2 名前空間のメトリクスでは、同じ CPUUtilization でも InstanceId ごとや AutoScalingGroupName ごとのメトリクスに CloudWatch Alarm が設定できたので、複数のメトリクスの集計に対して CloudWatch Alarm を設定することができるような気がしていたのですが、単に同じ CPUUtilization で InstanceId のみをディメンジョンに持つメトリクスと AutoScalingGroupName をディメンジョンに持つメトリクスが別々に記録されているだけでした。

Prometheus なら rate(node_cpu{mode="system",group="mygroup"}[5m]) とかでサッとできるので、CloudWatch Alarm のちょっと使いにくいなーと感じるところです。