MOSEC 개요
인공지능(ML/DL) 모델은 실험실이나 연구 환경에서는 잘 작동하지만, 실제 서비스로 배포할 때 직면하는 과제들은 매우 복잡합니다. 여러 요청을 동시에 처리하거나, 응답 지연 시간(latency)을 제어하는 등, 주어진 리소스를 어떻게 효율적으로 활용할지에 대한 고민은 물론, 요청이 많아지면서 발생하는 규모의 확장(Scaling)이나 워크로드 특성이 변화할 때의 대응 등과 같이 연구 단계에서 완성된 모델이 안정적으로 온라인 요청을 처리하기 위해서는 다수의 기술적 과제가 뒤따릅니다. 이런 문제들을 해결하기 위해서는, 단순히 “모델을 API로 노출하는 것”을 넘어서는 수준의 시스템 설계가 필요합니다.
MOSEC (Model Serving made Efficient in the Cloud) 은 이러한 현실적 제약을 고려해 설계한 오픈소스 모델 서빙 프레임워크입니다. MOSEC은 고성능 모델 서빙을 위해 Rust의 성능과 Python의 사용성을 결합한 오픈소스 프레임워크로, 모델을 손쉽게 API 서비스 형태로 배포할 수 있도록 설계되었습니다.
Rust 기반의 비동기 웹 서버와 Python API를 동시에 활용함으로써, 시스템 수준에서는 고속 처리와 안정성을, 개발자 입장에서는 높은 접근성과 생산성을 동시에 제공합니다. MOSEC의 주요 특징은 다음과 같습니다:
- 고성능 (Rust + 비동기 I/O 기반 웹 계층)
- 사용성 (Python API 중심 인터페이스)
- 동적 배칭 (Dynamic Batching)
- 파이프라인 병렬 처리 (CPU / GPU 혼합 워크로드 분할)
- 클라우드 친화성 (Kubernetes 배포, graceful shutdown, 메트릭 노출 등)
- 프레임워크 독립성 (PyTorch, TensorFlow, JAX 등 다양한 모델 지원)
즉, MOSEC은 “학습된 모델 → 실제 서비스 API” 사이의 간극을 메우는 미들웨어 역할을 하며, 특히 고성능을 요구하는 실시간 혹은 준실시간 응답 시스템에서 유용합니다.
MOSEC의 구조 및 동작
MOSEC은 “성능”, “단순함”, “확장성”이라는 세 가지 핵심 철학을 바탕으로 설계되었으며, 프레임워크의 내부 구조는 Rust와 Python의 협업에 초점을 두고 있습니다.
Rust는 웹 계층, 태스크 스케줄링, 프로세스 간 통신(IPC) 등 시스템 레벨의 요소를 담당합니다. 비동기 I/O를 기반으로 한 Rust의 처리 구조는 매우 빠른 응답 속도와 낮은 리소스 사용률을 보장합니다.
Python은 모델 로딩, 추론 로직, 사용자 정의 처리 등 상위 수준의 개발 인터페이스를 제공합니다. 따라서 개발자는 Rust의 복잡한 저수준 로직에 관여하지 않고도, 친숙한 Python 환경에서 모델을 즉시 서비스로 전환할 수 있습니다.
파이프라인 구조
MOSEC의 아키텍처는 파이프라인(pipeline) 개념을 중심으로 구성되어 있습니다. 각 파이프라인은 입력(ingress) → 여러 단계의 처리 스테이지 → 출력(egress) 의 형태로 구성됩니다:
-
입력(ingress) 단계에서는 클라이언트 요청을 받아들여 내부 형식으로 역직렬화(deserialize)하고, 첫 번째 처리 스테이지(Worker)로 넘기는 역할을 합니다.
-
각 중간 스테이지(Worker)는 전처리, 추론, 후처리 등을 담당할 수 있으며, 필요하다면 서로 다른 프로세스들로 나눠 실행할 수 있습니다.
-
출력(egress) 단계는 이전의 스테이지들을 통해 생성한 최종 결과를 직렬화(serialize)하여 클라이언트에게 응답을 돌려보냅니다.
즉, MOSEC에서 하나의 모델 서비스는 여러 단계를 거치는 파이프라인 형태로 구성되며, 각 단계는 서로 다른 프로세스로 실행됩니다. 이 과정에서 Rust 기반 컨트롤러가 각 단계 간의 데이터 흐름을 효율적으로 관리하고, CPU/GPU 자원을 최적화된 방식으로 배분합니다.
이러한 구조를 통해 MOSEC은 CPU, GPU, I/O 작업이 혼합된 복합 워크로드 환경에서도 안정적이고 일관된 성능을 제공할 수 있습니다.
또한, MOSEC은 이러한 파이프라인을 병렬로 처리할 수 있는 기능을 지원합니다. 즉, 전처리, 모델 추론, 후처리 단계를 각각 독립적인 프로세스로 분리하여 동시에 실행할 수 있습니다. 예를 들어, GPU가 현재 배치 추론을 수행하는 동안 CPU에서는 다음 입력 데이터를 전처리할 수 있으며, 동시에 다른 프로세스에서는 이전 배치의 후처리를 진행할 수 있습니다.
이러한 파이프라인 병렬 구조는 GPU의 유휴 시간을 최소화하고 전체 처리량(throughput)을 극대화하는 데에 매우 효과적입니다.
동적 배칭(Dynamic Batching)
MOSEC의 핵심적인 기능 중 하나는 동적 배칭(Dynamic Batching) 입니다. 이는 일정 시간 동안 받은 여러 사용자 요청들을 모아서 하나의 배치(batch)로 묶어 모델 추론(inference)을 수행하는 전략입니다. 이렇게 하면 모델이 한 번에 여러 입력을 처리하도록 함으로써 GPU의 병렬 연산 능력을 최대한 활용할 수 있습니다.
동적 배칭(Dynamic Batching) 기능은 다음 두 가지 주요 매개변수로 제어됩니다:
max_batch_size: 한 번에 묶을 수 있는 최대 요청 수max_wait_time: 요청을 기다릴 수 있는 최대 시간(밀리초 단위)
MOSEC은 위의 두가지 조건 중 하나라도 충족되면 배치를 즉시 실행합니다. 즉, 요청이 적을 때에도 과도한 대기 없이 신속한 응답을 보장하며, 동시에 높은 트래픽 환경에서는 배치 효율을 극대화합니다. 이러한 방식으로 GPU 병목을 완화하고 처리량(throughput)을 최대화할 수 있으며, 동시에 응답 지연(latency)를 어느 정도 통제할 수 있습니다.
컨트롤러 및 스케줄링
MOSEC 내부에는 Controller라는 Rust 기반 비동기 스레드가 있습니다. 컨트롤러는 다음과 같이 동작하며 각 워커(Worker) 프로세스의 상태를 모니터링하면서 새로 생성되는 작업을 효율적으로 분배합니다:
- 이전 큐(queue)로부터 새로운 작업(task)을 읽음
- 작업을 처리할 준비가 된 Worker 프로세스에 태스크를 할당
- 작업이 완료되면 다음 큐로 결과를 넘김
- 전체 흐름을 조율 (예: 병목 관리, 큐 관리 등)
즉, MOSEC은 단순히 HTTP 요청 → Python 함수 호출 흐름만 있는 것이 아니라, 내부적으로 Rust 스레드 + 여러 큐 + IPC (Inter-Process Communication) 구조를 가지고 있습니다. Rust의 비동기 이벤트 루프를 활용함으로써, 컨트롤러는 수많은 요청을 동시에 관리하더라도 낮은 지연시간을 유지할 수 있습니다.
이러한 구조는 Python의 GIL(Global Interpreter Lock) 제약을 우회하며, 시스템 전체의 처리 효율을 극대화합니다.
데이터 직렬화 및 역직렬화 (Serialization / Deserialization)
MOSEC의 데이터 흐름은 Rust와 Python 간의 상호작용을 중심으로 이루어집니다. 요청 데이터는 파이프라인 각 단계 사이를 오가며 여러 번의 직렬화(serialize)와 역직렬화(deserialize) 과정을 거치게 되며, 이러한 과정에서 발생하는 연산 비용은 전체 성능에 큰 영향을 미칩니다.
MOSEC은 기본적으로 JSON 형식을 사용하지만, 이미지 생성이나 임베딩 추론과 같은 바이너리 데이터를 처리할 때에는 MsgpackMixin 등의 더 효율적인 직렬화 포맷을 선택하는 것이 좋습니다. 이 포맷은 base64 인코딩을 피할 수 있어, 네트워크 전송 효율을 높이고 메모리 사용량을 줄여줍니다.
또한, 사용자는 파이프라인의 입/출력(Ingress/Egress) 워커(Worker)에 적절한 Mixin을 상속하거나 직렬화/역직렬화(Serialize/Deserialize) 메소드를 직접 구현하여 자신의 데이터 형식에 맞게 조정할 수 있습니다
MOSEC 사용 예시: Stable Diffusion 모델 서빙
아래의 예시는 MOSEC을 이용하여 Hugging Face의 Stable Diffusion 모델을 API 형태로 배포하는 방법을 보여줍니다:
from io import BytesIO
from typing import List
import torch
from diffusers import StableDiffusionPipeline
from mosec import Server, Worker
from mosec.mixin import MsgpackMixin
class StableDiffusion(MsgpackMixin, Worker):
def __init__(self):
self.pipe = StableDiffusionPipeline.from_pretrained(
"sd-legacy/stable-diffusion-v1-5", torch_dtype=torch.float16
)
self.pipe.enable_model_cpu_offload()
self.example = ["warmup prompt"] * 4 # 모델 워밍업용 예시 입력
def forward(self, data: List[str]) -> List[memoryview]:
results = self.pipe(data)
images = []
for img in results:
buf = BytesIO()
img.save(buf, format="JPEG")
images.append(buf.getbuffer())
return images
if __name__ == "__main__":
server = Server()
server.append_worker(StableDiffusion, num=1, max_batch_size=4, max_wait_time=10)
server.run()
위와 같은 코드를 실행하면 텍스트 프롬프트를 입력받아 이미지를 생성하는 API 서버가 실행됩니다. 또한, 서버는 Swagger(OpenAPI) 문서를 자동으로 생성하므로, 브라우저에서 http://127.0.0.1:8000/openapi/swagger/로 접속하여 API 명세를 즉시 확인할 수 있습니다.
이 모든 과정이 단일 Python 파일로 구현된다는 점은 MOSEC의 단순함과 강력함을 동시에 보여줍니다. 위 예제의 전체 코드 및 더 많은 사용 예시는 공식 문서에서 확인해볼 수 있습니다:
라이선스
MOSEC 프로젝트는 Apache License 2.0 하에 공개되어 있으며, 상업적 이용 및 수정이 자유롭게 허용됩니다.
MOSEC 홈페이지
MOSEC 프로젝트 GitHub 저장소
MOSEC 공식 문서
이 글은 GPT 모델로 정리한 글을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 덧글로 알려주시기를 부탁드립니다. ![]()
파이토치 한국 사용자 모임
이 정리한 이 글이 유용하셨나요? 회원으로 가입하시면 주요 글들을 이메일
로 보내드립니다! (기본은 Weekly지만 Daily로 변경도 가능합니다.)
아래
쪽에 좋아요
를 눌러주시면 새로운 소식들을 정리하고 공유하는데 힘이 됩니다~ ![]()

