들어가며: 장애 상황에서도 작동해야 하는 모니터링
모니터링 시스템은 장애가 발생했을 때 '무엇이 고장 났는지', '왜 그런지'를 알려주는 역할을 합니다. 그런데 만약 모니터링 시스템 자체가 장애가 난 인프라에 의존하고 있다면? 대시보드는 깜깜해지고, 알림은 오지 않으며, 장애 복구 도구가 오히려 장애의 일부가 됩니다.
에어비앤비도 이 문제를 겪었습니다. 수천 개 서비스가 공유 인프라(쿠버네티스, 서비스 메시) 위에서 돌아가는 환경에서, 메트릭 파이프라인이 바로 그 공유 인프라에 의존하고 있었기 때문입니다. 즉, **순환 의존성(Circular Dependency)**이 존재했습니다.
이 글에서는 에어비앤비가 어떻게 이 순환 의존성을 식별하고, 컴퓨팅/네트워킹/메타 모니터링 각 레이어에서 단절했는지 상세히 다룹니다.
참고: 이 글은 에어비앤비 엔지니어링 블로그의 내용을 기반으로 실무 적용 관점에서 재구성했습니다.
![]()
순환 의존성의 함정: 모니터링이 모니터링 대상을 의존할 때
에어비앤비의 옵저버빌리티 팀이 직면한 문제는 명확했습니다. 메트릭 파이프라인이 동일한 쿠버네티스 클러스터와 서비스 메시(Istio) 위에서 동작했고, 이는 곧 모니터링 대상 시스템이 다운되면 모니터링도 함께 다운되는 구조였습니다.
해결책: 세 가지 레이어에서의 의존성 제거
1. 컴퓨팅 레이어: 전용 쿠버네티스 클러스터
에어비앤비는 두 가지 극단적인 선택지 사이에서 절충안을 찾았습니다.
- 공용 프로덕션 클러스터 사용: 운영 부담은 적지만 순환 의존성 발생
- 자체 쿠버네티스 클러스터 운영: 완전한 격리는 가능하지만 운영 오버헤드 큼
선택한 해결책: 옵저버빌리티 워크로드만을 위한 전용 쿠버네티스 클러스터를 Cloud 팀이 관리하도록 했습니다. 이 클러스터는 다른 애플리케이션과 공유되지 않지만, 운영은 여전히 Cloud 팀이 담당합니다. 이를 통해 격리성과 관리 효율을 동시에 잡았습니다.
# 예시: 옵저버빌리티 전용 노드 풀 구성 (개념 코드)
# nodeSelector를 사용해 메트릭 수집 파드는 전용 노드에서만 실행
apiVersion: v1
kind: Pod
metadata:
name: prometheus-server
spec:
nodeSelector:
dedicated: observability
containers:
- name: prometheus
image: prom/prometheus
2. 네트워킹 레이어: 서비스 메시로부터의 독립
에어비앤비는 Istio 서비스 메시를 사용 중이었는데, 옵저버빌리티 트래픽은 비즈니스 트래픽과 성격이 완전히 달랐습니다.
- 트래픽 규모: 옵저버빌리티 트래픽이 비즈니스 트래픽보다 수 배에서 수십 배 더 많음
- 우선순위: 장애 상황에서 메트릭이 가장 중요하지만, 서비스 메시는 비즈니스 트래픽에 최적화됨
- 리스크: 텔레메트리 급증 시 공유 대역폭을 소비해 비즈니스 트래픽에 영향
해결책: Envoy 기반의 커스텀 L7 네트워크 인그레스 레이어를 구축했습니다. 이 프록시는 서비스 메시와 완전히 독립적으로 동작하며, 다음과 같은 기능을 제공합니다.
- 헤더 기반 라우팅: 각 서비스 이름을 특정 클러스터 백엔드로 매핑
- 트래픽 미러링: 테스트 목적으로 메트릭을 대체 목적지로 복사
- 세분화된 접근 제어: 외부 벤더나 특수 사용 사례에 대한 통제 가능
// Go를 사용한 커스텀 로드 밸런서 라우팅 로직 (개념 코드)
// 각 요청의 Tenant 헤더를 기반으로 적절한 백엔드 클러스터로 라우팅
func routeRequest(req *http.Request) *backend.Cluster {
tenant := req.Header.Get("X-Tenant-Id")
if cluster, ok := tenantClusterMap[tenant]; ok {
return cluster
}
// 기본 클러스터로 폴백
return defaultCluster
}
3. 메타 모니터링: 모니터를 모니터링하다
마지막으로, 모니터링 시스템 자체의 상태를 감시하는 레이어를 추가했습니다.
- 전용 Prometheus 인스턴스: 옵저버빌리티 스택과 다른 가용존, 다른 노드에 배치
- 고가용성 구성: Prometheus-Alertmanager 쌍이 동일한 인프라에 배치되지 않도록 설계
- Dead Man's Switch: 지속적으로 신호를 보내는 메커니즘. 신호가 끊기면 외부(CloudWatch Alarm)가 온콜을 호출
# Dead Man's Switch 알림 규칙 예시 (개념 코드)
groups:
- name: deadmansswitch
rules:
- alert: DeadMansSwitch
expr: vector(1)
labels:
severity: none
annotations:
description: "이 알림은 항상 발생합니다. 신호가 끊기면 문제가 발생한 것입니다."
이를 통해 '모니터링을 모니터링하는' 무한 재귀에 빠지지 않으면서도, 모니터링 스택의 침묵 장애(Silent Failure)를 감지할 수 있습니다.

