[CloudNeta] Hands-On LLM Serving 1주차 part 1 - 모델 서빙 기본

감사의 인사

이번에도 기회가 닿아 CloudNet@ 를 통해 모델 서빙 및 최적화에 대한 스터디에 참석할 수 있게 되었습니다.
다시금 감사의 인사를 드립니다.

본 스터디의 전체 목적은 LLM 서비스 제공 및 최적화에 대한 지식에 대한 학습 및 실습입니다. 이번 주차의 내용은 세 편으로 나누어 연재합니다.

  1. part 1 (이 글): 모델과 모델 서빙이란 무엇인지, 개념을 잡습니다.
  2. part 2: 인공지능 개요와 LLM 기초. part 3을 이해하는 데 필요한 최소한만 다룹니다.
  3. part 3: LLM 서빙. 토큰 생성 과정, 그리고 LLM 서빙이 왜 독특한 문제인지를 다룹니다.
들어가기에 앞서

스터디에서 추천받고, 읽기 계획중인 도서를 공개합니다.

들어가며

이번 스터디의 최종목적에 도달하기 위해 아래 질문을 먼저 살펴봅시다. 모델, 모델 서빙, 모델 서빙 최적화 는 무엇을 말하는 것일까요?

먼저 알아둘 최소 용어

본문에서 계속 쓰는 용어들입니다. 지금은 직관 수준으로만 잡고 넘어가도 충분합니다. LLM 내부 구조와 관련된 용어(토큰, 트랜스포머, 어텐션 등)는 part 2에서 제대로 다룹니다.

  • 학습(training): 모델 안의 숫자들을 데이터에 맞게 조금씩 조정해 나가는 과정입니다.
  • 추론(inference): 학습이 끝난 모델에 입력을 넣어 출력을 받는 것입니다. 서빙이 실제로 처리하는 작업이 바로 이것입니다.
  • 가중치(weight) · 편향(bias): 학습으로 조정되는 숫자들입니다. y = ax + bab에 해당하며, 이 숫자들을 통칭해 **파라미터(parameter)**라고 부릅니다. "GPT-2 124M" 할 때의 124M이 파라미터 개수입니다.
  • 텐서(tensor): 그 숫자들을 담는 다차원 배열입니다. shape(모양) 정보가 붙은 숫자 배열로 이해하면 충분합니다.
  • 임베딩(embedding): 텍스트 같은 입력을 모델이 계산할 수 있는 숫자 벡터로 바꿔놓은 표현입니다.

또한 인공지능 전반과 LLM 자체에 대한 기초는 part 2에서 별도로 다루기로 하고, 이 글에서는 "서빙의 대상"으로서의 모델이 무엇인지에 집중합니다.

모델이란?

모델은 데이터·구조·실행 코드가 합쳐진 실행가능한 소프트웨어입니다.

모델의 구성요소

모델은 모델 데이터, 모델 아키텍처, 그리고 모델 실행코드로 구성됩니다. 정말 러프한 비유를 들자면 아래와 같다고 할 수 있습니다.

모델 = 코드(아키텍처) + 코드가 만들어낸 상태(데이터) + 그 둘을 조립해 실행하는 부트스트랩(실행 코드)

모델 데이터

엄밀히 말하자면 학습으로 얻게 된 weight, bias, configuration 을 의미합니다. 파트 2에서 보다 자세히 살펴볼 배치 크기, 입력/출력 텐서, 라벨, 임베딩 등의 실행 메타데이터도 여기에 포함됩니다.

PyTorch에서는 학습으로 얻은 weight와 bias를 state_dict라는 "이름 → 텐서" 딕셔너리로 관리하며, 파일로 저장할 때도 이것을 저장합니다[1].

# 가중치와 편향이 사용하고자 하는 모델 객체의 state_dict에 저장됩니다.
torch.save(model.state_dict(), "model_weights.pt")

모델 아키텍처

