- Published on
CloudWatch Logs 비용 폭증 원인과 절감 10가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버/컨테이너가 늘어날수록 로그는 자연스럽게 증가하지만, CloudWatch Logs 비용 폭증은 대개 “자연 증가”가 아니라 설정/운영 상의 특정 트리거로 발생합니다. 특히 EKS/EC2에서 에이전트가 중복 수집하거나, 디버그 로그가 대량으로 찍히거나, 보존 기간이 무제한(또는 너무 김)으로 남아 있을 때 비용이 기하급수적으로 튀는 일이 흔합니다.
이 글은 비용을 구성요소(수집·저장·인덱싱·쿼리·전송)로 쪼개서 폭증 원인을 빠르게 특정하고, 바로 적용 가능한 절감 10가지를 IaC/CLI 예시와 함께 정리합니다. 운영 중 장애 분석 때문에 로그가 필요한 경우가 많으니, 무작정 끄기보다 "필요한 로그만, 필요한 기간만, 필요한 곳에" 남기는 방향으로 접근합니다.
관련 장애 상황에서 로그가 폭발하는 패턴은 Kubernetes의 재시작 루프에서 자주 보입니다. CrashLoopBackOff로 로그가 초당 수천 줄씩 쌓이면 수집/저장 비용이 즉시 증가하므로, 원인 분석은 별도로 병행하는 것이 좋습니다: Kubernetes CrashLoopBackOff 원인별 로그·Probe·리소스 디버깅
CloudWatch Logs 과금 구조(폭증 포인트)
CloudWatch Logs 비용은 보통 다음 축에서 발생합니다(계정/리전/옵션에 따라 항목 명칭은 다를 수 있음).
- Ingestion(수집): 애플리케이션/에이전트가 로그를 CloudWatch Logs로 넣을 때 GB 단위로 과금
- Storage(저장): 로그 그룹에 보관되는 데이터 용량 × 보존 기간
- Data scanning/Insights 쿼리: Logs Insights로 조회 시 스캔한 데이터 양에 비례
- Subscription/Delivery: Kinesis/Lambda/Firehose 등으로 구독 전송 시 전송/처리 비용
- Vended logs(서비스 로그): ALB/VPC Flow Logs 등 특정 서비스 로그는 별도 단가/패턴
따라서 “어느 항목이 늘었는지”를 먼저 확인해야 합니다.
1) 가장 흔한 폭증 원인 8가지
- 보존 기간이 ‘Never expire’ 또는 과도하게 길다
- EKS/EC2 로그 에이전트 중복 수집(Fluent Bit + CloudWatch Agent 동시, 혹은 DaemonSet 중복)
- DEBUG/TRACE 레벨이 프로덕션에 켜짐
- 예외/타임아웃 루프로 동일 로그가 초당 수백~수천 번 반복
- 멀티라인 로그/스택트레이스가 과도하게 발생(한 번의 오류가 수십 라인)
- 로그 그룹/스트림 난립(pod/컨테이너별 스트림 폭발)로 관리/쿼리 비용 증가
- Logs Insights 쿼리 남발(대규모 기간 스캔)
- 구독 필터로 외부로 내보내는 파이프라인 비용(Firehose/S3/ES 등)까지 합산되어 폭증
비용 폭증을 30분 내로 특정하는 체크리스트
1) Cost Explorer에서 ‘CloudWatch’ 세부 항목 분해
- 서비스: AmazonCloudWatch
- Usage Type/Operation 기준으로 분해
CW:Logs-IngestedBytes유사 항목이 급증 → 수집 폭증CW:Logs-Storage유사 항목이 급증 → 보존/저장 폭증CW:Logs-Insights유사 항목이 급증 → 쿼리 스캔 폭증
2) 어떤 로그 그룹이 가장 큰지 Top-N 확인
CloudWatch Logs 자체에서 “로그 그룹별 저장 용량”이 직관적으로 안 보일 때가 있어, 아래처럼 CloudWatch Logs API로 그룹을 나열하고 보존 기간/태그를 확인합니다.
aws logs describe-log-groups \
--query 'logGroups[*].{name:logGroupName,retention:retentionInDays,stored:storedBytes}' \
--output table
retentionInDays가 비어 있으면 무기한 보존입니다.storedBytes는 참고용이며, 실제 청구는 시점/압축/내부 정책에 따라 다를 수 있습니다.
3) EKS라면 CrashLoop/재시작부터 의심
Pod가 재시작 루프에 빠지면 로그가 폭증하고, 동시에 장애도 발생합니다.
kubectl get pods -A | egrep 'CrashLoopBackOff|Error|ImagePullBackOff'
kubectl describe pod -n <ns> <pod>
ALB/Ingress 장애로 502/504가 반복되며 액세스 로그/애플리케이션 로그가 동시에 늘어나는 경우도 많습니다. 이 경우는 원인 진단을 병행하세요: AWS ALB 502·504 난사 - 원인별 해결 체크리스트
CloudWatch Logs 비용 절감 10가지(실전 우선순위)
아래 10가지는 “효과가 큰 순서 + 적용 난이도”를 함께 고려했습니다.
1) 로그 그룹 보존 기간(retention) 강제 적용(가장 즉효)
무기한 보존은 가장 흔한 비용 폭증 원인입니다. 서비스 특성에 맞게 3/7/14/30일 등으로 제한하고, 장기 보관이 필요하면 S3로 내보내는 전략을 씁니다.
# 예: 14일 보존으로 설정
aws logs put-retention-policy \
--log-group-name "/aws/eks/prod/cluster" \
--retention-in-days 14
여러 로그 그룹에 일괄 적용하려면 태그/이름 규칙 기반으로 스크립트를 돌리는 것이 안전합니다.
2) DEBUG/TRACE 로그를 프로덕션에서 기본 차단
비용은 결국 “바이트”입니다. 로그 레벨 하나로 수집량이 10~100배까지 차이 납니다.
- 기본:
INFO이상 - 장애 시: 짧은 기간만
DEBUG로 올리고, 반드시 원복 - 민감 정보 마스킹(PII/토큰)도 같이 처리
애플리케이션 예시(Python):
import logging
import os
level = os.getenv("LOG_LEVEL", "INFO").upper()
logging.basicConfig(level=level)
logger = logging.getLogger(__name__)
logger.debug("debug payload size=%s", 10_000)
logger.info("service started")
3) 중복 수집 제거(EKS/EC2 에이전트 점검)
EKS에서 아래 조합이 겹치면 같은 로그가 두 번 들어가 비용이 2배가 됩니다.
- CloudWatch Agent + Fluent Bit(또는 Fluentd)
- DaemonSet이 2개(이전 배포 잔존)
- 노드에서 journald + container log 동시 수집
점검 포인트:
- DaemonSet 목록 확인
kubectl get ds -A | egrep -i 'fluent|cloudwatch|agent|log'
- 실제 CloudWatch에 동일 메시지가 2번씩 찍히는지 샘플링
4) 멀티라인 스택트레이스/대용량 메시지 줄이기
예외 1번이 50줄 스택트레이스라면, 오류율이 조금만 올라가도 수집량이 폭증합니다.
권장:
- 스택트레이스는 샘플링(아래 5번과 결합)
- 동일 예외 반복 시 요약 로그로 전환
- 요청/응답 바디 전체 로깅 금지(특히 JSON 대형 payload)
5) 로그 샘플링/레이트 리밋 적용(“반복 로그 폭발” 방지)
타임아웃/재시도 루프에서 동일 로그가 초당 수천 번 찍히는 상황을 막습니다.
예시(Node.js 개념 코드):
const last = new Map();
function rateLimitedLog(key, msg, intervalMs = 5000) {
const now = Date.now();
const prev = last.get(key) || 0;
if (now - prev > intervalMs) {
console.warn(msg);
last.set(key, now);
}
}
rateLimitedLog('db-timeout', 'DB timeout is happening');
6) 불필요한 로그 그룹/스트림 생성 패턴 수정
Pod/컨테이너/요청 단위로 스트림이 쪼개지면 탐색성과 비용(특히 쿼리 스캔)이 악화됩니다.
- 스트림 네이밍을 단순화(서비스/환경/파드 정도)
- 짧은 수명의 Job/CronJob는 별도 그룹으로 분리 + 짧은 retention
- dev/staging 로그를 prod와 분리
7) Logs Insights 쿼리 비용 줄이기(스캔 범위 최소화)
Logs Insights는 스캔한 데이터에 비례해 과금됩니다. 쿼리 자체가 복잡해서가 아니라, 기간/대상 로그 그룹이 넓을수록 비쌉니다.
실전 팁:
- 시간 범위를 먼저 5분/15분으로 좁히고 확대
filter를 초반에 강하게 걸기- 특정 로그 그룹만 대상으로 실행
예시 쿼리:
fields @timestamp, @message
| filter @message like /ERROR|Exception/
| sort @timestamp desc
| limit 50
8) 구독 필터(Subscription) 파이프라인 비용 재점검
CloudWatch Logs → Kinesis/Firehose/Lambda → S3/ES/OpenSearch로 보내는 구조는 CloudWatch 비용 외에 다운스트림 비용이 함께 증가합니다.
- 정말 모든 로그를 외부로 보내야 하는지(보통은 ERROR/WARN만)
- 구독 필터에서 필터링 가능한지
- 장애 분석용은 샘플링/기간 한정이 가능한지
9) 장기 보관은 S3로, 검색은 Athena/OpenSearch로 역할 분리
CloudWatch Logs는 운영 가시성에 좋지만, 장기 보관/대규모 분석에는 비용 효율이 떨어질 수 있습니다.
권장 아키텍처:
- CloudWatch Logs: 7~30일(운영/즉시 대응)
- S3(압축 Parquet/JSON gzip): 90일~수년(컴플라이언스/포렌식)
- Athena: 필요할 때만 쿼리
10) 태그/계정/리전 단위 거버넌스(예산 알림 + 자동 교정)
비용 폭증은 기술 문제이면서 운영 프로세스 문제입니다.
- 로그 그룹에
env=prod,team=payments같은 태그 강제 - AWS Budgets로 CloudWatch 비용 알림
- Config/Lambda로 retention 미설정 로그 그룹 자동 교정
예: retention 없는 로그 그룹을 찾아 14일로 강제하는 스크립트(간단 버전)
for g in $(aws logs describe-log-groups --query 'logGroups[?retentionInDays==`null`].logGroupName' --output text); do
echo "set retention 14d: $g"
aws logs put-retention-policy --log-group-name "$g" --retention-in-days 14
done
“비용 폭증”을 만드는 전형적인 시나리오 3가지
시나리오 A: CrashLoopBackOff + 스택트레이스
- Pod 재시작 → 초기화 실패 → 스택트레이스 30줄 출력 → 재시작
- 1분에 수십 번 반복되면 수집량이 폭발
- 해결: 재시작 원인 제거 + 스택트레이스 샘플링 + retention 단축
시나리오 B: 타임아웃 재시도 루프 + DEBUG
- 외부 API 지연 → 재시도 → 요청/응답 바디를 DEBUG로 로깅
- 장애가 길어질수록 로그가 더 많이 쌓여 비용이 2차 피해로 확대
- 해결: 레이트 리밋 + 바디 로깅 금지 + 장애 시에만 제한적 DEBUG
시나리오 C: 모든 로그를 외부로 구독 전송
- CloudWatch에 저장 + Firehose로 S3 적재 + OpenSearch 인덱싱
- 트래픽 증가 시 3곳에서 동시에 비용 증가
- 해결: CloudWatch에는 짧게, S3는 장기 보관, OpenSearch는 필요한 로그만
마무리: “줄이는 것”보다 “통제하는 것”이 핵심
CloudWatch Logs 비용은 한 번 폭증하면 원인을 못 찾는 동안 계속 누적됩니다. 가장 빠른 대응은 (1) retention 설정, (2) DEBUG 차단, (3) 중복 수집 제거입니다. 그 다음 단계로 샘플링/레이트 리밋, 쿼리 스캔 최소화, 구독 파이프라인 필터링, S3로 역할 분리를 적용하면 비용을 안정적으로 통제할 수 있습니다.
운영 중 502/504 같은 에러가 반복되며 로그가 급증하는 경우에는 애플리케이션/ALB/Ingress까지 함께 점검해야 근본적으로 줄어듭니다: AWS ALB 502·504 난사 - 원인별 해결 체크리스트