23. 원격 호스팅·관측성·레이트리밋·성능
🎯 이 장의 목표
- 호스팅 옵션(컨테이너 서비스·서버리스·게이트웨이)과 stateless 확장을 이해한다
- 관측성 3요소(로그·메트릭·트레이스)를 MCP 서버에 적용한다
- 레이트리밋·타임아웃·컨텍스트 비용으로 성능과 비용을 다스린다
22장에서 패키징했다면, 이제 운영입니다. 원격 서버는 죽거나 느려지거나 비싸질 수 있습니다 — 이를 막는 운영 기법을 봅니다.
호스팅 옵션
flowchart TD
Q{"워크로드 성격은?"}
Q -->|"가볍고 stateless,\n버스트 트래픽"| SL["서버리스\nLambda / Cloud Run / Workers"]
Q -->|"긴 연결·웜 캐시·\n사이드카 필요"| CT["컨테이너 서비스\nECS/Fargate / Kubernetes"]
Q -->|"서버 3~5개 초과"| GW["게이트웨이 패턴\n중앙 인증·라우팅·관측"]
classDef q fill:#bfdbfe,stroke:#1d4ed8,color:#000;
classDef opt fill:#5eead4,stroke:#0f766e,color:#000;
class Q q;
class SL,CT,GW opt;
- 서버리스(Lambda·Cloud Run·Workers): 가볍고 stateless한 도구 엔드포인트, 버스트 트래픽에 적합. 단 콜드스타트가 지속 연결·지연에 영향을 줄 수 있습니다.
- 컨테이너 서비스(ECS/Fargate·Kubernetes): 긴 연결, 웜 캐시, 사이드카, 의존성 무거운 워크로드에 적합. 런타임·네트워킹 제어가 큼.
- 각 플랫폼은 한 줄 배포 경로를 제공합니다(예:
gcloud run deploy, ECR 푸시 후 ECS 서비스 생성 등).
💡 팁
시작은 stateless로: 08장에서 본 대로, Streamable HTTP를 stateless 모드로 시작하면 각 호출이 독립적이라 레플리카를 자유롭게 늘릴 수 있어(세션 어피니티 불필요) 서버리스·로드밸런싱에 잘 맞습니다. 상태가 꼭 필요하면 Mcp-Session-Id로 stateful을 쓰되, 그만큼 확장이 까다로워집니다.게이트웨이 패턴 — 서버가 늘어날 때
서버가 3~5개를 넘으면 중앙 집중 관리가 사실상 필수가 됩니다. 한 플랫폼 팀은 "조직에 MCP 서버 14개, 그중 4개는 서로 모른 채 다른 팀이 만든 중복"이라 보고했습니다 — 2018년 마이크로서비스 난립과 같은 패턴입니다.
게이트웨이는 여러 MCP 서버 앞에 서서 통합 엔드포인트로 중앙 발견·인증·라우팅·관측·레이트리밋을 제공합니다. 21장에서 본 대로, 게이트웨이는 한 팀이 수천 에이전트에 단일 완화책을 동시에 밀어 넣을 수 있는 이음새이기도 합니다(폭발 반경 축소). 다만 클라이언트 패치와 벤더 위생을 대체하진 못합니다.
📌 핵심
인벤토리가 먼저: "보이지 않는 것은 지킬 수 없다"(21장). 게이트웨이든 별도 대장이든, 환경의 모든 서버·클라이언트를 목록화하는 것이 운영·보안의 출발점입니다.관측성 3요소
운영 가능한 서버는 무슨 일이 일어나는지 보입니다. 로그·메트릭·트레이스 세 축으로 접근합니다.
flowchart LR
SRV["MCP Server"] --> LOG["로그\n구조화 JSON\n(도구·인자·결과·에러)"]
SRV --> MET["메트릭\n지연·에러율·호출수\n(Prometheus)"]
SRV --> TR["트레이스\nOpenTelemetry\n(요청 흐름·업스트림)"]
LOG --> DASH["대시보드·알림\n(Grafana 등)"]
MET --> DASH
TR --> DASH
classDef s fill:#5eead4,stroke:#0f766e,color:#000;
classDef o fill:#bfdbfe,stroke:#1d4ed8,color:#000;
class SRV s;
class LOG,MET,TR,DASH o;
- 로그: 구조화 JSON 로깅으로 "어떤 도구가 무슨 인자로 불렸고 어떤 결과·에러였는지"를 남깁니다. 이는 21장의 감사 추적과 직결됩니다. stderr/프로토콜 로깅을 쓰고 stdout 오염 금지(07장), 민감정보는 정화(16장).
- 메트릭: 요청 지연, 에러율, 도구별 호출 수를 Prometheus 등으로 수집해 도구 이름별 평균 지연 같은 지표를 추적합니다.
- 트레이스: OpenTelemetry로 요청 흐름과 업스트림 호출을 추적합니다. 분산 환경에서 "어디서 느려지는지" 찾는 데 필수입니다. 여러 백엔드(Jaeger·Zipkin·Phoenix 등)로 내보낼 수 있습니다.
- 헬스 엔드포인트(
/health, 22장)로 준비 상태를 노출하고, 임계치 초과 시 알림을 겁니다.
🔒 로깅·모니터링 실패는 OWASP 항목: 어떤 도구가 무슨 인자로 불렸는지 감사 추적이 없으면, 사고를 탐지·조사할 수 없습니다(21장). 관측성은 성능뿐 아니라 보안의 일부입니다.
레이트리밋·타임아웃·취소
외부 API를 부르거나 비용이 큰 도구는 레이트리밋으로 남용을 막고 사용자 간 공정성을 지킵니다. 13장·06장에서 본 타임아웃·취소도 운영 차원에서 챙깁니다.
PYTHON
# (개념) 도구 레벨 레이트리밋 + 타임아웃 import asyncio, time from collections import defaultdict _calls = defaultdict(list) async def rate_limited(key: str, limit: int, window: float = 60.0): now = time.monotonic() _calls[key] = [t for t in _calls[key] if now - t < window] if len(_calls[key]) >= limit: raise RuntimeError("레이트리밋 초과") # 실행 에러로 처리 (16장) _calls[key].append(now) @mcp.tool() async def expensive_search(query: str) -> str: """비용이 큰 외부 검색 (분당 10회 제한).""" await rate_limited("expensive_search", limit=10) return await asyncio.wait_for(do_search(query), timeout=15.0) # 타임아웃
- 비용·자원 부담이 큰 도구에 레이트리밋을 둡니다.
- 명확한 타임아웃 임계치를 설정해 무한 대기를 방지하고, 가능하면 취소를 지원합니다(13장).
성능과 컨텍스트 비용
18장에서 본 "도구 스키마가 컨텍스트를 먹는다"는 운영 비용으로 직결됩니다. 토큰은 곧 돈이고 지연입니다.
- 도구는 적게·명확하게, 출력은 요약·페이지네이션으로 컨텍스트 친화적으로(18장).
- I/O는 async로 이벤트 루프를 막지 않게(15장). 동시 요청이 많은 원격 서버에서 특히 중요.
- 캐싱·웜 상태가 도움이 되는 워크로드는 컨테이너 서비스(웜 캐시)를 고려.
- 큰 데이터는 통째로 반환하지 말고 리소스 링크나 페이지네이션으로.
| 운영 관심사 | 기법 | 관련 장 |
|---|---|---|
| 확장 | stateless + 레플리카, 게이트웨이 | 08장 |
| 가시성 | 구조화 로그·Prometheus·OTel | 16장, 21장 |
| 남용 방지 | 레이트리밋·타임아웃·취소 | 13장 |
| 비용·지연 | 도구 절제·async·캐싱·요약 | 18장 |
이 장에서 배운 것
- 호스팅은 서버리스(가볍고 버스트) vs 컨테이너(긴 연결·웜 캐시), 서버가 많아지면 게이트웨이. stateless로 시작하면 확장이 쉽다.
- 관측성은 로그·메트릭·트레이스 3축 — OWASP가 짚듯 감사 추적은 보안이기도 하다. stdout 오염 금지, 민감정보 정화.
- 레이트리밋·타임아웃·취소로 남용과 무한 대기를 막는다.
- 도구 스키마·출력은 컨텍스트 비용이다 — 절제·async·요약·캐싱으로 성능과 비용을 다스린다.
✍️ 확인 문제
- 레플리카를 자유롭게 늘려 수평 확장하려면 어떤 모드로 시작하는 게 유리하고, 그 이유는?
- 관측성이 성능뿐 아니라 보안에도 중요한 이유를 OWASP 항목과 엮어 설명해 보자.
- 비용이 큰 외부 검색 도구에 적용할 운영 보호 장치 두 가지를 들어 보자.
다음 장: 24. 테스트 전략과 실제 사례 패턴