레이어 구조, 연결 방식, 연산을 정의합니다. 입력이 어떤 계산경로를 지나 출력으로 변환되는지를 결정합니다. 말하자면 모델의 내부 설계도입니다. 레이어가 몇 개인지, 어떤 종류인지, 서로 어떻게 연결되어 어떤 순서로 연산하는지, 입력과 출력이 어떤 구조인지가 여기서 정해집니다. 코드로 보면 아키텍처는 곧 모델 클래스 정의입니다.

모델 실행코드

말 그대로 모델을 실행하는 코드입니다. 아래 순서대로 구동되지요.

  1. 아키텍처를 초기화하고
  2. 가중치(weight)를 불러오고
  3. 추론(inference)을 실행하는 코드를 구성합니다
  4. 입력값이 들어왔을 때 예상된 예측값을 리턴합니다

PyTorch 코드로는 아래 네 줄입니다[1:2].

# 1. 모델 구조를 생성합니다 (이 시점의 텐서들은 아직 랜덤 숫자입니다)
model = TheModelClass(*args, **kwargs)

# 2. 저장해둔 state_dict를 읽어 학습된 숫자로 덮어씁니다
model.load_state_dict(torch.load("model_weights.pt", weights_only=True))

# 3. 추론 모드로 전환합니다
model.eval()

# 4. 입력을 전달해 예측 결과를 얻습니다
pred = model(inputs)

아키텍처와 데이터를 분리하는 이유

아키텍처와 가중치(weight)를 분리하면 버전변경, 부분 로딩, 파인튜닝, 레이어 추가 등의 운영 시나리오에 유리합니다. 예를 들어 새 모델 구조와 기존 checkpoint 사이에 일부 key가 맞지 않아도 호환되는 파라미터만 선택적으로 로드할 수 있습니다.

가령 아래와 같이 파일 구조가 구성될 수 있지요.


# 설정(Config) 파일

config.json : 모델 아키텍처 정보 (레이어 수, hidden size, 어텐션 헤드 수, activation 함수 등). 이 파일만으로 모델 구조를 재구성 가능
generation_config.json : 텍스트 생성 시 기본 파라미터 (max_length, temperature, top_p 등)

  

# 가중치(Weight) 파일

pytorch_model.bin : PyTorch의 전통적인 가중치 저장 포맷 (pickle 기반). state_dict()를 직렬화한 것  
model.safetensors : 최신 표준 포맷. pickle 대신 안전한 직렬화 방식 사용 (임의 코드 실행 위험 없음, 로딩 속도도 더 빠름)
.pt / .pth : PyTorch에서 torch.save()로 저장한 파일 (전체 모델 또는 state_dict)

...

  

# 토크나이저 관련 파일 (NLP/LLM 모델일 경우)

tokenizer.json : Fast tokenizer용 통합 파일 (vocab + merge rules + 설정 모두 포함)
tokenizer_config.json: 토크나이저 클래스, special token 설정 등
vocab.txt / vocab.json : 단어(토큰) 사전
...

기재된 상세 내용은 다음장의 내용에서 보다 상세히 풀어나가도록 하겠습니다.

모델의 생명주기

그렇다면 모델은 학습부터 배포까지의 사이클을 통해 어떤 생명주기를 가지는지 살펴보겠습니다.

---
title: 모델의 생명주기
---
flowchart LR
    pt1["데이터 수집"]
    pt2["학습·파인튜닝"]
    pt3["평가"]
    pt4["배포"]
    pt5["서빙"]
    pt6["모니터링·최적화"]
    pt7["재학습"]
    pt1 --> pt2
    pt2 --> pt3
    pt3 --> pt4
    pt4 --> pt5
    pt5 --> pt6
    pt6 --> pt7
    pt7 --> pt2
단계 역할
데이터 수집 로그, 문서, 센서, 사용자 데이터 수집
학습 데이터에서 패턴 학습
평가 정확도, Loss, Benchmark 검증
배포 모델을 버전 있는 소프트웨어 산출물로 패키징
서빙 API를 통해 실제 요청 처리
최적화 속도, 비용, 안정성 개선
재학습 운영 데이터를 다시 학습에 반영

