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


概要

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

実装

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

shell script

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

/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に合わせて編集してください

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

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

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を作成します

.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 などが追加されているはずです
(反映には時間がかかるときがあります)

以下のようになるはず

課題

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

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

参考