【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
- 2025 Japan AWS Top Engineers
- 2025 Japan All AWS Certifications Engineers
2023年度新卒入社。出身は韓国です。
好きな AWS サービス:AWS Control Tower
好きなこと:ピアノ、自転車
Recommends
こちらもおすすめ
-
こんにちは Okta
2024.2.13
-
atop コマンドを用いて Amazon EC2 の障害原因を切り分けよう!
2024.5.30
-
Okta導入物語(2)~テンプレートとSAML~
2024.4.1
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28

AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16