모델의 생명주기는 데이터 수집, 학습, 평가, 배포, 모니터링 그리고 재학습으로 이어집니다.
이번 스터디에서 중점적으로 살펴볼 모델 서빙은 학습된 모델이 실제 요청을 받아 예측 결과를 반환하는 운영 단계라 할 수 있습니다.
그러므로 모델 서빙의 주요 평가기준은 지연시간, 처리량, 안정성, 비용, 보안, 모니터링이 핵심이 됩니다.

모델 서빙과 모델 서빙 방안

헷갈리지 맙시다!

여기서의 모델 서빙방식은 ML 모델을 서빙하기 위한 방안을 의미합니다. 프론티어 모델 서빙과는 다소 거리가 있으므로 참고해주세요.

모델 서빙이란?

모델 서빙은 학습이 끝난 모델을 실제 서비스로 전달하는 것을 말합니다. 입력을 수신하고, 모델을 실행한 후 예측결과를 반환하는 과정이죠. 이 관점에서의 최적화는 빠르고 안정적이며 저렴하게 모델을 운영하는 방법을 일컫습니다.

또한 모델 서빙은 LLM 서빙보다 넓은 개념입니다. 모델 서빙 대상은 LLM 말고도 비전 모델, 추천 모델, 음성 인식 모델 등이 모두 포함됩니다. 다만 LLM은 토큰을 하나씩 생성하는 자기회귀(autoregressive) 구조 탓에 일반 모델 서빙과는 질적으로 다른 문제를 갖게 되고, 그래서 LLM 서빙이 별도의 분야로 다뤄집니다. 이 이야기는 part 3에서 본격적으로 다룹니다.

---
title: 모델의 서빙 과정
---
flowchart LR
    pt1["클라이언트 or 애플리케이션"]
    pt2["모델 서빙 API"]
    pt3["모델 로딩 및 관리"]
    pt4["추론 백엔드"]
    pt5["리소스(CPU, GPU, NPU, etc.)"]
    pt1 --> |HTTP·gRPC 요청| pt2
    pt2 --> |응답| pt1
    pt2 --> pt3
    pt3 --> pt4
    pt4 --> |추론 결과| pt2
    pt4 --> pt5
    pt5 --> pt4

이를테면 챗봇 앱이 AWS의 SageMaker 추론 엔드포인트에 배포된 모델 서빙, 그 중 LLM을 서빙하는 예시를 살펴봅시다.

이렇듯 LLM을 서빙할 때는 특히 토큰 생성방식이나 KV Cache, attention 비용, batching, decoding, GPU memory 병목 같은 LLM 고유 특성을 알아야 운영 비용과 성능을 제대로 제어할 수 있습니다.

모델 서빙방안에 대해

이어서 ML 모델의 서빙방식에 대해 살펴봅시다.

ML 프로덕션 서빙의 표준 패턴은 모델을 컨테이너 이미지에 패키징하고 Kubernetes 같은 오케스트레이션으로 배포·관리하는 것입니다. 대표적인 서빙 방법으로는 KServe[2]와 SageMaker AI[3]가 있습니다. 컨테이너 안에서는 TorchServe, TF Serving, NVIDIA Triton 같은 추론 서버가 돌면서 HTTP/gRPC API로 요청을 받습니다. 즉 표의 두 행은 "컨테이너(=추론 서버 인스턴스) 하나당 모델을 몇 개 올리느냐" 의 차이입니다.

방식 핵심 특징 적합한 상황
On-device 단말 안에서 직접 실행 초저지연, 오프라인, 개인정보
Single-model 컨테이너 하나에 모델 하나 높은 성능, 독립적 확장
Multi-model 컨테이너 하나에 여러 모델 모델 수가 많고 트래픽이 낮거나 불규칙
Serving platform 여러 앱·모델·워크플로 통합 대규모 조직, 복잡한 AI 서비스

모델 서빙 방안 - 온디바이스 서빙(On-Device Serving)

온디바이스 서빙은 모델을 스마트폰, 드론, 카메라, 로봇 등에서 직접 실행하는 방식입니다.

