Observability는 선택이 아니라 필수다. 하지만 SaaS 모니터링 비용이 인프라 비용보다 높아지는 순간이 온다. 그때가 자체 구축을 고민할 타이밍이다.
Datadog을 메인으로 사용하고 있었지만, 모든 환경에 적용하기엔 비용 부담이 있었다. Observability가 아예 없는 환경이 생기는 것보다는, 오픈소스로라도 갖추는 게 낫다고 판단했다. LGTM 스택(Loki, Grafana, Tempo, Mimir)을 EKS에 구축한 과정을 공유한다.
배경
Datadog을 메인으로 사용하고 있지만, 모든 환경에 적용하기엔 비용 부담이 있었다. 특히 작은 규모의 계정들은 Observability가 아예 없는 상태로 운영되고 있었다.
Observability의 최종 목표는 MTTD(Mean Time To Detect)를 줄이는 것이다. 대시보드가 아무리 훌륭해도 24시간 지켜볼 수는 없다. Push 기반 알림이 있어야 문제를 빠르게 인지할 수 있다.
LGTM 스택에서 Mimir Ruler와 Alertmanager를 활용해 알림 시스템을 구축했다. 알림 룰을 GitOps로 관리하고, 알림 폭탄을 방지하기 위한 설계 포인트를 정리한다.
알림 아키텍처
flowchart LR
subgraph GitOps[GitOps 관리]
Git[Git Repository]
CM[ConfigMap]
end
subgraph Mimir[Mimir]
Ruler[Ruler<br/>PromQL 평가]
Storage[(Metrics<br/>Storage)]
AM[Alertmanager<br/>라우팅/그룹핑]
end
subgraph Grafana[Grafana]
AlertUI[Alerting UI<br/>알림 현황 조회]
end
subgraph Slack[Slack]
Ch1[#alerts-service]
Ch2[#alerts-infra]
end
Git -->|Helm Deploy| CM
CM -->|Alert Rules| Ruler
Storage -->|메트릭 쿼리| Ruler
Ruler -->|알림 발생| AM
AM -->|namespace: app| Ch1
AM -->|namespace: observability| Ch2
AM -->|알림 상태| AlertUI
Ruler가 주기적으로 메트릭을 쿼리하고, 조건이 충족되면 Alertmanager로 알림을 보낸다. Alertmanager는 알림을 그룹핑하고, 라우팅 규칙에 따라 적절한 Slack 채널로 전송한다. Grafana에서는 현재 발생 중인 알림을 조회하고 히스토리를 확인할 수 있다.
Liveness Probe는 죽은 Pod를 살리기 위한 것이다. 그런데 잘못 설정하면 멀쩡한 Pod를 죽이는 도구가 된다.
이론적으로는 알고 있었다. Kubernetes Probe 3종류에서도 “LivenessProbe는 단순하게"라고 정리했다. 그런데 실제로 겪고 나니 체감이 다르다. Karpenter 노드 교체와 무거운 Liveness Probe가 만나면서 연쇄 재시작이 발생했다. Datadog 메트릭으로 타임라인을 복원하고 원인을 분석한 과정을 공유한다.
증상
아침에 5xx 알람이 터졌다. Spring Boot API 서버에서 약 15분간 간헐적 에러가 발생했다.
컨테이너 오케스트레이션 플랫폼을 바꾸는 건, 비행 중에 엔진을 교체하는 것과 같다. 핵심은 “어떻게 전환할 것인가"다. 한 번에 스위칭하면 빠르지만 위험하고, 너무 느리면 두 환경을 동시에 운영하는 비용이 커진다.
플랫폼 서버를 ECS에서 EKS로 마이그레이션했다. Big Bang 전환 대신 가중치 기반 점진적 트래픽 전환 전략을 택했다. 99:1에서 시작해 0:100까지, 각 단계마다 모니터링하고 이상이 없을 때만 다음 단계로 넘어가는 방식이다. 결과적으로 에러 없이 무중단으로 전환을 완료했다. 그 과정을 공유한다.
EKS에서 Pod가 S3에 파일을 올리거나 SQS에 메시지를 보내려면 AWS 자격 증명이 필요하다. 예전에는 AWS Access Key를 환경변수나 ConfigMap에 넣어서 해결했다. 동작은 하지만, 키가 유출되면 누구나 해당 권한을 사용할 수 있고, 키 로테이션도 수동이다.
IRSA(IAM Roles for Service Accounts)는 이 문제를 근본적으로 해결한다. Kubernetes의 ServiceAccount와 AWS IAM Role을 OIDC 프로토콜로 연결해서, Pod에 임시 자격 증명을 자동으로 주입한다. 키를 코드에 넣을 필요가 없고, 토큰은 자동 갱신되며, 네임스페이스 단위로 권한을 분리할 수 있다.
K8s 환경에서 앱이 DB 비밀번호나 API 키 같은 시크릿을 사용하려면, 어딘가에서 가져와야 한다. 이 “어딘가"와 “가져오는 방식"에 따라 아키텍처가 달라진다.
크게 두 가지 접근이 있다. 앱이 AWS API를 직접 호출해서 읽는 방식과, K8s Operator가 대신 읽어서 환경변수로 넣어주는 방식이다. 전자는 Parameter Store 직접 읽기, 후자는 External Secrets Operator다. 각각의 동작 원리와 트레이드오프를 정리한다.
방식 1: 앱이 직접 읽기 (Parameter Store)
Spring Boot 기준으로, Spring Cloud AWS가 제공하는 Parameter Store 통합 기능을 사용하는 방식이다.