Packer를 사용하여 Cloudwatch agent를 갖는 EC2 AMI 빌드하기 (AWS 모니터링 시리즈)

Packer를 사용하여 Cloudwatch agent를 가지는 AMI를 빌드하고 코드로 관리하기

June Lim
onthelook
12 min readNov 23, 2023

--

문제점

클라우드와치 에이전트를 설치를 ec2에서 할 수 있는건 아는데 매번 명령어를 입력해서 설치해주기가 번거롭고 귀찮다.

시행착오

AMI를 사용하여 이미지를 찍어내서 관리한다.

하지만 구성이 바뀌면 매번 인스턴스안에 접속해서 설정을 바꿔주기가 귀찮았다.

해결

packer를 사용해서 필요한 구성의 클라우드 와치 에이전트가 설치된 인스턴스 ami를 로컬에서 편리하게 만들어버리고 그 ami를 기준으로 인스턴스를 시작할 수 있도록 한다.

디렉토리 구조

otl-IaC/cloudwatch-agent-graviton-ami 디렉토리안에 필요한 구성들을 만들었습니다.

그리고 그 안에

  • variables.auto.pkrvars.hcl
  • aws-amazonlinux.pkr.hcl
  • variables.pkr.hcl
  • scripts

그리고 scripts 디렉토리 안에는 ec2 클라우드 와치 셋업을 위한 쉘스크립트 파일을 만들었습니다.

  • setup-cloudwatch.sh

aws-amazonlinux.pkr.hcl

packer {
required_plugins {
amazon = {
version = ">= 0.0.1"
source = "github.com/hashicorp/amazon"
}
}
}

locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}


source "amazon-ebs" "amazonlinux" {
ami_name = "${var.ami_prefix}-${local.timestamp}"
instance_type = "t4g.micro"
region = "ap-northeast-2"
source_ami = "ami-01b2d7e6a01b97691"
ssh_username = "ec2-user"

tags = {
Name = "${var.ami_prefix}-${local.timestamp}"
BuiltOn = "{{isotime}}"
}
}



build {
sources = [
"source.amazon-ebs.amazonlinux"
]

provisioner "shell" {
script = "scripts/setup-cloudwatch.sh"
environment_vars = [
"AWS_ACCESS_KEY=${var.aws_access_key}",
"AWS_SECRET_KEY=${var.aws_secret_key}",
"AWS_REGION=${var.aws_region}"
]
}
}

variable "ami_prefix" {
type = string
default = "amazonlinux-cloudwatch-agent-graviton--ami"
}

기본 ami 뼈대입니다.

특이사항은 provisioner를 사용해서 클라우드 와치 스크립트를 실행 후 셋업을 하는부분을 넣어줬습니다.

그리고 ami_prefix 부분에서 default부분에서 저장되는 ami이름을 설정 가능한데 너무 길면 식별이 콘솔에서 힘든것 같긴합니다.

해당 packer로 실행 후 처음에는 위에 선언된대로 t4g.micro를 사용해서 ami를 찍어내지만 실제 실행은 g 시리즈 어느것이든 상관없습니다.

variables.pkr.hcl

variable "aws_access_key" {
type = string
default = ""
}

variable "aws_secret_key" {
type = string
default = ""
}

variable "aws_region" {
type = string
default = ""
}

variables.auto.pkrvars.hcl

aws_access_key = ""
aws_secret_key = ""
aws_region = ""

실제 aws의 크레덴셜은 variables.pkr.hcl이 아닌 해당 파일에서 넣고 관리해주는게 플렉서블 합니다. (env파일처럼 사용)

setup-cloudwatch.sh

#!/bin/bash

# AWS CLI 설치
sudo yum update -y
sudo yum install -y python3 python3-pip

# AWS CLI 설치
sudo pip3 install awscli --upgrade --user

# AWS Credential 설정
mkdir -p /home/ec2-user/.aws
cat > /home/ec2-user/.aws/credentials <<EOL
[default]
aws_access_key_id = ${AWS_ACCESS_KEY}
aws_secret_access_key = ${AWS_SECRET_KEY}
EOL

cat > /home/ec2-user/.aws/config <<EOL
[default]
region = ${AWS_REGION}
EOL

# 설정 파일의 소유권 변경
sudo chown -R ec2-user:ec2-user /home/ec2-user/.aws

echo "AWS_ACCESS_KEY: $AWS_ACCESS_KEY"
echo "AWS_SECRET_KEY: $AWS_SECRET_KEY"
echo "AWS_REGION: $AWS_REGION"

# aws-amazonlinux-cloudwatch-agent다운
wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/arm64/latest/amazon-cloudwatch-agent.rpm
sudo yum install amazon-cloudwatch-agent.rpm -y

sudo systemctl enable amazon-cloudwatch-agent

