이 글은 시스템 복원력을 확보하기 위한 구체적인 구현 방안을 제시합니다.
1. 서킷 브레이커 패턴
-
구현:
CircuitBreakerRegistry를 통해 테넌트별 서킷 브레이커를 관리하며,Vectra::CircuitBreaker를 사용하여 실패 임계치, 복구 시간 등을 설정합니다. -
사용:
VectorIndexingService에서 Qdrant 쿼리 실행 시 서킷 브레이커를 적용하고, 실패 시 PostgreSQL 전체 텍스트 검색으로 폴백(fallback)하도록 구현하여 서비스 연속성을 확보합니다. -
상태:
CLOSED,OPEN,HALF_OPEN세 가지 상태로 동작하며,Admin::CircuitBreakersController를 통해 상태를 모니터링하고 관리합니다. -
효과: 블랙 프라이데이 트래픽 급증 시 Qdrant 장애에도 불구하고 서킷 브레이커가 작동하여 전체 Rails 앱 다운을 방지하고 99.2%의 검색 가용성을 유지했습니다.
2. 테넌트별 속도 제한 (Rate Limiting)
-
목적: 특정 테넌트의 과도한 요청으로 인한 플랫폼 전체의 서비스 거부(DOS) 공격을 방지합니다.
-
구현:
RateLimiterRegistry를 통해 테넌트별로 분당 500회 요청으로 제한하며,Vectra::RateLimiter를 사용합니다. -
사용:
VectorIndexingService에서 검색 요청 전에 속도 제한 토큰을 획득하며, 제한 초과 시429 Too Many Requests응답을 반환합니다. -
API 통합:
SearchController에서X-RateLimit-*헤더를 통해 클라이언트에게 속도 제한 정보를 제공합니다.
3. 헬스 체크
-
종합 헬스 체크 엔드포인트:
HealthController는 벡터 DB (Qdrant), 데이터베이스, Redis, 임베딩 서비스, Sidekiq 큐 상태를 점검하고 응답 지연 시간을 측정하여 시스템의 전반적인 상태를 보고합니다. -
Kubernetes 통합:
/health엔드포인트를 Kubernetes의livenessProbe및readinessProbe로 활용하여 컨테이너의 생존 및 서비스 준비 상태를 자동으로 관리합니다.
4. Prometheus 메트릭 및 모니터링
-
메트릭 정의:
config/initializers/prometheus.rb에서 벡터 검색 지연 시간, 총 검색 요청 수, 캐시 적중률, 서킷 브레이커 상태, 문서 처리 시간, Sidekiq 큐 크기 등 핵심 메트릭을 정의합니다. -
계측:
VectorIndexingService에 메트릭 계측 코드를 추가하여 검색 성공/실패, 캐시 적중 여부 등을 기록합니다. -
메트릭 엔드포인트:
/metrics엔드포인트를 통해 Prometheus가 수집할 수 있는 형식으로 모든 메트릭을 노출합니다.
5. Grafana 대시보드
- 주요 패널: 검색 성능 (P50, P95, P99 지연 시간), 처리량 (초당 요청 수, 테넌트별), 캐시 적중률, 서킷 브레이커 상태, 오류율 등을 시각화하여 시스템 상태를 한눈에 파악할 수 있도록 합니다.
이러한 복원력 패턴 구현 후, 시스템은 P95 검색 지연 시간 120ms (캐시 적중 시 8ms), 99.94%의 가용성, 평균 복구 시간 8분, 42%의 캐시 적중률을 달성했습니다. 서킷 브레이커는 2024년에 8번의 장애를 방지했으며, 속도 제한은 성공적인 DOS 공격을 막았습니다.