한국 개발 생태계에서의 적용 맥락
국내 SI나 스타트업 환경에서 이 아키텍처를 그대로 도입하기는 어려울 수 있습니다. 하지만 핵심 원칙은 충분히 적용 가능합니다.
- 소규모 팀: 전용 클러스터 대신 Namespace + NodeSelector 수준의 격리부터 시작하세요. Cloud 팀이 없다면, 운영 부담을 최소화하는 방향으로 절충해야 합니다.
- 서비스 메시 미사용 환경: 서비스 메시가 없다면 오히려 네트워킹 레이어의 독립성은 덜 중요할 수 있습니다. 대신 **메트릭 수집 파이프라인 자체의 SPOF(Single Point of Failure)**를 제거하는 데 집중하세요.
- 메타 모니터링: Dead Man's Switch는 AWS CloudWatch 외에도 PagerDuty, Slack Webhook 등으로 대체 가능합니다. 중요한 것은 '신호가 끊겼을 때 알림이 가는' 메커니즘을 만드는 것입니다.
이 기술의 한계 및 주의사항
- 운영 복잡도 증가: 전용 클러스터와 커스텀 프록시 레이어는 분명히 운영 부담을 늘립니다. 에어비앤비 규모가 아니라면 오버엔지니어링이 될 수 있습니다.
- 비용: 전용 인프라는 추가 비용을 발생시킵니다. '가용성 vs 비용' 트레이드오프를 명확히 해야 합니다.
- Dead Man's Switch의 한계: 외부 서비스(AWS SNS, CloudWatch) 자체에 장애가 발생하면 알림이 누락될 수 있습니다. 완벽한 해결책은 없으며, 여러 채널을 사용하는 것이 바람직합니다.
함께 보면 좋은 글
- Netflix가 Spinnaker에서 Temporal로 클라우드 배포 실패율을 4%에서 0.0001%로 낮춘 방법
- 스포티파이가 수천 개 레포지토리에 PR을 자동 생성하는 법: Context Engineering의 모든 것

결론: 모니터링은 관찰 대상보다 더 안정적이어야 한다
에어비앤비의 사례가 주는 핵심 교훈은 단순합니다.
"모니터링 시스템은 자신이 관찰하는 시스템보다 더 높은 가용성을 가져야 한다."
이를 위해 필요한 것은:
- 의존성 매핑: 모니터링 파이프라인이 어떤 인프라에 의존하는지 명확히 파악
- 의도적인 장애 도메인 격리: 공유 인프라에 의존하지 않는 독립적인 경로 확보
- 메타 모니터링: 모니터링 자체의 상태를 감시하는 메커니즘 구축
장애 상황에서도 시야를 잃지 않는 모니터링 시스템은 단순한 편의가 아니라, 비즈니스 신뢰도를 지키는 필수 요소입니다. 여러분의 조직에서도 지금 당장 '모니터링 파이프라인의 순환 의존성'을 점검해보세요.
다음 단계 학습 방향
- 관련 기술: Prometheus Operator, Thanos, Cortex, Grafana Mimir (대규모 메트릭 파이프라인)
- 개념 심화: Observability: The New Wave, Google SRE Book
- 실습: 자신의 프로젝트에서
kubectl top과Prometheus를 사용해 메트릭 파이프라인의 의존성을 추적해보세요.