명백하지 않은 장애가 진짜 적이다
서버가 완전히 다운되면 오히려 대응이 쉽다. 알림이 울리고, 인스턴스를 재시작하거나 교체하면 된다. 문제는 부분적으로, 간헐적으로 발생하는 장애다. AWS가 수백만 개의 Kubernetes 클러스터를 운영하며 얻은 가장 큰 교훈이 바로 이것이다. 특정 가용 영역(Availability Zone)이 완전히 죽는 게 아니라, 네트워크 지연이 늘어나거나 일부 요청만 실패하거나, 헬스체크는 통과하지만 실제 트래픽 처리는 망가진 상태 — 이런 "회색 지대 장애(gray failure)"가 실제 운영에서 훨씬 자주, 그리고 훨씬 오래 방치된다.
분산 시스템에서 이런 장애가 위험한 이유는 관측하기 어렵기 때문이다. 메트릭은 정상 범위 안에 있고, 로그도 뚜렷한 에러를 보여주지 않는다. 그러나 특정 존(zone)으로 라우팅된 요청의 P99 레이턴시만 슬금슬금 올라가고 있다. 이 신호를 놓치면 장애는 조용히 확산된다.
존(Zone) 단위 탄력성 설계가 필요한 이유
Kubernetes 환경에서 멀티 AZ 구성은 기본 중의 기본이다. 그러나 단순히 파드를 여러 존에 분산시키는 것만으로는 부족하다. 핵심은 특정 존이 "약해졌을 때" 해당 존으로의 트래픽을 신속하게 격리하거나 우회할 수 있는 메커니즘을 갖추는 것이다.
AWS EKS의 Zonal Shift 기능은 이런 맥락에서 나왔다. 존 전체가 완전히 장애 상태가 아니더라도, 운영자가 특정 AZ를 로드밸런서 라우팅에서 즉시 제외할 수 있게 한다. 문제가 의심되는 존을 선제적으로 격리하고, 나머지 존에서 서비스를 유지하는 방식이다.
# Kubernetes Topology Spread Constraints 예시
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-service
파드 스케줄링 단계에서도 존 간 균등 분산을 강제하는 것이 기본 전제가 되어야 한다. 특정 존에 파드가 몰려 있으면, 그 존에 문제가 생겼을 때 영향 범위가 훨씬 커진다.
실무에서 회색 지대 장애를 다루는 관점
백엔드 개발자 입장에서 이 문제는 단순히 인프라 팀의 영역이 아니다. 애플리케이션 레벨에서도 다음을 고려해야 한다.
- 헬스체크의 정확성:
/health엔드포인트가 단순히 HTTP 200을 반환하는 것을 넘어, 실제 DB 커넥션·외부 의존성 상태를 반영하는지 검토해야 한다. - 타임아웃과 재시도 전략: 특정 존의 응답이 느려질 때, 무한정 기다리거나 같은 존에 재시도하는 것은 장애를 악화시킨다. 재시도 시 다른 존 인스턴스로 라우팅될 수 있도록 서킷 브레이커와 함께 설계해야 한다.
- 존 인식(zone-aware) 로드밸런싱: 클라이언트 사이드 로드밸런서(예: Spring Cloud LoadBalancer)에서 존 친화성(zone affinity)을 설정하되, 특정 존 장애 시 자동으로 다른 존으로 폴백하는 로직이 있는지 확인해야 한다.
// Spring Cloud LoadBalancer - Zone preference 설정 예시
spring:
cloud:
loadbalancer:
zone: ${ZONE:us-east-1a}
configurations: zone-preference
정리
- 완전한 장애보다 부분적·간헐적 장애(gray failure) 가 탐지와 대응이 훨씬 어렵고, 분산 시스템 설계의 핵심 난제다.
- 멀티 AZ 구성은 필요조건일 뿐, 특정 존을 빠르게 격리하고 트래픽을 우회하는 메커니즘까지 갖춰야 실질적인 탄력성이 확보된다.
- 헬스체크 정확성, 타임아웃/재시도 전략, 존 인식 로드밸런싱은 인프라가 아닌 애플리케이션 레벨에서도 책임져야 할 영역이다.