구성

온디바이스 서빙의 구성은 아래와 같습니다.

온디바이스 서빙 구성 - (a) Model Wrapper와 Model Runtime으로 구성된 On-device AI app (b) 학습된 모델을 대상 기기용으로 컴파일·검증·배포하는 워크플로우

온디바이스 앱은 아래 흐름으로 호출합니다.

  1. 앱 로직에서 모델 래퍼 호출
  2. (데이터 변환 후) 모델 런타임 실행
  3. 로컬 하드웨어에서 추론

주요 활용방안과 제약사항

온디바이스 서빙은 이렇듯 아래와 같은 형태에서 적합합니다.

다만 온디바이스 서빙은 아래 제약사항과 트레이드오프가 있습니다.

  1. 연산·저장 공간 제약: CPU/GPU/메모리가 제한적이라 대형모델 구동이 어렵습니다
  2. 전력 소모: 모델 구동은 배터리를 빠르게 소모합니다
  3. 업데이트/유지보수의 어려움: 모델 개선 시 모든 기기에 개별적으로 업데이트를 배포해야 합니다
  4. 하드웨어 지원의 불일치: 기기마다 NPU 지원 여부가 다릅니다(예: iPhone Neural Engine 최적화 모델이 Android Snapdragon에선 비효율적일 수 있음).

모델 서빙 방안 - 단일 모델 서비스

가장 기본적인 서빙 방안입니다. 각 모델(그리고 각 모델 버전)을 독립된 웹 서비스로 배포하여 HTTP/gRPC로 예측 API를 노출하는 방식입니다. 표준 마이크로서비스 구조(컨테이너화)를 따르며, API가 요청을 받아 백엔드 워커(컨테이너)로 라우팅합니다.

단일 모델 서비스 구성 - Model service API and routing logic이 가용영역별 Single model 컨테이너로 요청을 라우팅

구성

단일 모델 서빙 컨테이너는 아래와 같은 구성을 가집니다.

  1. API Server: HTTP/gRPC로 추론 기능을 외부에 노출
  2. Model Management: 모델 다운로드 → 로컬 저장소 추출 → 추론 백엔드에 로딩 (신규 모델을 감지해 자동 갱신)
  3. Inference Backend: 실제 모델 실행 담당 (TensorFlow Serving, TorchServe, vLLM, TensorRT-LLM 등 활용)

라우팅 전략

모델 서빙은 통상 API처럼 라운드 로빈으로 처리하기엔 한계점이 있습니다. 모델의 요청에 따른 처리시간이 모두 다르기 때문입니다. 예를들어 클라이언트가 5,000토큰 LLM 요청한 것과 100토큰 요청한 것은 결과가 다르게 올 것이고, 고해상도 이미지/저해상도 이미지도 마찬가지입니다.

따라서 보다 정교한 전략이 필요합니다.

스케일링 방식

통상의 스케일링 방식과 같이 수직/수평 확장이 존재합니다

핵심 운영원칙

여러 머신에 분산하여 배치하기(inter-node) 보다는, 한 머신에 여러 GPU(intra-node)로 구성하는 것을 추천합니다. 머신 간 분산이 이루어진다면 네트워크 오버헤드와 동기화 복잡도라는 추가요소가 들어가 지연시간이 길어지기 때문입니다. 대표적인 예시로, NVIDIA의 NVLink 구성과 IB/RoCEv2 구성은 최대 18배 가량의 스루풋 차이가 납니다[4][5].

다만, 워크로드를 어떻게 꾸려야 할지 고민해야하는 입장에서는 Bin packing 스케줄링 방식을 사용할 수 있습니다. 자원은 유한하고, 상황에 맞게 커스텀 스케줄러를 구성해야할 수도 있지만, 이런 스케줄링 방식도 있다는 건 인지해야 할 것 같습니다.

가장 기본적 서빙 방안으로 불리는 이유

0 to 1을 수행하기 좋습니다.

하지만, 자원 효율성과 비용측면에서 약점이 있습니다.

