- Terraform으로 AMG 워크스페이스 + OpenSearch + ECS Telegraf 수집기 + S3 설정 동기화를 한 번에 구성, OpenSearch Serverless와 Managed 도메인 두 가지 옵션
- 적용 환경: AWS 서울 리전 (ap-northeast-2) / AMG Grafana 10.4 / OpenSearch Serverless 또는 Managed / ECS Fargate / 1차 검증 시점: 2024-11-21~28
- GitHub 저장소: https://github.com/20eung/aws-grafana
1. 이 구조의 장점 — 인프라 전체를 코드로
네트워크 장비 메트릭을 시각화할 때 Grafana + 시계열 데이터베이스 + 메트릭 수집기를 따로따로 설치하면 환경 일관성이 무너지기 쉽습니다. 노트북에서 테스트한 설정과 운영 환경이 다르고, 누군가 hand-configure한 부분이 운영 중에 갱신되지 않는 일이 생깁니다.
이 repo는 Terraform 한 번으로 다음을 모두 구성합니다.
| 구성 요소 | Terraform 자원 |
|---|---|
| 네트워크 | VPC, public/private 서브넷 × 2 AZ, NAT gateway × 2, IGW, route table |
| 컨테이너 registry | ECR (Telegraf 이미지 저장) |
| 시계열 데이터베이스 | OpenSearch Serverless (TIMESERIES collection) 또는 OpenSearch Managed domain |
| 데이터 액세스 정책 | OpenSearch Serverless access policy (collection-level) |
| 시각화 | AMG 워크스페이스 + SAML 인증 + API key rotation |
| 메트릭 수집 | ECS Fargate task (Telegraf container) + OpenSearch 적재 |
| 설정 동기화 | S3 bucket ↔ 컨테이너 entrypoint의 polling reload |
| 개발 환경 | Cloud9 IDE (선택사항) |
11개 .tf 파일 + Dockerfile + entrypoint script로 인프라 + 데이터 수집 + 설정 동기화를 모두 코드화합니다.
💼 이 글은 2026-07 시점의 AWS Managed Grafana / OpenSearch 기준입니다. OpenSearch Serverless는 Grafana 9.4+ 워크스페이스에서만 지원되므로 AMG 워크스페이스 생성 시 버전 확인이 필수입니다.
1차 검증 자료 시점은 GitHub 저장소 aws-grafana 의 마지막 commit 2024-11-28입니다. AWS 서비스는 리전 가용성, 옵션 (예: OpenSearch Serverless collection type, Managed domain 의 인스턴스 타입), IAM 권한 모델, Terraform provider 의 리소스 인자 등이 계속 변하므로 글 인용 화면과 현재 콘솔/provider 가 다를 수 있습니다. 이 글의 구성으로 재현할 때는 AWS 공식 User Guide + hashicorp/terraform-provider-aws 의 최신 릴리즈 노트를 다시 한번 확인하시는 것을 권장합니다.
2. 모듈 구조 — 11개 .tf 파일의 책임 분리
원본 repo의 파일 구조는 번호 체계로 책임이 분리되어 있어 Terraform plan/apply 순서가 자연스럽게 보장됩니다.
. ├── 01_provider.tf # AWS/Docker/Grafana provider 설정 ├── 02_vpc.tf # VPC + public/private 서브넷 + NAT + IGW + RT ├── 03_s3.tf # Telegraf 설정 저장용 S3 bucket ├── 04_ecr.tf # Telegraf 컨테이너 이미지 저장 ├── 05_opensearch.tf # OpenSearch Serverless (변형 A) ├── 05_opensearch_managed # OpenSearch Managed domain (변형 B) ├── 06_amg.tf # AMG 워크스페이스 + SAML + API key rotation ├── 07_ecs.tf # ECS Fargate task + service + IAM role ├── 08_cloud9.tf # Cloud9 IDE (개발 환경) ├── output.tf # outputs (endpoint URL, role ARN 등) ├── variables.tf # input 변수 ├── Dockerfile.telegraf # Telegraf 이미지 빌드용 ├── entrypoint-telegraf.sh # S3 sync + telegraf 실행 └── configs/ # Telegraf 설정 파일 디렉터리
05_opensearch.tf 와 05_opensearch_managed 는 둘 중 하나만 활성화 합니다. Serverless는 운영 부담이 적고 Managed는 검색/색인 기능이 풍부합니다. 사용 패턴에 따라 선택합니다.
💼 인프라 구성 순서: 01 provider → 02 VPC → 03 S3 → 04 ECR → 05 OpenSearch → 06 AMG → 07 ECS. 각 단계가 직전 단계 자원을 참조합니다 (예: 07_ecs가 06_amg의 IAM role을 assume).
3. 네트워크 인프라 (02_vpc.tf) — 서울 리전 멀티 AZ
원본 repo는 서울 리전(ap-northeast-2) 에 다음 VPC 구성을 적용합니다.
| 자원 | 설정 |
|---|---|
| VPC CIDR | 10.230.47.0/24 |
| 가용 영역 | data.aws_availability_zones.available (available state만) |
| Public 서브넷 | 2개, AZ 0/1, map_public_ip_on_launch = true |
| Private 서브넷 | 2개, AZ 0/1, NAT 경유 outbound |
| NAT Gateway | 2개 (AZ별 1개), Elastic IP 할당 |
| Internet Gateway | 1개 |
3.1 핵심 자원
resource "aws_vpc" "main" {
cidr_block = "10.230.47.0/24"
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(var.default_tags, {
Name = "${var.project}-vpc"
})
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 3, count.index + 2)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = false
tags = merge(var.default_tags, {
Name = "${var.project}-private-subnet-${count.index + 1}"
})
}
resource "aws_nat_gateway" "nat_gw" {
count = 2
allocation_id = aws_eip.nat_eip[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project}-nat-gateway-${count.index + 1}"
}
}
💼 NAT Gateway × 2 = AZ당 1개 패턴이 핵심입니다. NAT를 AZ 0에만 두면 AZ 1 private 서브넷의 outbound 트래픽이 AZ 0를 거쳐야 해서 AZ 장애 시 전체 private 서브넷이 외부와 단절됩니다. 멀티 AZ 운영 시 NAT도 AZ별로 배치합니다.
4. OpenSearch — Serverless vs Managed 선택
원본 repo는 두 가지 변형 파일을 모두 제공합니다.
4.1 변형 A — OpenSearch Serverless (05_opensearch.tf)
운영 부담이 적고, pay-per-use 과금. TIMESERIES collection으로 시계열 데이터 전용.
resource "aws_opensearchserverless_collection" "this" {
name = "telegraf-collection-04258"
description = "Telegraf Data"
type = "TIMESERIES"
tags = merge(var.default_tags, {
Name = "opensearch-serverless-telegraf-collection-04258"
})
}
resource "aws_opensearchserverless_vpc_endpoint" "this" {
name = "opensearch-vpc-endpoint-04258"
vpc_id = aws_vpc.main.id
subnet_ids = [for subnet in aws_subnet.private : subnet.id]
security_group_ids = [aws_security_group.opensearch_serverless_sg.id]
}
💼 VPC endpoint가 핵심입니다. Serverless collection을 VPC 내부에서 접근하려면 endpoint가 필요하고, security group은 443 inbound만 허용하도록 좁게 설정합니다.
데이터 액세스 정책
Amazon OpenSearch Service Serverless – Amazon Managed Grafana 기준 정책 예시:
resource "aws_opensearchserverless_access_policy" "telegraf" {
name = "telegraf-access-policy"
type = "data"
policy = jsonencode([
{
Rules = [
{
ResourceType = "collection"
Resource = ["collection/<COLLECTION_NAME>"]
Permission = ["aoss:*"]
}
]
Principal = [
"arn:aws:iam::ACCOUNT_ID:role/<TELEGRAF_ECS_TASK_ROLE>"
]
}
])
}
Permission = ["aoss:*"] = collection 전체에 대한 모든 권한. 운영에서는 aoss:DescribeCollectionItems, aoss:DescribeIndex, aoss:ReadDocument 같은 read-only 권한만 부여하는 게 안전합니다.
4.2 변형 B — OpenSearch Managed Domain (05_opensearch_managed)
검색/색인/분석 기능이 풍부하고, 직접 클러스터를 운영. 시간당 과금.
resource "aws_opensearch_domain" "grafana_opensearch" {
domain_name = var.opensearch_domain
engine_version = var.opensearch_version
cluster_config {
instance_count = 2
instance_type = "t3.small.search"
zone_awareness_enabled = true
zone_awareness_config {
availability_zone_count = 2
}
}
ebs_options {
ebs_enabled = true
volume_size = 20
volume_type = "gp2"
}
vpc_options {
subnet_ids = [aws_subnet.private[0].id, aws_subnet.private[1].id]
security_group_ids = [aws_security_group.opensearch_sg.id]
}
advanced_options = {
"rest.action.multi.allow_explicit_index" = "true"
"override_main_response_version" = "true"
}
encrypt_at_rest { enabled = true }
node_to_node_encryption { enabled = true }
}
| 항목 | Serverless | Managed |
|---|---|---|
| 과금 | pay-per-use (OCU 기반) | 시간당 인스턴스 과금 |
| 운영 부담 | 없음 (자동 관리) | 클러스터 운영 직접 |
| 검색/색인 기능 | 제한적 | Lucene/PPL 완전 지원 |
| VPC endpoint | 필수 | 보안그룹으로 제어 |
| Grafana 버전 | 9.4+ 필수 | 모든 버전 |
| 추천 사용처 | 메트릭 수집 전용 | 로그 분석 + 메트릭 혼합 |
💼 instance_type = "t3.small.search" 는 테스트 환경 최소 사양입니다. 운영에서는 m6g.large.search 이상 + EBS 100GB 이상을 권장합니다.
5. AMG 워크스페이스 + SAML 인증 (06_amg.tf)
5.1 워크스페이스 생성
resource "aws_grafana_workspace" "this" {
name = "${var.project}-grafana-workspace"
account_access_type = "CURRENT_ACCOUNT"
authentication_providers = ["SAML"]
permission_type = "SERVICE_MANAGED"
role_arn = aws_iam_role.grafana_service_role.arn
configuration = jsonencode(
{
"plugins" = {
"pluginAdminEnabled" = true
}
"unifiedAlerting" = {
"enabled" = false
}
}
)
grafana_version = "10.4"
data_sources = ["AMAZON_OPENSEARCH_SERVICE"]
description = "AWS Managed Grafana"
}
data_sources = ["AMAZON_OPENSEARCH_SERVICE"] = 워크스페이스 생성 시 OpenSearch 데이터 소스를 함께 활성화. 별도 수동 설정 없이 콘솔에서 바로 데이터 소스 메뉴에 나타납니다.
5.2 Azure AD SAML 설정
resource "aws_grafana_workspace_saml_configuration" "azure_ad_config" {
workspace_id = aws_grafana_workspace.this.id
idp_metadata_url = var.saml_azure_ad_metadata_url
admin_role_values = ["GrafanaAdmin"]
editor_role_values = ["GrafanaEditor"]
role_assertion = "roles"
groups_assertion = "groups"
}
Azure AD Enterprise application의 federation metadata URL을 idp_metadata_url 에 등록하면 AMG가 SAML 인증을 자동 구성합니다. IdP 그룹 클레임 → AMG 역할 매핑은 admin_role_values / editor_role_values 로 제어합니다.
5.3 Grafana API key 자동 rotation
resource "time_rotating" "grafana_key_rotation" {
rotation_days = 1
}
resource "aws_grafana_workspace_api_key" "this" {
key_name = "${var.project}-terraform-key"
key_role = "ADMIN"
seconds_to_live = 604800
workspace_id = aws_grafana_workspace.this.id
lifecycle {
create_before_destroy = true
replace_triggered_by = [time_rotating.grafana_key_rotation]
}
}
time_rotating.rotation_days = 1 + replace_triggered_by 조합으로 API key가 매일 자동 갱신됩니다. Terraform이 plan/apply 시점에 시간이 바뀌면 자원이 destroy + create 되며, 이전 키의 TTL(7일) 이 만료되기 전에 새 키가 발급됩니다. long-lived API key의 보안 리스크를 줄이는 표준 패턴입니다.
💼 post-07에서 다룬 Azure AD SAML flow와 동일하게 AMG는 SP-initiated 만 지원합니다. IdP-initiated 로그인은 거부되니 Relay State 비워두세요.
6. ECS Fargate Telegraf — 컨테이너 이미지 (Dockerfile.telegraf)
메트릭 수집 컨테이너는 Alpine 기반의 경량 이미지입니다. 핵심은 SNMP MIB 디렉터리 설정과 S3 동기화 entrypoint입니다.
FROM alpine:latest
RUN apk add --no-cache wget curl net-snmp net-snmp-tools aws-cli telegraf
RUN echo "mibs +ALL" > /etc/snmp/snmp.conf && \
echo "mibdirs /usr/share/snmp/mibs" >> /etc/snmp/snmp.conf
COPY ./configs/telegraf.conf /etc/telegraf/telegraf.conf
COPY ./configs/icmp_targets1.conf /etc/telegraf/telegraf.d/icmp_targets1.conf
COPY ./configs/snmp_targets1.conf /etc/telegraf/telegraf.d/snmp_targets1.conf
COPY entrypoint-telegraf.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 8125
EXPOSE 8092
EXPOSE 8094
ENTRYPOINT ["/entrypoint.sh"]
mibs +ALL = 사용 가능한 모든 MIB 로드. 네트워크 장비의 vendor-specific OID를 사람이 읽을 수 있는 이름으로 해석합니다. mibdirs /usr/share/snmp/mibs = alpine 패키지의 MIB 디렉터리 명시.
💼 alpine에 net-snmp-tools 포함은 snmptranslate, snmpwalk 같은 디버깅 도구를 함께 설치하기 위함입니다. 컨테이너 안에서 snmpwalk로 메트릭 수집 가능 여부를 바로 확인할 수 있어 운영 디버깅 시간을 줄여줍니다.
7. S3 기반 Telegraf 설정 자동 reload
원본 repo의 핵심 운영 편의성 패턴입니다. 컨테이너의 entrypoint script가 S3 버킷을 polling하다가 설정 파일 변경을 감지하면 pkill -HUP 로 telegraf를 reload합니다.
7.1 entrypoint-telegraf.sh
#!/bin/sh
export AWS_DEFAULT_REGION=ap-northeast-2
s3_sync() {
while true; do
echo "[INFO] S3 버킷을 /mnt/s3로 동기화 중..."
aws s3 sync s3://${S3_BUCKET} /mnt/s3 --delete
# telegraf.conf 변경 감지
if [ -f /mnt/s3/telegraf.conf ]; then
if ! cmp -s /mnt/s3/telegraf.conf /etc/telegraf/telegraf.conf; then
echo "[INFO] telegraf.conf 변경 감지, 덮어쓰기 + reload"
cp /mnt/s3/telegraf.conf /etc/telegraf/telegraf.conf
pkill -HUP telegraf
fi
fi
# telegraf.d 디렉토리 변경 감지
if [ -d /mnt/s3/telegraf.d ]; then
updated=false
for file in /mnt/s3/telegraf.d/*; do
filename=$(basename "$file")
if ! cmp -s "$file" "/etc/telegraf/telegraf.d/$filename"; then
cp "$file" /etc/telegraf/telegraf.d/
updated=true
fi
done
if [ "$updated" = true ]; then
pkill -HUP telegraf
fi
fi
sleep 60
done
}
s3_sync &
echo "[INFO] S3 sync + reload 워커 시작"
exec telegraf
7.2 reload 흐름
운영자가 S3에 새 telegraf.conf 업로드 ↓ (60초 이내) 컨테이너의 aws s3 sync 가 변경 감지 ↓ cmp 비교 → 다르면 /etc/telegraf/로 복사 ↓ pkill -HUP telegraf → SIGHUP 시그널로 설정 reload ↓ 새 설정으로 메트릭 수집 재개 ↓ ECS task 자체는 계속 동작 (재시작 없음)
💼 pkill -HUP 는 SIGHUP 시그널을 보내는 표준 유닉스 패턴입니다. 잘 만들어진 데몬은 SIGHUP을 받으면 설정 파일을 다시 읽고 프로세스는 유지합니다. ECS task 자체가 죽지 않아 IP 주소 / ENI도 유지됩니다.
💼 운영 주의: 잘못된 설정이 S3에 업로드되면 telegraf가 reload 후 시작 자체를 못해 ECS task가 unhealthy로 표시됩니다. staging prefix에서 검증 후 prod 이동하거나 versioned S3 bucket에서 빠르게 rollback 할 수 있는 체계를 권장합니다.
8. ECS Fargate task + OpenSearch 적재 (07_ecs.tf)
07_ecs.tf 는 ECS Fargate task definition + service + IAM role + task role + 보안 그룹을 모두 정의합니다. 12KB 분량으로 ECS 자원의 모든 측면을 다룹니다.
핵심 자원:
| 자원 | 용도 |
|---|---|
aws_ecs_cluster.this |
ECS cluster (Fargate) |
aws_ecs_task_definition.telegraf |
Telegraf task 정의 |
aws_ecs_service.telegraf |
ECS service (desired_count=1) |
aws_iam_role.ecs_task_execution_role |
ECS task 실행 role (image pull, log write) |
aws_iam_role.telegraf_ecs_task_role |
ECS task 자체 role (S3 read, OpenSearch write) |
aws_security_group.ecs_telegraf_sg |
컨테이너 SG (outbound만) |
telegraf_ecs_task_role 이 OpenSearch Serverless의 데이터 액세스 정책에 등록되어 있어, ECS task가 OpenSearch에 직접 write할 수 있습니다. ECS task가 NAT gateway를 통해 OpenSearch VPC endpoint로 접근합니다.
9. Cloud9 개발 환경 (08_cloud9.tf)
Cloud9 IDE를 Terraform으로 provision하여, 브라우저에서 바로 AWS 환경을 터미널로 접근할 수 있습니다.
resource "aws_cloud9_environment_ec2" "this" {
name = "${var.project}-cloud9-env"
instance_type = "t3.small"
subnet_id = aws_subnet.private[0].id
}
💼 Cloud9을 private 서브넷에 두려면 SSM Session Manager로 접근해야 합니다. 원본 repo의 session-manager-plugin.rpm 파일이 그 설정의 일부입니다. AWS Console에서 “Connect using SSM” 옵션을 선택하면 브라우저 터미널이 열립니다.
10. 자주 발생하는 오류와 해결법
| 증상/오류 메시지 | 원인 | 해결 방법 |
|---|---|---|
| OpenSearch 데이터 소스에 “No data” | AMG 워크스페이스가 Grafana 9.4 미만 | grafana_version = "10.4" 이상으로 설정 |
| OpenSearch Serverless 권한 거부 | access policy principal 누락 | telegraf_ecs_task_role ARN 추가 |
| ECS task가 계속 STOPPED | OpenSearch endpoint 접근 불가 | NAT gateway + security group egress 확인 |
| Telegraf 설정 reload 안 됨 | pkill -HUP 권한 부족 |
entrypoint에서 exec telegraf 로 foreground 실행 확인 |
| S3 sync는 되지만 reload 안 됨 | cmp -s 비교 실패 |
S3 파일이 base64/encoding 변환됐을 수 있음, raw upload |
time_rotating drift 에러 |
수동 terraform apply 시간 간격 |
1일 rotation이면 무시 가능, 더 짧으면 lifecycle 변경 |
| AMG API key 만료 | TTL 7일 지난 후 terraform destroy | create_before_destroy = true 로 자동 갱신 |
| Cloud9 연결 안 됨 | Session Manager plugin 미설치 | 레포의 session-manager-plugin.rpm 설치 |
| VPC endpoint가 active 안 됨 | subnet에 OpenSearch ENI 생성 대기 | terraform apply 후 5~10분 대기 |
| AMG Grafana 버전 호환 안 됨 | OpenSearch Serverless → Grafana 9.4+ 필수 | 워크스페이스 버전 명시 |
11. 마치며
AWS Managed Grafana + OpenSearch 조합은 인프라 + 데이터 수집 + 설정 동기화를 모두 코드로 관리할 수 있다는 점이 핵심 가치입니다. 원본 repo의 11개 .tf 파일 + Dockerfile + entrypoint script가 그 모든 측면을 다룹니다. 운영 중 설정 변경은 S3 업로드만으로, Grafana API key는 자동 rotation으로, 인프라 변경은 Terraform plan/apply로 — 운영 부담을 최소화하면서도 모든 변경 이력을 Git으로 추적할 수 있습니다.
추가로, 이 글에서 비교한 OpenSearch Serverless vs Managed domain 옵션, NAT gateway × AZ 패턴, Serverless collection type, Terraform provider 리소스 인자 등은 AWS 측 로드맵/요금제에 따라 자주 갱신됩니다. 글 작성 시점 (1차 검증 2024-11-28) 의 옵션과 현재 시점 옵션이 다를 수 있으므로 재현 전 OpenSearch Service User Guide + terraform-provider-aws CHANGELOG 최신판을 확인하시기 바랍니다.
🎯 핵심 요약
- 인프라 전체를 코드로 = VPC부터 AMG까지 Terraform 한 번, 운영 환경 일관성 보장
- OpenSearch Serverless vs Managed = 운영 부담 vs 검색 기능, TIMESERIES는 Serverless가 효율적
- VPC endpoint + 데이터 액세스 정책 = Serverless 접근 시 필수, principal ARN 등록으로 ECS task만 write 허용
- Grafana API key 자동 rotation =
time_rotating+create_before_destroy조합, 매일 자동 갱신 - S3 polling reload 패턴 =
pkill -HUP로 ECS redeploy 없이 설정 변경, staging 검증 후 prod 적용 권장 - NAT Gateway × AZ별 1개 = 멀티 AZ 환경에서 AZ 장애 격리, single NAT 패턴 금지
- Cloud9 in private subnet = SSM Session Manager로 접근, 브라우저에서 AWS 터미널