カスタムメトリクスを自前で取得してCloudWatchに送信してみた。

# 目次
[1.はじめに](#1-はじめに)
[2.やりたいこと](#2-やりたいこと)
[3.環境](#3-環境)
[4.結論](#4-結論)
[5.手順](#5-手順)
[6.おわりに](#6-おわりに)
[7.参考](#7-参考)

# 1. はじめに
こんにちは。
私はAWSの資格取得に向け日々勉強していますが、CloudWatchにカスタムメトリクスを自前で取得して送信する手法があることを学びましたので実装しました。

# 2. やりたいこと
カスタムメトリクスであるディスク使用率とメモリ使用率を取得してCloudWatchに送信するスクリプトを作成します。
さらに送信したカスタムメトリクスに対してCloudWatchアラームと連携させ監視する仕組みを構築します。

# 3. 環境
Amazon Linux2

# 4. 結論
* カスタムメトリクスを取得してCloudWatchに送信できる。
* 取得したカスタムメトリクスをCloudWatchアラームと連携できる。

# 5. 手順
## スクリプトを作成
ディスク使用率とメモリ使用率を取得し、カスタムメトリクスとしてCloudWatchに送信します。

``` putCustomMetrics.sh
#!/bin/bash

# ログ出力
log() {
  echo $1 | logger
}

# メトリクス送信
put_metrics(){
    # cron等でAPIリクエストが集中するのを防ぐためある程度wait
    sleep $(($RANDOM % 15))

    counter=0
    MAX_RETRY=3

    while :; do
    aws cloudwatch put-metric-data ${cw_opts}
    if [ $? -ne 0 ]; then
        if [ "${counter}" -ge "${MAX_RETRY}" ]; then
        log "failed to put metrics."
        exit 1
        fi
    else
        break
    fi

    counter=$((counter + 1))
    sleep 10
    done
}

# DimensionにインスタンスIDを指定する場合
# Dimensionには任意のKey-Valueを指定することが可能
instanceId=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

# インスタンス稼働リージョンの取得
region=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//')

# CloudWatchに登録する値(ルートデバイスのディスク使用率)をセット
diskusage=$(df  | sed -e 1d -e 's/%//' | awk  '$6 ~ /\057$/ {print $5}')

# ディスク使用率のカスタムメトリクスを作成
cw_opts="--namespace Example/EC2"
cw_opts=${cw_opts}" --metric-name DiskUsage"
cw_opts=${cw_opts}" --dimensions InstanceId=${instanceId}"
cw_opts=${cw_opts}" --unit Percent"
cw_opts=${cw_opts}" --region ${region}"
cw_opts=${cw_opts}" --value ${diskusage}"
# メトリクス送信
put_metrics

log "finish check_diskusage"

# CloudWatchに登録する値(メモリ使用率)をセット
# メモリ使用率（%）＝ ( ( total － available ) ÷ total ) × 100
total=$(free  | awk  '$1 ~ /^Mem:/ {print $2}')
available=$(free | awk  '$1 ~ /^Mem:/ {print $7}')
# 一旦( total － available )の値を格納
wk=$(($total-$available))
# wk ÷ totalの値(小数点第4位まで)を格納
wk=$(echo "scale=4; $wk / $total" | bc | xargs printf %.4f)
# 最後にメモリ使用率(小数点第2位まで)を格納
memoryusage=$(echo "scale=2; $wk * 100" | bc | xargs printf %.2f)

# メモリ使用率のカスタムメトリクスを作成
cw_opts="--namespace Example/EC2"
cw_opts=${cw_opts}" --metric-name MemoryUsage"
cw_opts=${cw_opts}" --dimensions InstanceId=${instanceId}"
cw_opts=${cw_opts}" --unit Percent"
cw_opts=${cw_opts}" --region ${region}"
cw_opts=${cw_opts}" --value ${memoryusage}"

# メトリクス送信
put_metrics

log "finish check_memoryusage"

exit 0
```
## 動作確認
事前にシェルスクリプトに実行権限の付与とAWS CLIの認証情報は設定済みです。
期待通りにメモリ使用率とディスク使用率のメトリクスが送信されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/6611cbcb-6838-f9f9-6143-057cd68f0507.png)

シェルスクリプトを1分間隔で実行するようにcronを設定します。
```
#分 時 日 月 曜日 コマンド
*/1 * * * * /home/ec2-user/putCustomMetrics.sh
```

次にCloudWatchアラームと連携して監視する仕組みを構築します。

## CloudWatchアラームと連携する
### 通知用のSNSを作成
Amazon SNSでトピックとサブスクリプションを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/3d0e3865-99d2-6d19-d6e5-3b1d9ac2a421.png)
### CloudWatchアラーム作成
現在のディスク使用率です。
```
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        474M     0  474M   0% /dev
tmpfs           483M     0  483M   0% /dev/shm
tmpfs           483M  404K  483M   1% /run
tmpfs           483M     0  483M   0% /sys/fs/cgroup
/dev/xvda1      8.0G  1.6G  6.5G  20% /
tmpfs            97M     0   97M   0% /run/user/1000
```
ルートデバイスの使用率が20%になっているので、CloudWatchアラームの閾値を15%にして閾値を超えたらメール送信するように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/d201481c-231c-5028-c6d0-c93e49296da9.png)

期待通りにアラートメールが送信されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/16e8f02a-3797-edbe-5ae5-a70d59822615.png)

メモリ使用率も同様に作成します。
現在のメモリ使用率です。
```
$ free
              total        used        free      shared  buff/cache   available
Mem:         988672      101088      349384         404      538200      745500
Swap:             0           0           0
```
メモリ使用率 = (total(988672) - available(745500)) / total(988672) * 100

およそ24%です。
CloudWatchアラームの閾値を20%にして閾値を超えたらメール送信するように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/d2a8582d-cebd-b6d7-49ad-9950632f2fe7.png)

メモリ使用率についても期待通りにアラートメールが送信されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1577269/23862ad0-4af0-27df-f0df-6a5a17e4e4c7.png)

# 6. おわりに
現在チームで保守している集計サーバーには監視機能を取り入れていないため、取り急ぎ今回実装したのを参考にして取り入れたいと思いました。詳細なメトリクスを取得する場合は、CloudWatchエージェントを導入する必要がありますが、自前でメトリクスを取得して監視できることを知っただけでも収穫です。

# 7. 参考
* [CloudWatchカスタムメトリクス取得用Bashスクリプト雛形](https://dev.classmethod.jp/articles/custommetrics-bashtemplate/)
* [メモリ使用率を取得する方法（Linux、freeコマンド）](https://qiita.com/yasushi-jp/items/9fa169251998b642aec9)