17-(2) Streamlit, FastAPI로 구성된 시스템에 Triton 기반 추론 서버를 통합하려면 어떤 구조로 설계하는 것이 바람직하다고 생각하나요?
🟢 Streamlit (UI), FastAPI (Backend Logic), Triton (Inference) 조합은 프로토타입부터 실제 서비스까지 확장할 수 있는 정말 강력하고 표준적인 구성
🟢 Intro
⚪ 0. 전체 아키텍처 개요
1
2
3
4
5
6
7
8
9
10
사용자 ─▶ Streamlit (Frontend UI)
│
▼
FastAPI (Backend Gateway)
│
▼
Triton Inference Server (Model Serving)
│
▼
GPU/CPU 모델
- Streamlit → UI + 사용자 입력/결과 표시
- FastAPI → 요청 관리, 전처리/후처리, 모델 버전 선택
- Triton → 추론 전용 서버
⚪ 1. 문제 정의: 세 개의 강력한 도구
최근 MLOps 환경에서 데모(UI), 백엔드(Logic), 추론(Inference)의 역할이 명확히 분리되고 있습니다.
- Streamlit (Frontend): Python 기반의 신속한 데이터 앱/데모 UI 구축 도구.
- FastAPI (Backend): 고성능 비동기 API 서버 구축을 위한 웹 프레임워크.
- Triton Inference Server (Inference): NVIDIA에서 개발한, 하드웨어(GPU/CPU)에 최적화된 고성능 추론 전용 서버.
이 세 가지를 조합할 때, 어떻게 설계해야 확장 가능하고(Scalable) 유지보수가 용이하며(Maintainable) 고성능(Performant) 시스템을 만들 수 있을까요?
⚪ 2. 안티 패턴 (Anti-Patterns)
먼저 피해야 할 두 가지 일반적인 함정이 있습니다.
A. 모놀리식 백엔드 (FastAPI + Model)
FastAPI 라우터 함수 내에서 model.run() (e.g., pytorch, onnxruntime)을 직접 호출하는 방식입니다.
- 문제점:
FastAPI는 비동기(async) I/O 처리에 최적화되어 있습니다. 하지만 딥러닝 추론(model.run())은 CPU/GPU를 점유하는 동기(Synchronous) 작업입니다. 이 무거운 작업이FastAPI의 이벤트 루프나 워커(Worker)를 ‘블로킹(Blocking)’시켜, 다른 모든 동시 요청을 대기 상태로 만듭니다. 이는 Triton의 Dynamic Batching, Concurrency 같은 장점을 전혀 활용하지 못하는 최악의 설계입니다. - 비유: 레스토랑에서 서빙 직원이 직접 주방에 들어가 요리까지 하는 것과 같습니다. 그 직원이 요리하는 동안 다른 테이블의 주문은 아무도 받지 못합니다.
B. UI-추론 직접 결합 (Streamlit -> Triton)
Streamlit 앱이 Triton 서버를 직접 호출하는 방식입니다.
- 문제점: 역할의 오염(Contamination of Concerns)이 발생합니다.
Streamlit은 UI 레이어입니다. 여기에 데이터 전처리(Pre-processing), 후처리(Post-processing), 사용자 인증, 요청 검증, 결과 로깅(Logging) 같은 비즈니스 로직이 섞이게 됩니다.- UI 코드가 복잡해지고, 나중에 모바일 앱이나 다른 백엔드 서비스가 이 Triton 모델을 재사용하고자 할 때, Streamlit에 종속된 전/후처리 로직을 다시 구현해야 합니다.
⚪ 3. 권장 아키텍처: Decoupled API Gateway Pattern
가장 바람직한 설계는 세 컴포넌트의 역할을 완벽히 분리(Decouple)하고, FastAPI가 중간에서 ‘API 게이트웨이’ 역할을 수행하도록 하는 것입니다.
아키텍처 흐름
- Client (User) $\rightarrow$ Streamlit (UI)
- 사용자가 UI와 상호작용합니다.
- Streamlit $\rightarrow$ FastAPI (API Gateway)
Streamlit은httpx.post()등을 사용해FastAPI의 엔드포인트(예:POST /api/predict)를 호출합니다. UI는 오직FastAPI의 존재만 알면 됩니다.
- FastAPI $\rightarrow$ Triton (Inference Server)
FastAPI는Streamlit에서 받은 원본 데이터를 Triton이 요구하는 Tensor 포맷으로 전처리(Pre-processing)합니다.FastAPI는tritonclient라이브러리(async 지원)를 사용하여Triton서버에 gRPC/HTTP로 추론을 요청합니다.
- Triton $\rightarrow$ FastAPI
Triton은 GPU 가속을 통해 최적화된 추론을 수행하고, Raw Tensor 결과를FastAPI에 반환합니다.
- FastAPI $\rightarrow$ Streamlit
FastAPI는Triton의 Raw Tensor를 받아 후처리(Post-processing)를 수행합니다. (e.g., 클래스 이름 매핑, JSON 직렬화)- 필요한 비즈니스 로직(e.g., DB 저장, 로깅)을 수행한 후, 최종 가공된 JSON을
Streamlit에 응답합니다.
- Streamlit $\rightarrow$ Client (User)
Streamlit은FastAPI로부터 받은 JSON 응답을 사용자 친화적인 차트, 텍스트, 이미지로 시각화합니다.
⚪ 4. 이 설계의 핵심 장점
A. 독립적인 확장성 (Independent Scaling)
이 아키텍처의 가장 큰 장점입니다. 각 컴포넌트는 서로 다른 자원(Resource)을 요구합니다.
- Streamlit (CPU/RAM): 동시 접속자 수에 따라 스케일링됩니다.
- FastAPI (CPU/RAM): 비즈니스 로직의 복잡성과 I/O 요청 수에 따라 스케일링됩니다.
- Triton (GPU/GPU-RAM): 추론 요청(RPS)과 모델 크기에 따라 스케일링됩니다.
Triton은 비싼 GPU 자원을 사용합니다. 만약 이들이 분리되지 않았다면, 단순히 UI 접속자가 늘었다는 이유만으로 비싼 GPU 서버를 불필요하게 증설해야 할 것입니다. 분리된 아키텍처는 Triton 컨테이너(GPU 사용)만 독립적으로 확장할 수 있게 하여 비용 효율성을 극대화합니다.
B. 성능 최적화 (Optimized Performance)
- FastAPI:
async/await를 통해 수천 개의 I/O(네트워크 통신)를 비동기적으로 처리하며 대기 시간을 최소화합니다. - Triton: C++ 기반 네이티브 서버가
Dynamic Batching,Concurrent Model Execution등FastAPI(Python) 레벨에서는 구현하기 어려운 고급 추론 최적화를 자동으로 수행합니다.
C. 유지보수 및 재사용성 (Maintainability & Reusability)
- UI 변경:
Streamlit코드를 수정하거나Gradio같은 다른 툴로 교체해도,FastAPI와Triton은 아무런 영향을 받지 않습니다. - 모델 변경:
Triton에v2모델을 배포해도,Streamlit은 이를 알 필요가 없습니다.FastAPI의 전/후처리 로직만 수정하면 됩니다. - 재사용성:
FastAPI가 제공하는POST /api/predict엔드포인트는 모바일 앱, 배치 스크립트 등 다른 클라이언트가 언제든 재사용할 수 있는 표준 API가 됩니다.
⚪ 5. Docker Compose 예시 (구현 아이디어)
이 구조는 docker-compose를 통해 쉽게 컨테이너화할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
version: '3.8'
services:
# 1. Frontend (UI)
streamlit_app:
build: ./streamlit
ports:
- "8501:8501"
# FastAPI의 내부 DNS 이름(fastapi_app)을 환경 변수로 주입
environment:
- API_GATEWAY_URL=http://fastapi_app:8000
networks:
- mla-network
# 2. Backend (API Gateway)
fastapi_app:
build: ./fastapi
ports:
- "8000:8000"
# Triton의 내부 DNS 이름(triton_server)을 환경 변수로 주입
environment:
- TRITON_SERVER_URL=triton_server:8001 # (gRPC 포트 기준)
networks:
- mla-network
# CPU만 사용
# 3. Inference Server (GPU)
triton_server:
image: nvcr.io/nvidia/tritonserver:24.09-py3
ports:
- "8000:8000" # (HTTP - FastAPI와 겹치므로 외부 포트 변경 필요)
- "8001:8001" # (gRPC)
- "8002:8002" # (Metrics)
volumes:
- ./model_repository:/models # 호스트의 모델을 컨테이너에 마운트
command: tritonserver --model-repository=/models
networks:
- mla-network
# NVIDIA GPU 사용 설정
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
networks:
mla-network:
driver: bridge
🟢 정리하면
Streamlit + FastAPI + Triton 통합 아키텍처
구조
[User] → Streamlit (UI) ↓ FastAPI (Backend Gateway) ↓ Triton Inference Server (Model Serving)
역할
| Component | 역할 | |————|——| | Streamlit | 입력/시각화 | | FastAPI | 전처리, 후처리, 로깅 | | Triton | GPU 추론, 멀티모델 관리 |
연결 방식
- Streamlit ↔ FastAPI: REST (HTTP)
- FastAPI ↔ Triton: gRPC (권장)
배포 방식
- Docker Compose (개발용)
- Kubernetes + KServe (운영용)
🟢 예시 답안 (코드잇 제공)
Streamlit, FastAPI, 그리고 Nvidia Triton Inference Server를 통합하여 AI 모델 서빙 시스템을 구성할 때는 각 구성 요소의 역할을 명확히 분리하고, 통신 구조를 효율적으로 설계하는 것이 중요합니다. 바람직한 설계 방식은 다음과 같이 세 가지 계층으로 나누어 생각할 수 있습니다.
첫 번째 계층은 사용자 인터페이스 계층으로, Streamlit이 담당합니다. 이 계층에서는 사용자로부터 입력을 받고 결과를 시각적으로 제공하는 역할을 합니다. 예를 들어, 이미지를 업로드하거나 텍스트를 입력하고, 그 결과를 그래프나 표로 출력하는 작업이 이곳에서 수행됩니다. Streamlit은 인터랙티브한 프론트엔드 역할에 집중하며, 복잡한 비즈니스 로직은 다음 계층에 위임합니다.
두 번째 계층은 애플리케이션 로직 계층으로, FastAPI가 중심입니다. 이 계층에서는 사용자 입력을 받아 전처리하고, Triton 서버에 추론 요청을 보낸 뒤, 응답을 받아 후처리하는 과정을 담당합니다. 즉, FastAPI는 Streamlit과 Triton 사이의 중간 허브 역할을 하며, 다양한 모델 요청을 통합하고 API 구조를 체계화하여 확장성과 유지보수성을 높입니다.
세 번째 계층은 모델 추론 계층으로, Nvidia Triton Inference Server가 담당합니다. Triton은 사전 학습된 모델을 고속으로 추론하기 위한 최적화된 서버이며, TensorFlow, PyTorch, ONNX 등 다양한 프레임워크 기반 모델을 동시에 운영할 수 있습니다. FastAPI는 Triton의 REST API 또는 gRPC 인터페이스를 통해 모델에 요청을 전달하고 응답을 받습니다. 이 구조 덕분에 모델 서빙과 웹 서버는 서로 독립적으로 운영되며, 리소스 분리와 확장성이 용이합니다.