AWS ECS(Elastic Container Service)에서는 컨테이너와 태스크의 메트릭을 편리하게 모니터링할 수 있도록 Container Insights 기능을 제공합니다. 겉보기에는 유용해 보이지만, 실제로는 적지 않은 비용 부담을 초래할 수 있습니다.
AWS 공식 문서에 따르면, 하나의 클러스터에서 비교적 소규모 워크로드(예: 5개 서비스, 10개의 태스크 정의, 50개 컨테이너)를 운영할 경우에도 매월 약 $57.91의 비용이 발생합니다. 이는 기본 Container Insights만 사용한 경우의 비용이며, Enhanced Observability(향상된 관찰성 기능)를 활성화하면 $158.48까지 급증할 수 있습니다.
※ 참고: AWS CloudWatch 요금 예제 12번 (https://aws.amazon.com/ko/cloudwatch/pricing/)
이러한 비용 문제를 해결하려면, 메트릭 저장소를 CloudWatch 대신 외부 저장소로 전환하는 방식이 효과적일 수 있습니다.
본 글에서는 AWS ECS에서 수집되는 메트릭 데이터를 OpenTelemetry와 ADOT(AWS Distro for OpenTelemetry)를 활용해 외부에 구축된 Prometheus 서버로 전송하는 방법을 소개합니다.
OpenTelemetry는 모니터링 시 발생하는 여러가지 형태의 데이터를 수집/변환/write하기 쉽게 정형화한 프로토콜입니다. 이 표준 프로토콜을 통해 다양한 클라우드 환경에서 일관된 방식으로 관찰 데이터를 처리할 수 있습니다.
ADOT에 대한 소개 링크: https://aws-otel.github.io/docs/introduction
ADOT는 AWS에서 발생하는 지표 데이터(log, metric, trace)를 OpenTelemetry의 형태로 변경해주도록 하기 위해 만든 AWS 프로젝트입니다. AWS에서 제공하는 이 배포판은 AWS 서비스와의 통합이 최적화되어 있어, AWS 환경에서 보다 쉽고 다양한 저장소에 관찰성(Observability)을 구현할 수 있도록 도와줍니다.
AWS 리소스와 매니지드 서비스에서 활용할 수 있도록 제공됩니다. AWS App Runner, AWS Lambda, Amazon Elastic Compute Cloud (EC2), Amazon Elastic Container Service (ECS), Amazon Elastic Kubernetes Service (EKS) on EC2, AWS Fargate 등 대부분의 AWS 서비스에서 OpenTelemetry를 수집할 수 있으며, 하이브리드 클라우드와 온프레미스 환경에서도 활용 가능합니다.
링크 : https://aws-otel.github.io/docs/adot-collector-using-ecs

ECS는 Trace 및 Metrics의 OpenTelemetry 데이터를 수집 가능하도록 OpenTelemetry Collector를 구현해 놓았습니다. Log는 보통 애플리케이션에서 관리(Logback 등)를 하게 되기 때문에, 기본 구현 부분에서 제외되어 있습니다. 그러나 필요한 경우 Log 또한 OTLP(OpenTelemetry Protocol) 표준에 맞춰 구현이 가능합니다.
아래는 공식문서에서 제공하는 ECS OpenTelemetry Collector의 설정 방법입니다. 아래 config를 포함하여 ECS task의 sidecar로 OTEL collector를 배포하면, 데이터 유형에 맞게 수집이 가능합니다.
| 데이터 유형 | 목적지 | 유형 | 설정 파일 |
|---|---|---|---|
| Traces | X-Ray | Traces: end-to-end request path | ecs-xray.yaml |
| Traces + Metrics | X-Ray + CloudWatch | Traces + CW Metrics | ecs-cloudwatch-xray.yaml |
| Traces + Metrics | X-Ray + AMP | Traces + AMP Metrics | ecs-amp-xray.yaml |
| Metrics | CloudWatch | CW Metrics only | ecs-cloudwatch.yaml |
| Metrics | AMP | AMP Metrics only | ecs-amp.yaml |
ECS의 데이터를 AMP로 수집하는 방법: https://docs.aws.amazon.com/ko_kr/prometheus/latest/userguide/AMP-onboard-ingest-metrics-OpenTelemetry-ECS.html
이번 장에서는 ECS OTEL에서 제공하는 기능 중 특별히 Metrics 정보를 수집하는 방법에 대해 알아보겠습니다. 일반적으로는 AWS Managed Prometheus(AMP)로 수집하는 것이 일반적이지만, 이미 기존 프로메테우스 서버와 그라파나가 구축되어 있는 환경에서는 해당 서버로 데이터를 전송하는 것이 중앙 집중화에 더 효과적일 수 있습니다.
위 링크의 'AWS Distro for Open Telemetry를 사용하여 Amazon ECS에서 지표 수집 설정' 가이드와 유사하지만, 몇 가지 부분이 다릅니다. ADOT를 통해 ECS에서 지표를 수집하고 외부 프로메테우스에 적용하기 위해서는 다음과 같은 필수 단계를 거쳐야 합니다.
어떤 지표를 어떻게 수집하고, 어떻게 처리하며, 어디에 저장할지에 대한 명세를 해야 합니다. 이 명세는 YAML 파일로 정의됩니다.
receivers:
awsecscontainermetrics:
# 메트릭 수집 주기 설정 (기본은 20초이지만 자주 보내지 않기 위해 설정)
collection_interval: 1m
processors:
attributes:
actions:
# container.name 속성 값을 container_name이라는 새 속성으로 삽입
- key: container_name
from_attribute: container.name
action: insert
# job이라는 새 속성을 서비스명으로 삽입 (보통 프로메테우스는 job label을 통해 서비스 식별)
- key: job
value: "service_name"
action: insert
exporters:
prometheusremotewrite:
# 수집한 메트릭을 전송할 Prometheus Remote Write endpoint
endpoint: [Prometheus Remote Write endpoint]
headers:
# 인증 헤더 (프로메테우스에 접근시 필요한 ID:PWD를 base64 형태로 변경하여 추가)
Authorization: "Basic passwordpassword"
resource_to_telemetry_conversion:
# 리소스 속성을 메트릭 라벨로 변환
enabled: true
# 메트릭 이름에 _sum, _count 등의 suffix를 붙이지 않음 (지표의 깔끔함을 위해)
add_metric_suffixes: false
service:
pipelines:
metrics:
# 메트릭 수신기 지정
receivers: [awsecscontainermetrics]
# 속성 가공 processor 지정
processors: [attributes]
# 메트릭 내보내기 대상 exporter 지정
exporters: [prometheusremotewrite]
각 구성 요소에 대한 상세 설명:
receivers:
awsecscontainermetrics는 ECS Task Metadata Endpoint에서 제공하는 데이터와 docker stats를 자동으로 수집합니다. collection_interval을 조정하여 수집 빈도를 제어할 수 있습니다. 너무 짧게 설정하면 리소스 사용량이 증가할 수 있으므로 워크로드 특성에 맞게 조정하는 것이 중요합니다.processors:
attributes 프로세서는 수집된 데이터에 추가 메타데이터를 삽입하거나 변형합니다. 여기서는 container.name을 container_name으로 매핑하고, 서비스 이름을 job 라벨로 추가하여 Prometheus에서 쿼리 시 편리하게 사용할 수 있도록 합니다.exporters:
prometheusremotewrite는 수집된 메트릭을 Prometheus Remote Write API를 통해 외부 Prometheus 서버로 전송합니다. 인증이 필요한 경우 headers 섹션에 적절한 인증 정보를 제공해야 합니다.resource_to_telemetry_conversion을 활성화하면 리소스 속성(예: 컨테이너 ID, 이름 등)이 메트릭 라벨로 추가되어 더 세밀한 필터링이 가능해집니다. (테스크 단위, 서비스 단위, 클러스터 단위로의 그룹화 쿼리가 가능)service:
pipelines는 데이터 흐름을 정의합니다. 여기서는 메트릭 파이프라인을 구성하여 awsecscontainermetrics 수신기에서 데이터를 가져와 attributes 프로세서로 처리한 후 prometheusremotewrite 내보내기 도구로 전송합니다.FROM public.ecr.aws/aws-observability/aws-otel-collector:latest
COPY adot-config.yaml /etc/ecs/otel-config.yaml
CMD ["--config=/etc/ecs/otel-config.yaml"]
이 Dockerfile은 AWS에서 제공하는 공식 ADOT Collector 이미지를 기반으로 하고, 우리가 만든 설정 파일을 컨테이너 내부의 /etc/ecs/otel-config.yaml 경로에 복사합니다. 컨테이너가 시작될 때 이 설정 파일을 사용하도록 지정합니다.
# create repo:
COLLECTOR_REPOSITORY=$(aws ecr create-repository --repository aws-otel-collector \
--query repository.repositoryUri --output text)
# build ADOT collector image:
docker build -t $COLLECTOR_REPOSITORY:ecs .
# sign in to repo:
aws ecr get-login-password --region my-region | \
docker login --username AWS --password-stdin $COLLECTOR_REPOSITORY
# push ADOT collector image:
docker push $COLLECTOR_REPOSITORY:ecs
ADOT Collector를 ECS 태스크의 사이드카로 추가하기 위해 태스크 정의에 다음과 같은 컨테이너 정의를 추가합니다:
{
"name": "aws-otel-collector-pm",
"image": "2단계에서 푸쉬한 이미지 URL",
"cpu": 0,
"portMappings": [],
"essential": true,
"environment": [
{
"name": "AWS_REGION",
"value": "ap-northeast-2"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/aws-otel-collector",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "ecs"
}
}
}
위 컨테이너 정의를 기존 태스크 정의에 추가합니다. 예시를 들면 다음과 같습니다:
"containerDefinitions": [
{
"name": "aws-otel-collector-pm",
"image": "2단계에서 푸쉬한 이미지 URL",
"cpu": 0,
"portMappings": [],
"essential": true,
"environment": [
{
"name": "AWS_REGION",
"value": "ap-northeast-2"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/aws-otel-collector",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "ecs"
}
}
},
{
"name": "nginx-app",
"image": "ubuntu/nginx:latest",
"cpu": 0,
"portMappings": [
{
"name": "nginx-app-80-tcp",
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
],
"essential": true,
"environment": [],
"mountPoints": [],
"volumesFrom": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/nginx-app",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "ecs"
}
},
"systemControls": []
}
]
이렇게 구성하면 ADOT Collector는 ECS 태스크 내의 다른 컨테이너들과 함께 실행되어, 같은 태스크 내에서 발생하는 메트릭을 수집하고 구성된 Prometheus 서버로 전송합니다.
설정이 완료되면 프로메테우스 서버에서 메트릭이 수집되는지 확인할 수 있습니다.

다음과 같은 메트릭 데이터들이 수집됩니다:

메트릭이 수집되지 않는 경우:
리소스 사용량 관련 이슈:
collection_interval 값을 조정하여 수집 빈도 최적화. 디폴트가 20s이기 때문에, 자주 수집하고 싶지 않다면 조정이 필요.다음 단계로는 Grafana를 활용한 대시보드 구성 및 알람 설정에 대해 알아보겠습니다. 각 지표가 어떤 것을 의미하는지 otel collector 오픈소스의 코드(https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/awsecscontainermetricsreceiver)를 살펴보고, 이를 통해 수집된 메트릭을 시각화하고 이상 징후를 감지하는 방법에 대해 살펴볼 예정입니다.