Beanstalk上のSpringBootアプリケーションのヒープ領域をCloudWatch Metricsで監視する

# 概要

Elastic Beanstalk上で起動しているSpringBootアプリケーションのヒープ領域を監視するために
ActuatorのMetricsで取得して、CloudWatch Metricsに投げます

# 実装

Elastci Beanstalkの標準機能であるebextensionsを使用し
Beanstalk環境を構成するインスタンスにcronとshellスクリプトを配置します

## shell script

以下のようなshell scriptを作成しました

```shell:/put-actuator-metrics.sh
#!/bin/bash      
get_actuator_metrics() {
    targetMetric=$1
    instanceId=$2
    autoScalingGroupName=$3
    baseUnit=$4      
    actuatorResponse=$(curl http://localhost:8080/metrics/$targetMetric)
    metricValue=$(echo $actuatorResponse | jq '.measurements[].value')
    
    # put autoscaling group dimensions
    aws cloudwatch put-metric-data --region ap-northeast-1 \
        --namespace System/Linux \
        --metric-name $targetMetric \
        --dimensions AutoScalingGroupName=$autoScalingGroupName \
        --value $metricValue \
        --unit $baseUnit
}

# metadataからInstanceIdを取得
instanceId=$(curl http://169.254.169.254/latest/meta-data/instance-id)
# AWS CLIでInstanceIdを元にAutoscaling Group名を取得
autoScalingGroupName=$(\
    aws autoscaling describe-auto-scaling-instances \
    --instance-ids $instanceId --region ap-northeast-1 --output json | \
    jq -r '.AutoScalingInstances[].AutoScalingGroupName'
)

# put jvm.memory.max
get_actuator_metrics jvm.memory.max $instanceId $autoScalingGroupName Bytes
# put jvm.memory.used
get_actuator_metrics jvm.memory.used $instanceId $autoScalingGroupName Bytes
```

actuatorからmetricsを取得している以下の箇所は
SpringBootアプリケーションのserver portに合わせて編集してください

```shell
actuatorResponse=$(curl http://localhost:8080/metrics/$targetMetric)
```

また各AWS CLIの引数としているリージョンも環境に合わせて編集してください

```shell
aws autoscaling describe-auto-scaling-instances \
--instance-ids $instanceId --region ap-northeast-1 --output json | \
```

## IAM Role

AutoScaling Group名を取得するためにBeanstalkにアタッチしているIAMロールには
AWS AutoScalingの読み取り権限を付与してください

## ebextensions

作成したshell scriptを配置するためのebextensionsを作成します

```yaml:.ebextensions/monitoring-jvm-memory.config
packages:
  yum:
    jq: []

files:
  "/etc/cron.d/actuatormetrics":
    mode: "000644"
    owner: root
    group: root
    content: |
      * * * * * root sh /usr/local/bin/put-actuator-metrics.sh >> /var/log/put_actuator_metrics.log 2>&1

  "/usr/local/bin/put-actuator-metrics.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash      
      get_actuator_metrics() {
          targetMetric=$1
          instanceId=$2
          autoScalingGroupName=$3
          baseUnit=$4      
          actuatorResponse=$(curl http://localhost:8080/metrics/$targetMetric)
          metricValue=$(echo $actuatorResponse | jq '.measurements[].value')
          # put autoscaling group dimensions
          aws cloudwatch put-metric-data --region ap-northeast-1 \
              --namespace System/Linux \
              --metric-name $targetMetric \
              --dimensions AutoScalingGroupName=$autoScalingGroupName \
              --value $metricValue \
              --unit $baseUnit
      }
      instanceId=$(curl http://169.254.169.254/latest/meta-data/instance-id)
      autoScalingGroupName=$(\
          aws autoscaling describe-auto-scaling-instances \
          --instance-ids $instanceId --region ap-northeast-1 --output json | \
          jq -r '.AutoScalingInstances[].AutoScalingGroupName'
      )
      # put jvm.memory.max
      get_actuator_metrics jvm.memory.max $instanceId $autoScalingGroupName Bytes
      # put jvm.memory.used
      get_actuator_metrics jvm.memory.used $instanceId $autoScalingGroupName Bytes

container_commands:
  rm_old_cron:
    command: "rm -f /etc/cron.d/actuatormetrics.bak"
```

これを追加して `eb deploy` します

## Beanstalkのモニタリング画面を確認

AutoScalingGroupを対象にグラフの追加を行うと
選択できるメトリクスに `jvm.memory.used` などが追加されているはずです
（反映には時間がかかるときがあります）

以下のようになるはず
![スクリーンショット 2019-06-05 15.02.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262765/bbe07be4-31e5-db99-3ea7-32c050b70625.png)


# 課題
AutoScalingGroupでまとめてCloudWatch MetricsにPUTしているので
複数台で分散しているBeanstalk環境では、AutoScalingGroup配下の各EC2インスタンスのメトリクスが混ざってしまいます
（負荷の高いインスタンスとそうでないインスタンスが区別できない）

ですがNameSpaceを `System/Linux` dimensionsを `AutoScalingGroupName` としたとき以外で
Beanstalkの管理画面上に表示させることに成功していません

# 参考

- Elastic Beanstalk 環境で EC2インスタンスによる Cron ジョブの作成
  - https://aws.amazon.com/jp/premiumsupport/knowledge-center/cron-job-elastic-beanstalk/
- Spring Boot Actuator 2のエンドポイントを叩いてみる
  - https://xn--fhqq99a3lb.jp/374#toc16