2026년에 DeepSeek와 Qdrant로 프로덕션급 RAG를 구축한 방법
요약
DeepSeek와 Qdrant를 활용하여 지연 시간과 비용 문제를 해결한 프로덕션급 RAG 아키텍처 구축 사례를 소개합니다. 기존 스택의 높은 비용과 지연 시간을 극복하고, 멀티 리전 배포와 안정적인 성능을 확보하는 실전적인 방법을 다룹니다.
핵심 포인트
- DeepSeek와 Qdrant 조합을 통한 RAG 성능 최적화
- p99 지연 시간을 5초에서 2초 미만으로 단축
- 토큰당 비용을 획기적으로 절감하여 운영 효율성 증대
- 멀티 리전 배포를 통한 시스템 가용성 및 안정성 확보
2026년에 DeepSeek와 Qdrant로 프로덕션급 RAG를 구축한 방법
모든 일의 시작이었던 새벽 3시의 Slack 알림이 아직도 기억납니다. 우리의 검색 증강 생성 (RAG, Retrieval-Augmented Generation) 파이프라인은 429 에러를 내뱉고 있었고, p99 지연 시간 (latency)은 8초를 넘어섰으며, 예산은 마치 땔감처럼 빠르게 소진되고 있었습니다. 그 사건은 제가 여러분께 소개해 드릴 재작성의 촉매제가 되었습니다. 현재 우리는 자동 확장 (auto-scaling), 99.9% 가동 시간 (uptime) 목표, 그리고 마침내 재무팀이 데일리 스탠드업 미팅에서 저를 눈치 보지 않게 만든 비용 프로필을 갖춘 DeepSeek와 Qdrant 스택을 여러 리전에 걸쳐 운영하고 있습니다.
이 글은 이론적인 내용이 아닙니다. 이 포스트에 담긴 모든 수치, 모든 벤치마크, 모든 코드 라인은 실제 프로덕션 트래픽에서 나온 것입니다. 무엇이 효과적이었는지, 무엇이 저를 힘들게 했는지, 그리고 엔터프라이즈 RAG 시스템을 위해 어떻게 가능한 한 가장 '지루할 정도로 안정적인' 상태를 유지했는지 보여드리겠습니다.
기존 스택을 거의 다 태워버릴 뻔했던 이유
새로운 아키텍처에 대해 설명하기 전에 배경을 먼저 말씀드리겠습니다. 우리는 인기 있는 관리형 벡터 서비스 (managed vector service)와 유명 제공업체의 플래그십 모델을 사용하여 완벽하게 작동하는 RAG 파이프라인을 운영하고 있었습니다. 서류상으로는 모든 것이 건강해 보였습니다. 하지만 실제로는 우리의 대시보드가 다른 이야기를 하고 있었습니다.
기존 설정은 임베딩 및 생성 (embedding-and-generation) 단계에서 평균 약 1.2초의 지연 시간을 기록했는데, 이는 네트워크 홉 (network hops), 검색 시간 (retrieval time), 후처리 (post-processing)를 모두 합치기 전까지는 괜찮게 들릴 수 있습니다. 엔드 투 엔드 (end-to-end) p99는 5초를 넘어서고 있었습니다. 처리량 (throughput)은 부하 상황에서 초당 약 320 토큰을 맴돌았는데, 이는 데모용으로는 훌륭하지만 업무 시간 동안 수천 명의 동시 접속자를 처리해야 할 때는 끔찍한 수준입니다. 내부 평가 하네스 (eval harness)로 측정한 품질은 도메인 특화 벤치마크 세트에서 평균 84.6%를 기록했습니다.
그다음은 청구서였습니다. 매달 마치 작은 장례식을 치르는 기분이었습니다. 솔직히 말해서, 더 저렴한 대안들이 똑같이 잘 해낼 수 있는 일을 수행하는 모델에 대해 우리는 100만 출력 토큰(output tokens)당 10달러가 넘는 비용을 지불하고 있었습니다. 저는 대안을 찾기 시작했고, 논의의 흐름을 완전히 바꿔놓은 무언가를 발견했습니다. Global API는 단일 통합 SDK를 통해 184개의 AI 모델을 노출하며, 가격은 100만 토큰당 0.01달러에서 3.50달러 사이였습니다. 오타가 아닙니다. 하나의 엔드포인트, 모든 주요 제공업체, 그리고 우리 재무 이사가 신체적으로 안도감을 느낄 정도의 가격이었습니다.
마침내 우리의 SLO를 만족시킨 아키텍처
시스템을 재설계하기 위해 앉았을 때, 저에게는 타협할 수 없는 세 가지 조건이 있었습니다.
- 멀티 리전 배포 (Multi-region deployment): 특정 지역의 장애가 고객 서비스 중단으로 이어지지 않도록 합니다.
- 검색 및 생성 핫 패스 (retrieval-plus-generation hot path)의 p99 지연 시간(latency) 2초 미만.
- 측정 가능한 품질 저하 없이 최소 40%의 비용 절감.
새로운 아키텍처는 깔끔하게 네 개의 계층으로 나뉩니다.
- 에지 계층 (Edge layer): 상태 인식 라우팅 (health-aware routing) 기능을 갖추고 사용자 근처에서 TLS를 종료하는 글로벌 API 게이트웨이.
- 검색 계층 (Retrieval layer): 교차 리전 복제 (cross-region replication)를 지원하며 세 개의 리전에 배포된 Qdrant 클러스터로, 얇은 캐싱 프록시 (caching proxy) 뒤에서 서비스됩니다.
- 생성 계층 (Generation layer): Global API 통합 엔드포인트를 통해 호출되는 DeepSeek 모델로, 리전 페일오버 (regional failover) 및 웜 폴백 큐 (warm fallback queue)를 갖추고 있습니다.
- 관측성 계층 (Observability layer): 분산 트레이싱 (distributed tracing), 실시간 p99 대시보드, 그리고 SLO 소진율 (burn rates)과 연동된 자동 알림.
제가 실제로 구축한 순서대로 각각에 대해 말씀드리겠습니다.
적절한 모델 선택하기 (그리고 왜 내가 GPT-4o를 사랑하기를 그만두었는지)
솔직히 말씀드리면, GPT-4o는 훌륭한 모델입니다. 그렇지 않다고 부정하지는 않겠습니다. 하지만 제가 직접 앉아서 품질 대비 비용 (cost-per-quality) 분석을 수행했을 때, 결과 수치는 우리의 워크로드(workload)에 대해 그 지출을 정당화하지 못했습니다. Global API를 통해 128K 컨텍스트 윈도우 (context window)를 사용할 때, GPT-4o는 입력 토큰 100만 개당 $2.50, 출력 토큰 100만 개당 $10.00의 비용이 발생합니다. 답변의 대부분이 검색된 컨텍스트 (retrieved context)에 포함되는 검색 집약적인 (retrieval-heavy) 대용량 워크로드의 경우, 이러한 가격 모델은 불균형적으로 큰 부담을 줍니다.
저는 네 가지 대안을 진지하게 검토했습니다. 다음은 제 아키텍처 리뷰 자료(architecture review deck)에 포함된 비교 테이블입니다:
| 모델 | 입력 ($/M) | 출력 ($/M) | 컨텍스트 (Context) |
|---|---|---|---|
| DeepSeek V4 Flash | 0.27 | 1.10 | 128K |
| ... |
여기서 핵심은 DeepSeek V4 Flash가 우리의 워크로드 기준으로 GPT-4o보다 입력 비용은 약 9배, 출력 비용은 약 9배 더 저렴했다는 점입니다. 우리는 이를 내부 평가 세트 (internal eval set)로 벤치마킹했으며, 트래픽의 대부분을 차지하는 검색 기반 질문 (retrieval-grounded questions)에서 대등하거나 약간 더 나은 품질을 확인했습니다. 그것만으로도 제가 결정을 내리기에는 충분했습니다.
일부 고객들이 프롬프트 (prompts)에 계약서 전체를 집어넣기 때문에 더 큰 컨텍스트 윈도우가 필요한 경우 — 실제로 필요합니다 — 우리는 200K 컨텍스트를 제공하는 DeepSeek V4 Pro를 사용합니다. 0.55/2.20의 가격은 여전히 이전에 지불하던 비용의 아주 일부분에 불과합니다.
코드: 멀티 리전 RAG 파이프라인 구축하기
제가 실제로 배포한 코드를 보여드리고 싶습니다. 첫 번째 스니펫은 Global API 엔드포인트(endpoint)를 대상으로 하는 핵심 생성 호출 (generation call)을 보여줍니다. 이것은 큐 깊이 (queue depth)와 p95 지연 시간 (p95 latency)을 기반으로 Kubernetes HPA를 통해 오토스케일링 (auto-scaled)되는 우리의 생성 워커 (generation worker) 내부에서 실행되는 코드입니다.
import openai
import os
import time
...
여기서 짚고 넘어갈 몇 가지 사항이 있습니다. 첫째, 관측성 계층 (observability layer)이 grep 없이도 적절한 p99 지연 시간 (latency) 대시보드를 구축할 수 있도록 모든 호출을 구조화된 필드 (structured fields)로 로깅하고 있습니다. 둘째, 이 작업은 검색 기반 워크로드 (retrieval-grounded workload)이므로 temperature를 낮게 고정했습니다. 창의적인 해석이 아닌, 결정론적이고 충실한 답변을 원하기 때문입니다. 셋째, 모델 이름 문자열은 Global API가 기대하는 정확한 식별자입니다. 초기에 사람이 읽기 쉬운 이름을 전달하느라 상당히 많은 시간을 허비했습니다.
두 번째 스니펫은 Qdrant를 사용한 검색 측 흐름을 보여주며, 여기에는 생성 비용을 40% 절감해 준 캐시 계층 (cache layer)이 포함되어 있습니다.
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
import hashlib
...
이 계층의 캐시 히트율 (cache hit rate)은 정말 큰 도움이 되었습니다. 프로덕션 트래픽 기준으로 약 40%를 유지하고 있으며, 이는 비용 절감으로 거의 직결됩니다. 캐시된 검색은 임베딩 (embedding) 호출을 완전히 건너뛰고 미리 가져온 컨텍스트 (context)를 생성 계층에 제공하기 때문입니다.
지연 시간(Latency) 및 SLA: 실제로 중요한 수치들
많은 벤더 블로그들이 이 부분을 미화한다고 생각하기에, 지난 30일간의 솔직한 프로덕션 수치를 말씀드리겠습니다.
평균 엔드 투 엔드 (end-to-end) 지연 시간: 검색과 생성을 합쳐 1.2초.
처리량 (Throughput): 생성 워커 (generation worker)당 지속적으로 초당 320 토큰, 수평적 오토스케일링 (horizontal auto-scaling)을 통해 스파이크 대응.
품질 (Quality): 내부 평가 스위트 (eval suite) 전반에 걸쳐 평균 벤치마크 점수 84.6%.
가동 시간 (Uptime): 지난 90일 동안 99.94%, 유일한 장애는 우리가 직접 유발한 지역적 Redis 페일오버 (failover) 동안 발생한 4분간의 구간뿐이었습니다.
p99 이야기는 더 흥미롭습니다. 생성 호출만 따졌을 때, 우리의 p99는 약 1.8초입니다. 검색, 네트워크, 후처리 (post-processing)를 포함한 엔드 투 엔드 p99는 2.4초입니다. 이는 요청의 99%에 대해 설정한 3초라는 SLO (Service Level Objective) 범위 내에 충분히 들어오며, 향후 기능을 추가하더라도 즉시 SLO를 위반하지 않고 수용할 수 있는 여유 공간을 제공합니다.
p99 측면에서 가장 도움이 되었던 것은 두 가지였습니다. 첫째, 검색 (Retrieval)과 생성 (Generation)을 독립적인 오토스케일링 그룹 (Auto-scaling groups)으로 분리하여, 느린 임베딩 (Embedding) 호출이 생성 단계의 백프레셔 (Backpressure)로 이어지지 않도록 했습니다. 둘째, 하프 오픈 (Half-open) 상태를 가진 서킷 브레이커 (Circuit breaker)를 구현하여, 특정 리전 (Region)의 성능이 저하되기 시작할 때 p99가 폭증하기 전에 트래픽이 전환되도록 했습니다. 클라우드 아키텍트의 고전적인 교훈입니다: 꼬리 (Tail)를 보호하면, 중앙값 (Median)은 알아서 관리됩니다.
멀티 리전 및 오토스케일링: 현재 설정
세 개의 리전 (us-east, eu-west, ap-southeast)에 걸친 배포 구성은 다음과 같습니다:
- Qdrant: 리전당 하나의 클러스터 (Cluster)를 운영하며, 읽기 복제본 (Read replicas)을 위한 비동기 복제 (Async replication)와 5초의 RPO (Recovery Point Objective)를 적용합니다. 쓰기 (Write) 작업은 테넌트 (Tenant)별로 홈 리전에 고정되며, 읽기 (Read) 작업은 어디서든 수행될 수 있습니다.
- Generation: 리전별 로드 밸런서 (Load balancer) 뒤에 배치된 상태 비저장 (Stateless) 워커 (Workers)입니다. 이들은 상위 모델 라우팅 (Upstream model routing)을 처리해 주는 글로벌 API (Global API)를 호출합니다. 오토스케일링 (Auto-scaling)은 큐 깊이 (Queue depth)가 50을 초과하거나 p95 지연 시간 (Latency)이 1.5초를 초과할 때 트리거됩니다.
- Cache: 웜 키 (Warm keys)를 위한 교차 리전 복제 (Cross-region replication) 기능이 포함된 리전별 Redis 클러스터 (Cluster)를 사용합니다.
- API Gateway: 리전별 200ms p95 지연 시간을 기준으로 가중치가 부여된, 상태 인식 라우팅 (Health-aware routing) 기능을 갖춘 글로벌 애니캐스트 (Global anycast)를 사용합니다.
오토스케일링 정책은 제가 가장 많이 반복해서 개선한 부분이었습니다. 초기 버전은 CPU를 기준으로 공격적으로 스케일링했는데, 이는 실수였습니다. 병목 현상은 로컬 컴퓨팅이 아니라 거의 항상 LLM 호출 지연 시간에서 발생하기 때문입니다. 지연 시간 및 큐 기반 스케일링 (Latency-and-queue-based scaling)으로 전환한 이후, 화요일 오전의 트래픽 급증 시기에도 p99가 극적으로 안정화되었습니다.
장애 조치 (Failover)를 위해, 저는
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기