반응형
1. Modal 기술이란?
1-1. 왜 필요한가 — 기존 방식의 한계
2023년 LLM 폭발 이후, AI 워크로드는 세 가지 동시 요구를 던지기 시작했습니다.
- 요청당 GPU가 필요하다. 단 한 번의 추론도 A100 또는 H100을 쥐어야 의미 있는 응답 시간이 나옵니다.
- 0에서 수백 컨테이너로 즉시 확장되어야 한다. 콜드 스타트가 분 단위면 사용자가 떠납니다.
- Python 한 파일로 끝나야 한다. ML 엔지니어는 Dockerfile·Helm chart·Karpenter NodePool을 짜고 싶어 하지 않습니다.
기존 솔루션들은 이 셋 중 하나 이상을 못 합니다.
| 기존 방식 | 못하는 것 |
|---|---|
| AWS Lambda | GPU 없음, 15분 제한, 250MB 이미지 제한, 영속 볼륨 없음 |
| AWS SageMaker Endpoint | 콜드 스타트 2~6분, 항상 켜둬야 비용 발생, IaC 작성 필요 |
| EKS + Karpenter + KServe | 노드 프로비저닝 60~180초, GPU 드라이버 운영, ML 엔지니어가 K8s를 알아야 함 |
| RunPod / Lepton (구) | 컨테이너 영속, 0으로 안 줄어듦, 사실상 GPU 임대 모델 |
| Knative on K8s | scale-to-zero는 되지만 GPU 1급 시민 아님, 이미지 풀 시간 그대로 |
1-2. 기술 정의
Modal은 Python 함수에 @app.function() 데코레이터를 붙이면 그 함수가 클라우드의 GPU 컨테이너에서 실행되는, "코드 = 인프라"인 서버리스 컴퓨트 플랫폼입니다. Spotify ML 인프라를 만든 Erik Bernhardsson이 2021년에 창업했고, 워커는 Rust로 짜여 있으며, 컨테이너는 gVisor 샌드박스 안에서 돕니다. 핵심 차별점은 파일시스템·메모리 스냅샷으로 GPU 컨테이너 콜드 스타트를 1초 미만까지 줄였다는 점입니다.
2. Modal 기술 특징
| 특징 | 설명 |
|---|---|
| 스냅샷 기반 콜드 스타트 | 초기화된 Python 인터프리터·import된 모델 가중치까지 스냅샷으로 저장 → 0.5~2초 안에 새 컨테이너 부팅 |
| Python-native 인프라 정의 | Dockerfile·YAML 없이 Image.debian_slim().pip_install(...) 같은 Python DSL로 이미지 빌드 |
| 0→N 자동 확장 | 트래픽 없으면 0 컨테이너, 호출이 들어오면 즉시 확장. 1초당 과금 |
| GPU 1급 시민 | T4, L4, A10G, A100, L40S, H100, H200 모두 함수 인자로 선택 (gpu="H100") |
| Volume / NFS 일체화 | S3 백엔드 분산 FS를 FUSE로 마운트, 모델 가중치·체크포인트 영속화 |
| Sandbox | gVisor + seccomp + cgroups v2로 사용자 코드 격리, 멀티테넌트 안전성 |
| Web Endpoint 내장 | @modal.fastapi_endpoint() 데코레이터로 함수가 곧 HTTPS API |
3. Modal 동작방식
3-1. 구성 요소
- Modal CLI / SDK (Python) — 사용자 측. 함수 정의를 App graph로 직렬화해 컨트롤 플레인에 전송.
- 컨트롤 플레인 (Control plane) — gRPC API 서버. 이미지 빌드 요청, 함수 등록, 호출 라우팅, 권한·과금 관리.
- 이미지 빌더 (Image builder) —
ImageDSL 그래프를 받아 OCI 이미지로 빌드. 빌드 결과는 콘텐트 어드레스로 캐시. - 스케줄러 (Scheduler, Rust) — 호출이 들어오면 빈 워커를 찾아 컨테이너 슬롯을 할당. GPU pool 관리.
- 워커 노드 (Worker, bare-metal) — Rust 데몬. 컨테이너를 gVisor 안에서 실행하고 stdin/stdout·gRPC를 컨트롤 플레인에 중계.
- 스냅샷 스토어 — 메모리·파일시스템·CUDA 컨텍스트까지 포함한 컨테이너 스냅샷 저장. 새 호출이 오면 fork 대신 restore.
- Volume / NFS 백엔드 — S3 호환 객체 스토리지 위에 자체 메타데이터 서버. FUSE로 컨테이너에 마운트.
3-2. 데이터 흐름 (한 번의 GPU 추론 호출)
[ 사용자 ] --(.remote() / HTTPS)--> [ 컨트롤 플레인 ]
|
v
[ 스케줄러(Rust) ]
|
+---------------------------+---------------------------+
| |
(warm 컨테이너 있음) (warm 없음)
| |
v v
기존 슬롯에 라우팅 스냅샷 restore
| |
+---------------------------+---------------------------+
|
v
[ 워커 노드 / gVisor 샌드박스 ]
|
(모델 가중치는 Volume에서 FUSE 캐시 hit)
|
v
함수 실행 (GPU)
|
결과 직렬화 → 컨트롤 플레인 → 사용자
핵심은 스냅샷 restore 분기입니다. 일반적인 컨테이너 부팅이라면 이미지 풀(수 GB) → 컨테이너 시작 → Python import → CUDA 초기화 → 모델 로드 순서로 30~120초가 걸립니다. Modal은 이 모든 단계가 끝난 직후의 메모리 상태를 스냅샷으로 떠두고, 새 호출이 오면 그 스냅샷을 메모리에 mmap한 뒤 CUDA 컨텍스트를 복구합니다.
4. Modal 구성 및 흐름도
4-1. 단계별 설명
| 단계 | 행위자 | 하는 일 |
|---|---|---|
| 1 | 개발자 | modal deploy app.py로 App graph 업로드 |
| 2 | 컨트롤 플레인 | Image DSL을 분석해 캐시 미스인 레이어만 다시 빌드 |
| 3 | 이미지 빌더 | 콘텐트 어드레싱된 OCI 이미지 생성, 분산 캐시에 저장 |
| 4 | 사용자 | .remote() 호출, 또는 web endpoint에 HTTPS 요청 |
| 5 | 스케줄러 | 필요한 GPU 타입의 워커 슬롯 검색, warm 컨테이너 우선 선택 |
| 6 | 워커 | 스냅샷 restore 또는 콜드 부팅, gVisor 샌드박스 안에서 함수 실행 |
| 7 | 워커 → 컨트롤 플레인 | stdout·return value를 gRPC stream으로 송신 |
| 8 | 컨트롤 플레인 → 사용자 | 결과 반환, 컨테이너는 idle timeout(기본 60초)까지 warm 유지 |
4-2. 실제 처리 흐름 (Llama 3 추론 1회)
- 로컬에서
generate.remote("Hello")호출. - SDK가 인자를 cloudpickle로 직렬화 → gRPC로 컨트롤 플레인 전송.
- 스케줄러가 H100 풀에서 warm 컨테이너 1개 발견 → 라우팅.
- 워커 안 함수가 KV cache hit → 이미 로드된 모델로 forward pass.
- 응답 토큰 stream을 gRPC로 컨트롤 플레인 → 클라이언트로 전달.
- 호출 종료 후 60초간 warm 유지, 이후 스냅샷만 남기고 컨테이너 회수.
5. Modal 설치 방법
Modal은 클라우드 SaaS이므로 서버를 설치하는 게 아니라 SDK + 인증만 셋업합니다.
# 1) Python 3.9+ 가상환경 권장
python -m venv .venv && source .venv/bin/activate
# 2) SDK 설치 (CLI 포함)
pip install modal
# 3) 토큰 발급 (브라우저 자동 오픈 → 약관 동의 → 토큰 자동 저장)
modal token new
# 4) 헬스 체크
modal profile current
# expected: 워크스페이스 이름과 사용자 이메일 출력
설치된 토큰은 ~/.modal.toml에 저장됩니다. CI 환경에서는 MODAL_TOKEN_ID·MODAL_TOKEN_SECRET 환경변수로 주입하세요.
6. Modal 사용 방법
6-1. 가장 단순한 GPU 함수
# app.py
import modal
app = modal.App("hello-gpu")
image = (
modal.Image.debian_slim(python_version="3.11")
.pip_install("torch==2.4.0", "transformers==4.44.0")
)
@app.function(gpu="A10G", image=image, timeout=600)
def generate(prompt: str) -> str:
from transformers import pipeline
pipe = pipeline("text-generation", model="gpt2")
return pipe(prompt, max_new_tokens=50)[0]["generated_text"]
@app.local_entrypoint()
def main():
print(generate.remote("Modal makes serverless GPU"))
실행은 modal run app.py. 첫 호출은 이미지 빌드 + 모델 다운로드 때문에 30~60초, 두 번째부터는 스냅샷이 있어 1초 내외입니다.
6-2. Web Endpoint (HTTPS API 한 줄)
import modal
app = modal.App("api")
@app.function(gpu="A10G", image=image, min_containers=1)
@modal.fastapi_endpoint(method="POST")
def infer(item: dict):
return {"result": generate_local(item["prompt"])}
modal deploy app.py → 즉시 https://<workspace>--api-infer.modal.run URL이 발급됩니다. min_containers=1은 항상 1개 warm을 띄워두는 옵션이며, 그만큼 idle 비용이 누적됩니다.
6-3. 클래스 기반 — 모델을 한 번만 로드
@app.cls(gpu="H100", image=image, scaledown_window=300)
class LLM:
@modal.enter() # 컨테이너 부팅 시 1회 실행 → 스냅샷에 포함됨
def load(self):
from vllm import LLM as VLLM
self.engine = VLLM(model="meta-llama/Llama-3.1-8B-Instruct")
@modal.method()
def generate(self, prompt: str) -> str:
return self.engine.generate([prompt])[0].outputs[0].text
@modal.enter()가 끝난 직후의 메모리 이미지가 스냅샷으로 저장되므로, 다음 호출은 vLLM 엔진이 이미 GPU에 로드된 상태에서 시작됩니다. 이게 Modal 콜드 스타트 마법의 정체입니다.
6-4. Volume — 모델 가중치 영속화
model_volume = modal.Volume.from_name("llama-weights", create_if_missing=True)
@app.function(
gpu="H100",
image=image,
volumes={"/models": model_volume},
)
def warm():
# /models 경로는 분산 FS가 FUSE로 마운트된 영속 디렉터리
pass
6-5. 운영 시 고려사항
- idle 비용:
min_containers를 1 이상으로 두면 트래픽 0이어도 GPU가 계속 청구됩니다. 실시간 요구가 없으면 그냥 0으로 두세요. - 스냅샷 무효화: 이미지 또는
@modal.enter()코드가 바뀌면 스냅샷이 무효가 되어 첫 호출이 다시 느려집니다. 배포 직후 한 번 워밍업 호출을 권장. - 리전: Modal은 us-east, us-west, eu-west 중심. APAC 사용자는 첫 hop 지연(80~150ms)을 감수해야 합니다.
- egress: Modal → 외부 인터넷 트래픽은 GB당 과금. 큰 결과는 S3·Volume에 저장하고 presigned URL을 반환하는 패턴이 저렴합니다.
- 동시성 제한: 함수당 기본 100 컨테이너. 그 이상은 워크스페이스 설정에서 상향 요청.
- 관측성: 대시보드에 함수별 호출·지연·비용이 보이지만, OpenTelemetry export는 2026년 5월 기준 베타. 자체 APM과 통합하려면 컨테이너 안에서 직접 OTLP를 내보내야 합니다.
7. Modal 자주 쓰는 명령어
| 명령어 | 용도 |
|---|---|
modal token new |
인증 토큰 발급 후 로컬 저장 |
modal run app.py |
로컬 엔트리포인트 1회 실행 (테스트용) |
modal deploy app.py |
앱을 영속 배포 (web endpoint·cron 함수 살아남음) |
modal serve app.py |
로컬 코드 변경을 핫리로드하며 web endpoint 임시 노출 |
modal app list |
배포된 앱 목록 / 상태 확인 |
modal app logs <name> |
실시간 로그 tail |
modal app stop <name> |
배포 종료, warm 컨테이너 모두 회수 |
modal volume create <name> |
영속 Volume 생성 |
modal volume put <v> src dst |
로컬 파일을 Volume에 업로드 (모델 가중치 사전 적재) |
modal secret create <n> KEY=VAL |
함수에서 환경변수로 주입할 비밀 생성 |
modal shell <app::function> |
동일 이미지·동일 GPU 컨테이너에 인터랙티브 셸 진입 (디버깅 핵심) |
7-1. 사례 — 5분 만에 Llama 3 챗 API 띄우기
# 1. 가중치를 Volume에 사전 다운로드해 놓는 1회성 함수 실행
modal run --detach download.py::seed_weights
# 2. 추론 앱 영속 배포
modal deploy llama_api.py
# 3. 로그 보면서 워밍업 호출 1번
curl -X POST https://acme--llama-api-infer.modal.run \
-H "Content-Type: application/json" \
-d '{"prompt":"안녕"}'
# 4. 두 번째 호출부터 1초 내 응답 (스냅샷 hit)
modal app logs llama-api
8. Modal 활용방안
8-1. 잘 맞는 워크로드
- LLM·확산모델 추론 API — 트래픽 변동성이 크고, 모델이 무거워서 cold start가 치명적인 케이스.
- 배치 ML 작업 — 야간 임베딩 재생성, 데이터셋 라벨링, 비전 전처리. 1000 컨테이너로 fan-out 후 회수.
- fine-tuning 단발 잡 — H100 1~8장으로 수 시간 돌리고 끝나는 잡. 인프라 영구 보유 불필요.
- 웹훅·이벤트 트리거 GPU 처리 — 이미지 업로드 → 자동 캡셔닝, 영상 업로드 → 트랜스코딩 같은 이벤트 기반.
- 경량 SaaS 백엔드 — Postgres·Redis는 외부에 두고, "GPU가 필요한 한 함수"만 Modal에 분리.
8-2. 대안 기술 비교
| 기술 | 강점 | Modal 대비 약점 |
|---|---|---|
| AWS Lambda | 초저비용 CPU 함수, AWS 통합 | GPU 없음, 15분 제한, 패키지 250MB |
| SageMaker Endpoint | VPC 통합, 엔터프라이즈 거버넌스 | 콜드 스타트 분 단위, IaC 필요, 항상 켜둬야 함 |
| RunPod Serverless | GPU 단가 저렴, 다양한 GPU 모델 | Python-native DSL 부재, 스냅샷 없음, 콜드 스타트 더 김 |
| Replicate | 모델 마켓플레이스 1급, Cog 표준 | "모델 1개 = 1 endpoint" 모델, 자유로운 일반 함수에는 부적합 |
| Anyscale (Ray) | 대규모 분산 학습·RLHF에 강함 | Ray 멘탈 모델 학습 비용, 단일 함수 추론에는 과함 |
| SkyPilot (별도 글 참조) | 멀티-클라우드 GPU 가격 비교 후 자동 배포 | 사용자 클라우드 계정 필요, 서버리스 아님 |
| EKS + KServe | 완전한 통제, 온프레미스 가능 | 전담 플랫폼팀 필수, 0→N 확장이 분 단위 |
8-3. 언제 쓰면 안 되는가
- 온프레미스 / 에어갭 환경이 강제될 때. Modal은 SaaS 단독이며 self-host 옵션이 없습니다.
- 지속 트래픽 + 비용 최적화가 최우선일 때. 24/7 풀로 GPU를 돌리는 워크로드라면 직접 GPU 임대가 단가가 낮습니다.
- VPC 내부 데이터에만 접근해야 하는 규제 워크로드. PrivateLink·VPC peering은 엔터프라이즈 플랜 한정이며 셋업 비용이 큽니다.
- 비-Python 스택이 1급 시민이어야 할 때. JVM·Go·Rust 워크로드도 컨테이너로 돌릴 수는 있지만 SDK·DX는 Python 중심.
- 매우 낮은 지연(p50 < 50ms) 추론 — 컨트롤 플레인을 거치는 라운드트립 자체가 그 정도를 먹습니다.
9. 정리: 무엇을 트레이드오프하는가
Modal의 본질은 "운영 부담을 0으로 만드는 대신 SaaS·Python에 묶이는" 트레이드오프입니다. 데이터 게이트키핑이 강한 조직이라면 EKS+KServe가 여전히 정답이지만, "ML 엔지니어 1명이 다음 주까지 GPU API를 띄워야 한다"는 상황이라면 Modal보다 빠른 길은 2026년 현재 거의 없습니다. 스냅샷 기반 콜드 스타트라는 한 가지 엔지니어링 결정이 이 모든 차이를 만들었습니다.
반응형
'AI(Artificial Intelligence)' 카테고리의 다른 글
| 벤더 락인 방지와 비용 최적화를 위한 LLM 게이트웨이, LiteLLM (1) | 2026.05.14 |
|---|---|
| 내 컴퓨터가 슈퍼 AI가 된다! 로컬 LLM의 절대 강자 'Ollama'에 대해 알아보겠습니다. (0) | 2026.04.03 |
| AI의 장기 기억 장치, '벡터 데이터베이스'에 대해 알아보겠습니다. (0) | 2026.04.02 |
| 챗봇의 시대는 끝났다? 스스로 업무를 완수하는 '에이전틱 AI(Agentic AI)'를 알아보겠습니다. (0) | 2026.03.27 |
| 멀티모달 AI란 무엇인가? 텍스트를 넘어 보고 듣고 이해하는 AI의 시대 (0) | 2026.03.26 |