【Amazon CloudWatch】インスタンスの起動時にログ収集を自動化する ※Terraformコード付き

AWS

2024.3.30

Topics

こんにちは。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 エージェントのインストール

kisaragi

2023年度新卒入社。クラウドエンジニア。出身は韓国です。 2月生まれの「如月」です。宜しくお願い致します。 尊敬するエンジニア:waka、Cold-Airflowさん

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら