서론: 왜 OpenTelemetry + Push 방식이 필요한가?

클라우드 인프라가 커질수록 모니터링 비용과 복잡도는 함께 증가합니다. 특히 국내 기업들은 서드파티 라이선스 비용벤더 종속(Vendor Lock-in) 문제로 고민이 많죠. AWS 블로그에 소개된 이 아키텍처는 CloudWatch Metric Streams와 OpenTelemetry Collector를 Lambda로 연결해, VPC 내부에서 민감한 메트릭 데이터를 안전하게 수집하는 방법을 보여줍니다.

핵심 질문: "Prometheus Pull 방식으로 API를 계속 긁어오다가 Throttling 나고 비용 폭탄 맞기 전에, Push 방식으로 갈아탈 수 없을까?"

이 질문에 대한 해답이 바로 이 글입니다. AI가 생성한 코드, 그대로 배포하면 생기는 재앙과 Vercel의 해법에서도 강조했듯, 운영 환경에서의 데이터 파이프라인 안정성은 절대 타협할 수 없는 요소입니다.

AWS CloudWatch Metric Streams architecture diagram with Lambda and OpenTelemetry collector inside VPC Development Concept Image

본론 1: Pull vs Push — 왜 Push가 정답일까?

많은 팀이 Prometheus의 Pull 모델에 익숙합니다. 하지만 규모가 커지면 다음과 같은 문제가 발생합니다.

항목Pull (Prometheus)Push (OpenTelemetry + Metric Streams)
API 호출주기적 Polling → Throttling 위험Event-driven → 필요할 때만 전송
지연 시간분 단위 (Polling 주기)Sub-minute (실시간에 가까움)
비용API 호출 비용 + 라이선스 비용라이선스 무료 (Apache 2.0)
확장성Scrape 대상 증가 시 병목Collector 수평 확장 가능
보안Endpoint 노출 필요VPC 내부에서 안전하게 수신

실제 아키텍처 (AWS 공식 레퍼런스)

CloudWatch Metric Streams → Firehose → Lambda (Transform) → NLB → OpenTelemetry Collector (EC2)

Lambda 함수가 핵심입니다. Firehose는 VPC 내부 endpoint로 직접 전송할 수 없기 때문에, Lambda가 중간에서 데이터를 받아 NLB를 통해 Collector로 밀어 넣습니다.

# Lambda Transform 함수 예시 (Python 3.9+)
import json
import base64
import requests
import os

def lambda_handler(event, context):
    output = []
    
    for record in event['records']:
        # Firehose는 base64로 인코딩된 데이터를 전달
        payload = base64.b64decode(record['data']).decode('utf-8')
        metrics = json.loads(payload)
        
        # OpenTelemetry Collector HTTP endpoint로 전송
        otel_endpoint = os.environ['OTEL_COLLECTOR_ENDPOINT']
        try:
            resp = requests.post(
                f"{otel_endpoint}/v1/metrics",
                json=metrics,
                timeout=5
            )
            resp.raise_for_status()
            # 성공 시 Firehose에 OK 신호
            output.append({
                'recordId': record['recordId'],
                'result': 'Ok',
                'data': record['data']
            })
        except Exception as e:
            print(f"전송 실패: {e}")
            # 실패 시 Firehose가 S3 백업으로 보내도록 처리
            output.append({
                'recordId': record['recordId'],
                'result': 'ProcessingFailed',
                'data': record['data']
            })
    
    return {'records': output}

🚨 주의사항: Lambda 함수는 Firehose와 동기적으로 동작합니다. 즉, Lambda가 타임아웃(기본 60초)에 걸리면 전체 스트림이 지연됩니다. timeout을 충분히 설정하고, 재시도 로직을 꼭 추가하세요.

OpenTelemetry collector running on EC2 instance receiving metrics from Lambda function Dev Environment Setup

본론 2: CloudFormation으로 한 방에 배포하기

AWS CLI와 CloudFormation을 이용해 전체 스택을 자동화할 수 있습니다. 아래는 핵심 파라미터 예시입니다.

// parameters.json
[
  {
    "ParameterKey": "VpcId",
    "ParameterValue": "vpc-12345678"
  },
  {
    "ParameterKey": "SubnetIds",
    "ParameterValue": "subnet-11111111,subnet-22222222"
  },
  {
    "ParameterKey": "OtelCollectorEndpoint",
    "ParameterValue": "http://internal-nlb-123456789.elb.ap-northeast-2.amazonaws.com:4318"
  },
  {
    "ParameterKey": "StageBucketName",
    "ParameterValue": "cf-stage-bucket-203918862653"
  }
]
# CloudFormation 스택 생성
aws cloudformation create-stack \
  --stack-name cw-metrics-demo \
  --template-body file://cf-packaged-file.yaml \
  --parameters file://parameters.json \
  --capabilities CAPABILITY_IAM

국내 SI 환경에서의 적용 맥락

  • 보안 규제가 강한 금융권/공공기관에서는 VPC 내부에 Collector를 두고 NLB로만 접근을 허용하는 이 아키텍처가 매우 적합합니다.
  • 다만, Lambda가 VPC 내부에 위치해야 NLB에 접근할 수 있다는 점을 잊지 마세요. Lambda에 VPC 설정을 추가하면 콜드 스타트가 길어질 수 있으므로, Reserved Concurrency를 설정해 두는 것이 좋습니다.
  • S3 버킷은 백업 용도로만 사용하므로, 실제 비용이 거의 발생하지 않습니다. 하지만 데이터 유실 방지를 위해 S3에 실패한 레코드를 저장하는 ProcessingFailed 경로를 꼭 구성하세요.

Network Load Balancer distributing traffic to OpenTelemetry collectors in private subnets Technical Structure Concept

결론: 실무 적용 전 체크리스트

이 아키텍처는 Push 기반 모니터링으로 전환하려는 모든 팀에게 훌륭한 출발점이 됩니다. 하지만 몇 가지 고려사항이 있습니다.

이 기술의 한계 또는 주의사항

  1. Lambda 동기 호출 비용: Firehose가 레코드 1MB당 Lambda를 호출하므로, 메트릭 양이 많으면 Lambda 비용이 증가할 수 있습니다. 사전에 예상 비용을 계산해보세요.
  2. Collector 장애 시 데이터 손실: Lambda에서 Collector로 전송 중 장애가 발생하면, Firehose는 재시도하지만 일부 데이터가 손실될 수 있습니다. S3 백업 + Dead Letter Queue 구성이 필수입니다.
  3. OTLP 버전 호환성: CloudWatch Metric Streams는 OpenTelemetry 0.7과 1.0을 지원합니다. Collector 버전과 맞춰야 합니다.

다음 단계 학습 방향

  • border-radius의 한계를 깬 CSS corner-shape, 실무 적용 가이드 — 프론트엔드 성능 최적화에 관심이 있다면 함께 보세요.
  • AWS Distro for OpenTelemetry (ADOT) 공식 문서를 읽어보고, X-Ray나 AMP와의 연동을 테스트해보세요.
  • 실제 프로덕션에 적용하기 전에 카나리아 배포로 일부 메트릭만 먼저 전환해 보는 것을 추천합니다.

함께 보면 좋은 글

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.