# Create the CloudWatch agent config file directory if not exists
sudo mkdir -p /opt/aws/amazon-cloudwatch-agent/bin/


# Create the CloudWatch agent config file
sudo bash -c 'cat << EOF > /opt/aws/amazon-cloudwatch-agent/bin/config.json
{
"agent": {
"metrics_collection_interval": 60,
"logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
},
"metrics": {
"append_dimensions": {
"InstanceId": "\${aws:InstanceId}"
},
"metrics_collected": {
"cpu": {
"measurement": [
"cpu_usage_idle",
"cpu_usage_iowait",
"cpu_usage_user",
"cpu_usage_system"
],
"resources": [
"*"
],
"totalcpu": false
},
"mem": {
"measurement": [
"mem_used_percent"
]
},
"netstat": {
"measurement": [
"tcp_established",
"tcp_syn_sent",
"tcp_close_wait"
]
}
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/messages",
"log_group_name": "{instance_id}/instance",
"log_stream_name": "{instance_id}/stdin_message",
"timezone": "Local"
},
{
"file_path": "/var/log/secure",
"log_group_name": "{instance_id}/instance",
"log_stream_name": "{instance_id}/secure",
"timezone": "Local"
},
{
"file_path": "/var/log/boot.log",
"log_group_name": "{instance_id}/instance",
"log_stream_name": "{instance_id}/bootLog",
"timezone": "Local"
}
]
}
}
}
}
EOF'


sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s

위 스크립트들을 사용하여 프로젝트 디렉토리를 구성하고 packer 빌드하면됩니다.

그리고 ami를 설정하면 cloudwatch에서 {인스턴스 id}/instance 이렇게 로그 그룹을 확인하여 로그를 확인 할 수 있게 됩니다.

그 전 블로깅 내용을 보셨다면 충분히 json 파일을 커스텀하셔서 용도에 맞게 사용하실 수 있으실거라 생각합니다.

READme가 혹시 궁금하시다면 내용은 밑과 같습니다.

# CloudWatch Agent on Graviton Amazon Linux 프로젝트

이 프로젝트는 Amazon Linux OS에서 Graviton인스턴스들을 기반으로 AWS CLI와 CloudWatch Agent를 설치한 AMI(Amazon Machine Image)를 자동으로 생성합니다.


## Prerequisites
- Packer 설치되어 있어야 함 (packer version으로 확인)
- AWS CLI 설치 및 구성되어 있어야 함 (aws --version으로 확인)
- AMI를 생성하고 관리하기 위한 필요한 IAM 권한을 갖춘 AWS 계정
- CloudWatch Logs에 로그를 전송할 수 있는 IAM 역할


## AMI 빌드하기

AMI를 생성하려면 사용할 프로젝트의 디렉토리로 이동한 다음 다음 명령을 실행하세요:

packer build .


## 빌드 후 단계

AMI를 빌드한 후, 생성된 AMI를 사용하여 인스턴스를 시작할 수 있습니다. 인스턴스에서는 AWS CLI와 CloudWatch Agent가 설치되어 있으며, CloudWatch Logs로 로그를 전송할 수 있습니다.

CloudWatch Agent를 사용하여 로그 및 메트릭 데이터를 수집하려면 CloudWatch Logs에 로그 그룹 및 로그 스트림을 생성해야 합니다. 또한, CloudWatch Logs에 로그를 전송할 수 있는 IAM 역할을 생성하여 인스턴스에 할당해야 합니다. 이 역할에는 cloudwatch:PutLogEvents 및 필요한 다른 권한이 포함되어야 합니다.

아래는 예시 IAM 정책입니다. 필요에 따라 이 정책을 수정하여 적절한 권한을 설정하세요.
```bash
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudWatchLogsAccess",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:YOUR_REGION:YOUR_ACCOUNT_ID:log-group:YOUR_LOG_GROUP_NAME"
]
}
]
}

```

### AMI를 시작한 후에는 CloudWatch Agent를 구성하고, 필요한 로그 그룹 및 로그 스트림을 생성하여 로그를 전송할 수 있습니다. CloudWatch Agent의 설정 파일을 수정하여 수집하려는 로그 및 메트릭을 구성할 수 있습니다.

처음 packer를 접하시는분도 위를 참고하셔서 핸즈온처럼 하실 수 있을것이라 생각합니다.

이렇게 ami를 사용하여 인스턴스를 시작하는 방법으로 시작하여, 더 자유롭게 커스텀하여 사용하는 packer로 ami 구축하기까지를 함께하셨습니다.

현재 사내에서는 모니터링이 필요한 인스턴스들의 경우, 해당 ami로 시작하여 처음부터 클라우드 와치 에이전트가 설치되어있으니 편리하게 사용중입니다.

--

--