예를 들어 고객 100명이 각자 모델 10개씩 배포하는 에이전트 플랫폼이면 총 1,000개의 개별 서비스를 운영해야 합니다.
그렇다보니 유지보수/패치/모니터링 부담이 압도적이고, 실제로 안 쓰이는 모델도 자원을 낭비하게 됩니다.

이러한 한계로 인해 멀티 모델 서비스를 구성하게 됩니다.

모델 서빙 방안 - 멀티 모델 서비스

이어서 멀티모델 서비스입니다. 이는 하나의 서빙 컨테이너에 여러 모델을 함께 호스팅합니다. 리소스는 모델끼리 공유하고 트래픽에 따라 모델을 로드/언로드 하는 방식입니다. 위에서 살펴본 가성비를 극대화하는 방식입니다.

구성

멀티-모델 서빙 컨테이너는 아래 구성을 가집니다.

  1. Model Server Inference Backend: 프레임워크(TensorFlow, ONNX, PyTorch 등)가 서로 달라도 통합된 예측 API로 처리하는 "블랙박스" 역할. 내부적으로 각 프레임워크별 백엔드를 여러 개 갖고 있음. 대표 오픈소스 예시: NVIDIA Triton Inference Server

  2. Model Cache Management:

    • 모델 저장소에서 모델을 다운로드해 추론 백엔드에 로드
    • LRU(Least Recently Used) 캐시로 자원 사용량이 높으면 가장 안 쓰인 모델을 언로드해 메모리 확보

멀티 모델 서빙 컨테이너 구성 - API server, LRU 기반 Model Cache Management, Model Server Inference Backend, Model Storage 간의 상호작용

멀티 모델 서비스의 어려움과 해결책

멀티모델 서비스는 아래와 같은 문제를 가집니다:

  1. 라우팅 문제: 모델이 아무 컨테이너에나 있을 수 있어서, 이미 해당 모델이 로드된 컨테이너로 요청을 보내야 함. 안 그러면 콜드 스타트(모델 로딩 대기)나 모델 스와핑(기존 모델 언로드 후 새 모델 로드)으로 지연이 생김
  2. 모델별 스케일링 문제: 모델마다 트래픽이 달라서, 인기 있는("hot") 모델은 더 많은 인스턴스가 필요함

멀티 모델 서비스의 모델별 스케일링 문제 - Model↔hosts 매핑에 따라 라우팅 로직이 요청을 해당 모델이 로드된 컨테이너로 분배

이를 해결하기 위해 라우팅 영역으로 해결방안을 모색하기도 합니다. Envoy AI Gateway , LiteLLM 등이 그 예시입니다.

Envoy AI Gateway 2-Tier 구성 예시 - Tier 1(Unified LLM Provider, Auth, Rate Limiting 등)과 Tier 2(KVCache Aware, Prefix Cache, P/D Disaggregation 등)를 거쳐 자체 호스팅 모델 또는 외부 LLM Provider로 라우팅

멀티 모델 서비스가 어려운 상황

단일 모델 서비스 vs 멀티 모델 서비스

둘은 서로 상호 보완적입니다. 실무에서는 두 방식을 조합해 서로 다른 서빙 케이스를 하나의 플랫폼 안에서 처리하며, 이것이 다음 주제인 모델 서빙 플랫폼(Model Serving Platform)으로 이어집니다.

모델 서빙 방안 - 서빙 플랫폼 운영

비즈니스가 복잡해지면 단일 모델 서비스, 멀티 모델 서비스를 나열하는 것 만으로는 부족해집니다.

  1. 여러 모델의 협업이 필요한 태스크 증가: Siri 같은 음성 비서는 음성 인식 + NLP + 추천 모델 + 음성 합성(TTS)을 하나의 명령 처리를 위해 함께 작동시켜야 합니다
  2. 자원 최적화의 복잡도 증가: 플랫폼 위에 앱이 늘어날수록 GPU/CPU/메모리를 여러 모델에 걸쳐 효율적으로 배분하면서도 확장성, 비용 효율성, 자원 경합 최소화를 모두 챙겨야 합니다

