【Amazon CloudWatch】インスタンスの起動時にログ収集を自動化する ※Terraformコード付き
こんにちは。kisaragi です。
この記事では、Systems Manager Parameter StoreとCloudWatchAgentの連携について解説していきます。
この記事、何がうれしい?
メモリ使用率など、CloudWatchだけでは監視が難しいものでもCloudWatch Agentのカスタムメトリクスで監視ができるようになります。
しかし、インスタンスを作成する度にCloudWatchAgentのカスタムメトリクスを設定することは大変だな…と思ったこと、ございませんか。
この記事では、インスタンスごとにカスタムメトリクスの設定手動で行わずとも起動時に設定可能になります!
前提 / 環境
- Terraform 1.7.2
- VPC等の前提環境
- EC2のIAM Roleには以下のポリシーが付与されていること
- “AmazonSSMManagedInstanceCore”
- “CloudWatchAgentServerPolicy”
構成図
この記事のゴール
EC2 インスタンスの起動後、CloudWatchからメモリ監視ができていることを確認します。
Systems Manager Parameter Storeの設定
先ず、CloudWatchで監視したいテンプレートをParameter Storeに収納しておく必要があります。
CloudWatchAgent 設定ファイルの作成方法についてはこちらをご覧ください。
CloudWatch エージェント設定ファイルを手動で作成または編集する
1. AWSコンソール画面からSystems Managerへ移動します。
2. パラメータストアタブからパラメータを作成します。
3. 画像のようにパラメータ名と値を入力し、作成します。
注:パラメータ名は“AmazonCloudWatch-“で始まる必要があります。
サンプル CloudWatchAgent 設定ファイル
パラメータの値にはCloudWatchAgentの設定を入力しておく必要がございます。
今回はメモリ・ディスクなどの監視ができる下記のサンプルコードで解説していきます。
Linux サンプル
{ "agent": { "metrics_collection_interval": 60, "run_as_user": "cwagent" }, "metrics": { "aggregation_dimensions": [ [ "InstanceId" ] ], "append_dimensions": { "AutoScalingGroupName": "${aws:AutoScalingGroupName}", "ImageId": "${aws:ImageId}", "InstanceId": "${aws:InstanceId}", "InstanceType": "${aws:InstanceType}" }, "metrics_collected": { "collectd": { "metrics_aggregation_interval": 60 }, "disk": { "measurement": [ "used_percent" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "mem": { "measurement": [ "mem_used_percent" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" } } } }
Windows サンプル
{ "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "C:\\Program Files\\Update Services\\LogFiles\\Change.log", "log_group_class": "STANDARD", "log_group_name": "Change.log", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "C:\\Program Files\\Update Services\\LogFiles\\SoftwareDistribution.log", "log_group_class": "STANDARD", "log_group_name": "SoftwareDistribution.log", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "C:\\Windows\\System32\\LogFiles\\HTTPERR\\httperr.log", "log_group_class": "STANDARD", "log_group_name": "httperr.log", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "C:\\Windows\\WID\\Log\\error.log", "log_group_class": "STANDARD", "log_group_name": "error.log", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "", "log_group_class": "STANDARD", "log_group_name": ".", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "", "log_group_class": "STANDARD", "log_group_name": ".", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "file_path": "", "log_group_class": "STANDARD", "log_group_name": ".", "log_stream_name": "{instance_id}", "retention_in_days": -1 } ] }, "windows_events": { "collect_list": [ { "event_format": "xml", "event_levels": [ "VERBOSE", "INFORMATION", "WARNING", "ERROR", "CRITICAL" ], "event_name": "System", "log_group_class": "STANDARD", "log_group_name": "System", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "event_format": "xml", "event_levels": [ "VERBOSE", "INFORMATION", "WARNING", "ERROR", "CRITICAL" ], "event_name": "Application", "log_group_class": "STANDARD", "log_group_name": "Application", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "event_format": "xml", "event_levels": [ "VERBOSE", "INFORMATION", "WARNING", "ERROR", "CRITICAL" ], "event_name": "Security", "log_group_class": "STANDARD", "log_group_name": "Security", "log_stream_name": "{instance_id}", "retention_in_days": -1 }, { "event_format": "xml", "event_levels": [ "VERBOSE", "INFORMATION", "WARNING", "ERROR", "CRITICAL" ], "event_name": "System", "log_group_class": "STANDARD", "log_group_name": "System", "log_stream_name": "{instance_id}", "retention_in_days": -1 } ] } } }, "metrics": { "aggregation_dimensions": [ [ "InstanceId" ] ], "append_dimensions": { "AutoScalingGroupName": "${aws:AutoScalingGroupName}", "ImageId": "${aws:ImageId}", "InstanceId": "${aws:InstanceId}", "InstanceType": "${aws:InstanceType}" }, "metrics_collected": { "LogicalDisk": { "measurement": [ "% Free Space" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "Memory": { "measurement": [ "% Committed Bytes In Use" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" } } } }
Terraform サンプルコード
注:locals変数等は環境に合わせて変更する必要がございます
main.tf
#-------------------------------------- # VPC #-------------------------------------- resource "aws_vpc" "vpc" { cidr_block = "10.0.0.0/16" instance_tenancy = "default" enable_dns_support = true enable_dns_hostnames = true assign_generated_ipv6_cidr_block = false tags = { Name = "kisaragi-vpc" } } #-------------------------------------- # Subnet #-------------------------------------- resource "aws_subnet" "public_subnet_1a" { vpc_id = aws_vpc.vpc.id availability_zone = "ap-northeast-1a" cidr_block = "10.0.1.0/24" map_public_ip_on_launch = true tags = { Name = "kisaragi-public-subnet-1a" type = "public" } } #-------------------------------------- # Route table #-------------------------------------- resource "aws_route_table" "public_rt" { vpc_id = aws_vpc.vpc.id tags = { Name = "kisaragi-public-rt" type = "public" } } resource "aws_route_table_association" "public_rt_1a" { route_table_id = aws_route_table.public_rt.id subnet_id = aws_subnet.public_subnet_1a.id } #-------------------------------------- # Internet Gateway #-------------------------------------- resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.vpc.id tags = { Name = "kisaragi-igw" } } resource "aws_route" "public_rt_igw" { route_table_id = aws_route_table.public_rt.id gateway_id = aws_internet_gateway.igw.id destination_cidr_block = "0.0.0.0/0" } #-------------------------------------- # IAM Role #-------------------------------------- locals { policy = { AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" CloudWatchAgentServerPolicy = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" } } resource "aws_iam_instance_profile" "ec2_profile" { name = aws_iam_role.ec2_iam_role.name role = aws_iam_role.ec2_iam_role.name } resource "aws_iam_role" "ec2_iam_role" { name = "kisaragi-ec2-iam-role" assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json } resource "aws_iam_role_policy_attachment" "ec2_iam_role_policy" { for_each = local.policy role = aws_iam_role.ec2_iam_role.name policy_arn = each.value } data "aws_iam_policy_document" "ec2_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } } } #-------------------------------------- # Security Group #-------------------------------------- resource "aws_security_group" "sg-ec2" { name = "kisaragi-sg-ec2" description = "instance role security group" vpc_id = aws_vpc.vpc.id tags = { Name = "kisaragi-sg-ec2" } } resource "aws_security_group_rule" "ec2_ingress_http" { security_group_id = aws_security_group.sg-ec2.id type = "ingress" protocol = "tcp" from_port = 80 to_port = 80 cidr_blocks = ["0.0.0.0/0"] } resource "aws_security_group_rule" "ec2_ingress_https" { security_group_id = aws_security_group.sg-ec2.id type = "ingress" protocol = "tcp" from_port = 443 to_port = 443 cidr_blocks = ["0.0.0.0/0"] } resource "aws_security_group_rule" "ec2_egress" { security_group_id = aws_security_group.sg-ec2.id type = "egress" protocol = "-1" from_port = 0 to_port = 0 cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] }
ec2_linux.tf
locals { linux_ami = "ami-0253ce315ad0c9655" # Amazon Linux 2 linux_parameter = "AmazonCloudWatch-kisaragi-linux" # Parameter Storeで作成したパラメータ名 } resource "aws_instance" "linux" { ami = local.linux_ami instance_type = "t2.micro" subnet_id = aws_subnet.public_subnet_1a.id iam_instance_profile = aws_iam_instance_profile.ec2_profile.name associate_public_ip_address = true vpc_security_group_ids = [aws_security_group.sg-ec2.id] user_data = <<EOF #!/bin/bash amazon-linux-extras install -y collectd systemctl enable collectd.service systemctl start collectd.service wget https://amazoncloudwatch-agent.s3.amazonaws.com/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm rpm -U ./amazon-cloudwatch-agent.rpm /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:${local.linux_parameter} EOF lifecycle { ignore_changes = [user_data] } tags = { Name = "kisaragi-linux" } }
ec2_windows.tf
locals { windows_ami = "ami-00b340f8e22b8bc29" # Microsoft Windows Server 2019 windows_parameter = "AmazonCloudWatch-kisaragi-windows" # Parameter Storeで作成したパラメータ名 } resource "aws_instance" "windows" { ami = local.windows_ami instance_type = "t2.micro" subnet_id = aws_subnet.public_subnet_1a.id iam_instance_profile = aws_iam_instance_profile.ec2_profile.name associate_public_ip_address = true vpc_security_group_ids = [aws_security_group.sg-ec2.id] user_data = <<EOF <powershell> Invoke-WebRequest -Uri https://amazoncloudwatch-agent.s3.amazonaws.com/windows/amd64/latest/amazon-cloudwatch-agent.msi -OutFile amazon-cloudwatch-agent.msi msiexec /i amazon-cloudwatch-agent.msi Start-Sleep -s 60 & "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c ssm:${local.windows_parameter} </powershell> EOF lifecycle { ignore_changes = [user_data] } tags = { Name = "kisaragi-windows" } }
結果
1. AWSコンソール画面からCloudWatchへ移動します。
2. すべてのメトリクスタブからCWAgent項目を選択します。
3. 対象インスタンスからメモリ使用率が監視できていることを確認します。
参考資料
CloudWatch エージェント設定ファイルを手動で作成または編集する
エージェント設定を使用した EC2 インスタンスへの CloudWatch エージェントのインストール
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitter - 2024 Japan AWS Jr. Champions
2023年度新卒入社。出身は韓国です。
Recommends
こちらもおすすめ
-
Oktaがあるとすごく助かる人たちってどんな人たち?
2024.2.29
-
こんにちは Okta
2024.2.13
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28
AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16