githubリポジトリ:"https://github.com/shu130/terraform-study23"
- EC2 オートスケーリンググループでApacheを導入し、CloudWatchエージェントを利用してアクセスログとエラーログを CloudWatch Logs に送信する環境を構築
- ASG (Auto Scaling Group) を使用して EC2 インスタンスをスケーリングし、Web サーバの冗長性を確保
- SSMパラメータストア を活用してCloudWatch エージェントの設定を共有、複数インスタンスへの一貫した設定配布
.
├── provider.tf
├── vpc.tf
├── iam.tf
├── autoscaling.tf
├── cloudwatch.tf
├── ssm.tf
├── scripts
│ └── fetch_cloudwatch_config.sh
├── variables.tf
├── terraform.tfvars
└── outputs.tf
- ASG用IAM ロールを作成
- CloudWatch エージェントがCloudWatch Logs にログ送信できるように、CloudWatchAgentServerPolicy ポリシーを IAM ロールにアタッチ
- EC2 インスタンスが SSM パラメータストアから設定情報を取得できるように、AmazonSSMReadOnlyAccess ポリシーもIAM ロールにアタッチ
- 作成した IAM ロールを IAM インスタンスプロファイル として定義し、Auto Scaling Group (ASG) のインスタンスが起動時にこの IAM ロールを使用
# ./iam.tf
# IAMロールを作成
resource "aws_iam_role" "cloudwatch_agent_role" {
name = "CloudWatchAgentRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})
}
# IAMロールに2つのポリシーをアタッチ:
## 1.cloudwatch-Logsへのログ送信用
resource "aws_iam_role_policy_attachment" "cloudwatch_logs" {
role = aws_iam_role.cloudwatch_agent_role.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
## 2. SSMパラメータストア接続用
resource "aws_iam_role_policy_attachment" "ssm_access" {
role = aws_iam_role.cloudwatch_agent_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess"
}
# IAMロールをインスタンスプロファイルとして定義
resource "aws_iam_instance_profile" "cloudwatch_instance_profile" {
name = "CloudWatchInstanceProfile"
role = aws_iam_role.cloudwatch_agent_role.name
}- IAM インスタンスプロファイルを通じて、ASG 内のすべての EC2 インスタンスが一貫して CloudWatch Logs へのアクセスと、SSM パラメータストアから設定を取得
- EC2 インスタンスのスケールイン/スケールアウトを自動化
user_dataスクリプトによりASG でインスタンスが起動する際に、Apache をインストールし、CloudWatch エージェントの設定ファイルを取得して、CloudWatch Logs にログを送信
#./autoscaling.tf
# 起動テンプレート作成
resource "aws_launch_template" "web_launch_template" {
name_prefix = "${var.ec2_instance_name}-lt-"
image_id = var.ec2_ami
instance_type = var.ec2_instance_type
key_name = var.ec2_key_pair
# インスタンスプロファイルを適用
iam_instance_profile {
name = aws_iam_instance_profile.cloudwatch_instance_profile.name
}
network_interfaces {
associate_public_ip_address = true
security_groups = [aws_security_group.asg_sg.id]
}
# user_dataスクリプトを指定
user_data = base64encode(templatefile("${path.module}/scripts/fetch_cloudwatch_config.sh", {}))
tag_specifications {
resource_type = "instance"
tags = {
Name = "${var.ec2_instance_name}"
}
}
}
# オートスケール設定
resource "aws_autoscaling_group" "web_asg" {
desired_capacity = 2
max_size = 4
min_size = 2
vpc_zone_identifier = aws_subnet.public_subnets[*].id
launch_template {
id = aws_launch_template.web_launch_template.id
version = "$Latest"
}
tag {
key = "Name"
value = "${var.ec2_instance_name}"
propagate_at_launch = true
}
}- 初回起動時に自動実行
- インスタンス起動時にApacheとCloudWatchエージェントをインストール
- SSMパラメータストアからCloudWatchエージェントの設定を取得
- 動的にインスタンスIDを取得して設定に反映
# ./scripts/fetch_cloudwatch_config.sh
#!/bin/bash
# Apacheのインストールと起動
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
# CloudWatchエージェントのインストール
yum install -y amazon-cloudwatch-agent
# jqのインストール
yum install -y jq
# SSMパラメータストアからCloudWatch設定情報を取得し、エージェント起動
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
aws ssm get-parameter --name "AmazonCloudWatch-ForwardProxy" --query "Parameter.Value" --output text --region "us-west-2" | \
jq --arg id "$INSTANCE_ID" '.logs.logs_collected.files.collect_list[] |= (.log_stream_name = $id)' | \
sudo tee /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json > /dev/null
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.jsonaws_ssm_parameterリソースで、CloudWatch エージェントの設定情報を保存- EC2インスタンスの起動スクリプト (
fetch_cloudwatch_config.sh)によって、
SSMから設定を取得し、jqでプレースホルダ{instance_id}を実際のインスタンスIDに置き換え
# CloudWatchエージェントの設定をSSMパラメータストアへ保存
resource "aws_ssm_parameter" "cloudwatch_config" {
name = "AmazonCloudWatch-ForwardProxy"
type = "String"
value = jsonencode({
logs = {
logs_collected = {
files = {
collect_list = [
{
file_path = "/var/log/messages"
log_group_name = "var-log-messages"
log_stream_name = "{instance_id}"
timestamp_format = "%b %d %H:%M:%S"
},
{
file_path = "/var/log/httpd/access_log"
log_group_name = "HttpAccessLog"
log_stream_name = "{instance_id}"
timestamp_format = "%b %d %H:%M:%S"
},
{
file_path = "/var/log/httpd/error_log"
log_group_name = "HttpErrorLog"
log_stream_name = "{instance_id}"
timestamp_format = "%b %d %H:%M:%S"
}
]
}
}
}
})
}- CloudWatch エージェントは、SSM から取得した設定に基づいてログを収集し、アクセスログとエラーログをCloudWatchロググループに送信
- ASG のインスタンスごとに異なるログストリーム名を使用して、ログを管理
# ./cloudwatch.tf
# CloudWatchエージェントのログ送信先:
## 1.アクセスロググループ
resource "aws_cloudwatch_log_group" "http_access_log" {
name = "HttpAccessLog"
retention_in_days = 7
}
## 2. エラーロググループ
resource "aws_cloudwatch_log_group" "http_error_log" {
name = "HttpErrorLog"
retention_in_days = 7
}tail -f /var/log/httpd/access_log- 実際にアクセスしたときに、新しいログが生成される
(http://<EC2パブリックIP> でアクセス)
tail -f /var/log/httpd/error_log- エラー時にログが出力されている
(http://<EC2パブリックIP/nonexistpage> でアクセス)
- AWSコンソールにアクセス
- 「CloudWatch」→「ロググループ」で、
var-log-messages、HttpAccessLog、HttpErrorLogの3つのロググループがある - 3つのグループでそれぞれのログストリームが作成されておりログ表示できた
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status- エージェントが正常に稼働
cat /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json- 設定ファイルが正しく取得されており、インスタンスIDが設定されている
cat /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log- エージェントの起動状況やエラーの詳細が出力
今回はここまでにしたいと思います。
