新しいCloudWatch Agentを有効化したEC2オートスケール環境をCloudFormationで設定してみた

新しいCloudWatchエージェントを有効にしたオートスケール環境を AWS CloudFormationを利用して設定し、ログ退避とリソースの詳細監視を試みてみました。
2018.12.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

AWSチームのすずきです。

2017年12月にリリースされた新しいCloudWatch Agent、メモリやディスクの使用率、詳細なCPU使用などのカスタムメトリックとログファイルの収集を実現します。

今回、EC2オートスケールで起動したEC2インスタンスのリソース利用状況を確認するため、 新しいCloudWatch AgentをCloudFormationを利用して設定する機会がありましたので、紹介させて頂きます。

新しいCloudWatch Agentでメトリクスとログの収集が行なえます

CloudWatch エージェントにより収集されるメトリクス

構成

構成図

Role

  • Cloudwatch、CloudwatchLogs、SSMへのアクセス権限の付与のため、管理ポリシー「CloudWatchAgentServerPolicy」を利用します。
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

LaunchTemplate

  • AMI(OS)はAmazonLinux2の最新版を利用しました。
  • AmazonLinux1でも同じRPMパッケージ、設定コマンドが利用可能です。

  • EC2の詳細モニタリングを有効化、CPU使用率など標準メトリックの収集間隔を300→60秒にしました。

    Properties:
        Monitoring:
          Enabled: true
  • ユーザーデータを利用してEC2インスタンスの起動時に新しいCloudwatchエージェントをインストールします。
  • OSのメモリ不足判定の指標とするため小容量のスワップファイルを作成、スワップを有効化しました。
        UserData: !Base64
          Fn::Sub: |
            #!/bin/bash -xe
            if [ ! -f /swapfile ]; then dd if=/dev/zero of=/swapfile bs=1M count=256; chmod 600 /swapfile; mkswap /swapfile; swapon /swapfile; fi
            rpm -Uvh https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
            /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop
            /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:${CloudWatchAgentParameter} -s

SSM Parameter

  • 新しいCloudwatchエージェントの監視対象設定、SSMパラメータに反映します。
  • 管理ポリシー「CloudWatchAgentServerPolicy」で参照可能なSSMパラメータとする為、「Name」のプリフィックスを「AmazonCloudWatch-」としています。
  • ログ収集対象として、OSログとhttpdのエラーログを対象としました。
  • カスタムメトリックの収集は、メモリ使用率、スワップ使用率、プロセス数、CPU利用内訳、TCP接続数、ディスク使用率を設定しました。
  • 収集間隔、粒度が細かい事が望まれる監視項目は60秒、他は300秒としました。
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub 'AmazonCloudWatch-${AWS::StackName}-${Ec2InstanceTagName}-cloudwatch-agent-config'
      Type: String
      Value: !Sub |
        {
          "logs": {
            "logs_collected": {
              "files": {
                "collect_list": [
                  {
                    "file_path": "/var/log/messages",
                    "log_group_name": "${LogGroupMessages}"
                  },
                  {
                    "file_path": "/var/log/secure",
                    "log_group_name": "${LogGroupSecure}"
                  },
                  {
                    "file_path": "/var/log/cloud-init-output.log",
                    "log_group_name": "${LogGroupCloudinitoutput}"
                  },
                  {
                    "file_path": "/var/log/amazon/ssm/amazon-ssm-agent.log",
                    "log_group_name": "${LogGroupSsmAmzonssmagent}"
                  },
                  {
                    "file_path": "/var/log/httpd/error_log",
                    "log_group_name": "${LogGroupHttpderrorlog}"
                  }
                ]
              }
            }
          },
          "metrics": {
            "append_dimensions": {
              "AutoScalingGroupName": "${!aws:AutoScalingGroupName}",
              "ImageId": "${!aws:ImageId}",
              "InstanceId": "${!aws:InstanceId}",
              "InstanceType": "${!aws:InstanceType}"
            },
            "metrics_collected": {
              "mem": {
                "measurement": [
                  "mem_used_percent"
                ],
                "metrics_collection_interval": 60
              },
              "cpu": {
                "measurement": [
                  {"name": "cpu_usage_idle", "unit": "Percent"},
                  {"name": "cpu_usage_nice", "unit": "Percent"}
                ],
                "metrics_collection_interval": 60
              },
              "processes": {
                "measurement": [
                  "total"
                ],
                "metrics_collection_interval": 60
              },
              "netstat": {
                "measurement": [
                  "tcp_established"
                ],
                "metrics_collection_interval": 60
              },
              "disk": {
                "measurement": [
                  "used_percent"
                ],
                "resources": [
                  "/"
                ],
                "ignore_file_system_types": [
                  "xfs"
                ],
                "metrics_collection_interval": 300
              },
              "swap": {
                "measurement": [
                  "swap_used_percent"
                ],
                "metrics_collection_interval": 300
              }
            }
          }
        }

LogGroup

  • CloudFormationでロググループを事前作成は必須ではありませんが、CloudwatchLogsに退避したログが残り続ける事を避けるために設定を実施しています。
  LogGroupMessages:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/${AWS::StackName}/cloudwatch-agent/messages'
      RetentionInDays: !Ref 'LogGroupExpiredate'

動作例

ログ退避

ロググループ

ログストリーム

イベントの検索

  • 特定時間、特定文字列を含むログを確認する事が可能です。

カスタムメトリック

mem_used_percent

  • メモリ使用率40〜60%程度である事を確認できました。

swap_used_percent

  • IO発生がシステム影響を及ぼす事があるスワップ利用はない事が確認できました。

cpu_usage_idle

  • CPU負荷は発生していなかった事が確認できました。

disk used_percent

  • 1日のディスク使用率の増加、0.1%前後である事が確認できました。

tcp_established

processes total

まとめ

EC2で稼働させるワークロードのボトルネックとなる項目のカスタムメトリックを取得する事で、利用するインスタンスタイプの選定(スケールアップ、スケールダウン)や、 稼働台数(スケールアウト、スケールイン)の適正化に必要な情報を得る事ができます。

また、ログをCloudwatchLogsに退避する事で、オートスケールの増減により削除されたEC2インスタンスのログ確認や、複数のEC2インスタンスで発生したログ参照も容易になります。

Cloudwatchの利用費のみで利用できる新しいCloudWatch Agent、是非ご活用ください。

テンプレートサンプル

今回の検証、以下のCloudFormationテンプレートを利用して実施しました。