이런 경우라면 모델 서빙 플랫폼 자체를 고려해야합니다.

구성

  1. 게이트웨이: 외부 요청의 진입점입니다
  2. 라우팅: 어떤 모델을 어느 서빙 그룹에서 처리할지 선택합니다
  3. 그래프 실행(Graph Execution) 컴포넌트: 한 AI 요청이 여러 모델을 순차/병렬로 사용 지원을 수행합니다
  4. 리소스 그룹: 앱마다 리소스(CPU, GPU, 메모리 총량)한도를 분리합니다
    • 예측 워크로드별로 별도 구성을 해서
    • 비즈니스 요구사항에 맞게 쿼터를 할당받도록 구성합니다.

KServe InferenceService 기반 모델 서빙 플랫폼 동작 흐름 - InferenceService 생성부터 KServe Controller의 리소스 구성, Runtime 이미지 Pull, 모델 아티팩트 로딩, API 요청 처리까지

실행과정은 아래와 같습니다.

종합선물세트네요!

모델 서빙 플랫폼 예시

이런 플랫폼을 구성하는 예시로 아래와 같은 방안들이 있습니다. 대체로 이런 흐름을 가집니다.

모델 서빙을 살펴봐야 하는 이유?

위에서 다양한 모델의 서빙 방안들을 살펴보았는데요. 이러한 특성들로 인해 클라우드 벤더가 Managed endpoint를 제공한다 하더라도, 서빙하는 근본을 이해하지 못하면 최적의 결정을 내리기 어렵습니다. 이를테면:

에 대해 결정하기 어렵죠.

확실한 것은 지향하는 목표에 따라 정답이 있기 때문에 이를 저울질할 수 있어야 한다는 점입니다. 그러므로 서빙의 원리를 어떻게 되는 지 안다면 아래 장점을 확보하는 셈입니다.

다음 편 예고

이번 편에서는 모델이 무엇이고, 어떤 방식으로 서빙하며, 어떤 도구가 있는지 다뤘습니다. part 2에서는 LLM 서빙을 이해하기 위한 최소한의 LLM 기초(토큰, 트랜스포머, 텍스트 생성 과정)를 잡고, part 3에서 LLM 서빙이 왜 일반 모델 서빙과 다른 문제인지를 본격적으로 다루겠습니다.

다음 편: 1주차 part 2 - LLM 기초

감사합니다.


  1. 이 절의 예시 코드는 PyTorch 공식 튜토리얼 Saving and Loading Models에서 가져왔습니다. ↩︎ ↩︎ ↩︎ ↩︎

  2. Kubernetes 위에서 모델 서빙을 표준화해주는 오픈소스 추론 플랫폼입니다. InferenceService라는 CRD(Custom Resource Definition) 하나로 그 조합을 모델 서빙용으로 추상화합니다. YAML에 "S3의 이 경로에 PyTorch 모델이 있다"고 선언하면, 알아서 추론 서버 컨테이너(Triton, TorchServe 등)를 골라 띄우고, HTTP/gRPC 엔드포인트를 만들고, 오토스케일링까지 붙여줍니다. 이로 인해 CNCF 인큐베이팅 프로젝트가 되었습니다. 참고 링크 ↩︎

  3. AWS에서 제공하는 Fully managed ML 서비스 입니다. 후술할 모델 아티팩트를 S3 에 업로드하고 엔드포인트를 만들기만 하면 AWS에서 호스팅·확장·패치를 관리합니다. 참고 링크 ↩︎

  4. Kubernetes Bin Packing 스케줄링 (CloudNet@ 스터디 영상). NVLink(H100 기준 900GB/s)와 IB/RoCEv2(400Gbps ≒ 50GB/s)의 대역폭 차이는 NVIDIA H100 제품 페이지 참고. ↩︎

  5. EC2 P5en 인스턴스 등의 릴리즈를 확인하고, 운영 원칙에 맞게 준비된 컴퓨팅 자원이 있는지 확인할 필요도 있겠습니다. ↩︎