모니터링 데이터 S3 Object Storage 적재 구조
문제 상황#
Staging 환경에서 Spot 인스턴스가 회수되고 새 노드가 다른 AZ에 생성되었을 때, 모니터링 Pod(Loki, Tempo 등)가 기존 PVC를 마운트하지 못하고 Pending 상태에 빠지는 문제가 발생했다.
증상#
- Spot 노드 회수 → 새 노드가 다른 AZ(예: ap-northeast-2a → 2c)에 프로비저닝
- 모니터링 Pod 재스케줄 → 기존 PVC(EBS)가 이전 AZ에 고정되어 있어 마운트 실패
- Pod:
Pending→Warning: FailedAttachVolume - 로그/트레이스 수집 중단
근본 원인: EBS는 단일 AZ에 바인딩된다#
[기존 상태]
Node (ap-northeast-2a) ← Spot
└── Loki Pod
└── PVC → EBS (ap-northeast-2a에 고정)
[Spot 회수 후]
Node (ap-northeast-2c) ← 새로 프로비저닝
└── Loki Pod (Pending)
└── PVC → EBS (ap-northeast-2a) ← 마운트 불가!
EBS 볼륨은 생성된 AZ에서만 사용할 수 있다. Spot 인스턴스는 가용한 AZ 어디든 뜰 수 있기 때문에 AZ 불일치가 빈번하게 발생했다.
PVC(EBS) 방식의 추가 문제점#
| 문제 | 설명 |
|---|---|
| AZ 고정 | EBS는 단일 AZ에 바인딩 → Spot 회수 시 마운트 실패 |
| 비용 | gp3 100GB = ~$8/월, 데이터 증가 시 디스크 확장 필요 |
| 용량 한계 | 디스크 가득 차면 Pod CrashLoop, 수동 확장 필요 |
| 데이터 보존 | Pod/노드 장애 시 복구 복잡 |
해결#
로그(Loki), 트레이스(Tempo), 장기 메트릭(Thanos) 모두 S3를 영구 저장소로 사용하는 구조로 설계했다.
S3 vs PVC 비교#
| 항목 | PVC (EBS) | S3 |
|---|---|---|
| 비용 | gp3 100GB = ~$8/월 | 100GB = ~$2.3/월 |
| 용량 | 고정, 수동 확장 | 무제한 |
| AZ 제약 | 단일 AZ 바인딩 | 리전 내 어디서든 접근 |
| Spot 호환 | AZ 불일치 위험 | 영향 없음 |
| Lifecycle | 수동 관리 | 자동 (Glacier 전환, 만료 삭제) |
| 내구성 | 99.999% | 99.999999999% (11 9s) |
도구별 S3 적재 구조#
1. Loki (로그)#
[앱 Pod] → stdout
→ [Promtail/Alloy] → 로그 수집
→ [Loki Ingester] → 메모리에 청크 버퍼링 (WAL)
→ 일정 시간/크기 도달 → S3에 청크 업로드
- Ingester가 로그를 메모리에 모아뒀다가 일괄로 S3에 flush
- PVC는 WAL(Write-Ahead Log) 용도로 최소한만 사용하거나 미사용
- 쿼리 시 S3에서 직접 읽어옴 (Querier 컴포넌트)
# Loki S3 설정
loki:
storage:
type: s3
s3:
region: ap-northeast-2
bucketnames: goormgb-loki-chunks
2. Tempo (트레이스)#
[앱 Pod] → OTLP (gRPC:4317)
→ [OTel Collector] → 트레이스 수집
→ [Tempo Ingester] → 메모리에 블록 버퍼링 (WAL)
→ 일정 시간 도달 → S3에 블록 업로드
- Loki와 거의 동일한 패턴
- 트레이스 데이터를 블록 단위로 S3에 저장
- 검색 시 S3에서 읽어옴
# Tempo S3 설정
tempo:
storage:
trace:
backend: s3
s3:
bucket: goormgb-tempo-traces
region: ap-northeast-2
3. Thanos (메트릭 장기 보관)#
Thanos는 Loki/Tempo와 다른 패턴이다. Prometheus 자체는 PVC를 사용하고, Thanos Sidecar가 TSDB 블록을 S3에 복사한다.
[Prometheus] → 로컬 TSDB (PVC, 최근 수일간 보관)
→ [Thanos Sidecar] → 2시간마다 TSDB 블록을 S3에 업로드
쿼리 시:
[Thanos Query]
→ 최근 데이터: Prometheus 직접 조회 (Sidecar 경유)
→ 과거 데이터: S3에서 조회 (Thanos Store Gateway 경유)
- Prometheus는 여전히 PVC를 사용 (최근 데이터 저장 + WAL)
- Thanos Sidecar가 완성된 TSDB 블록을 S3에 업로드
- 과거 데이터 쿼리는 Thanos Store Gateway가 S3에서 읽어서 응답
# Thanos S3 설정 (Prometheus values)
prometheus:
prometheusSpec:
thanos:
objectStorageConfig:
secret:
type: S3
config:
bucket: goormgb-thanos-metrics
region: ap-northeast-2
데이터 흐름 전체 구조#
┌─── S3: goormgb-loki-chunks ───┐
[Loki Ingester] ───▶ │ 로그 청크 │
│ Lifecycle: 30d → Glacier │
│ 400d → 삭제 │
└─────────────────────────────────┘
┌─── S3: goormgb-tempo-traces ──┐
[Tempo Ingester] ───▶ │ 트레이스 블록 │
│ Lifecycle: 30d → Glacier │
│ 400d → 삭제 │
└─────────────────────────────────┘
┌─── S3: goormgb-thanos-metrics ─┐
[Thanos Sidecar] ───▶ │ TSDB 블록 (2h 단위) │
│ Lifecycle: 30d → Glacier │
│ 400d → 삭제 │
└─────────────────────────────────┘
S3 Lifecycle 정책#
비용 최적화를 위해 S3 Lifecycle 정책을 적용했다:
| 기간 | 스토리지 클래스 | 비용 (100GB 기준) |
|---|---|---|
| 0~30일 | S3 Standard | ~$2.3/월 |
| 30~400일 | S3 Glacier | ~$0.4/월 |
| 400일 이후 | 삭제 | $0 |
최근 데이터는 빠르게 조회할 수 있고, 오래된 데이터는 Glacier로 자동 전환되어 비용을 절감한다.
S3 접근 권한: IRSA#
Pod에서 S3에 접근할 때 정적 AWS Access Key가 아닌 IRSA(IAM Roles for Service Accounts)를 사용한다.
[Loki Pod]
→ ServiceAccount: loki-sa
→ annotation: eks.amazonaws.com/role-arn: arn:aws:iam::role/loki-irsa
→ IAM Role: S3 PutObject/GetObject 권한만 부여
- 정적 키 없이 OIDC 기반으로 임시 자격증명 발급
- Pod별 최소 권한 원칙 적용
- 키 로테이션 불필요
정리#
| 도구 | 로컬 저장 (PVC) | S3 저장 | 적재 방식 |
|---|---|---|---|
| Loki | WAL만 (최소) | 로그 청크 전체 | Ingester가 직접 S3에 flush |
| Tempo | WAL만 (최소) | 트레이스 블록 전체 | Ingester가 직접 S3에 flush |
| Thanos | Prometheus PVC (최근 데이터) | TSDB 블록 복사 | Sidecar가 2시간마다 업로드 |
S3 적재 방식을 선택한 핵심 이유:
- Staging Spot 인스턴스 환경에서 PVC AZ 불일치 문제 회피
- 데이터 증가에 따른 디스크 확장 운영 부담 제거
- EBS 대비 약 70% 비용 절감 (Lifecycle 정책 적용 시)
- 11 9s 내구성으로 데이터 안전성 확보