📌 Notice
LLM Serving and Optimization Study (=LLMSO)
직접 실습을 통해 LLM 서비스 제공 및 최적화에 대한 지식에 대한 학습 및 실습 내용을 정리한 블로그입니다.
CloudNet@에서 스터디를 진행하고 있습니다.
EKS 관련 이전 스터디 내용은 아래 링크를 통해 확인할 수 있습니다.
모델을 메모리에 올리고 API로 감싸면 Serving은 시작할 수 있습니다.
하지만 실제 애플리케이션은 한 번의 요청으로 한 번만 모델을 호출하지 않습니다.
Agent가 계획을 세우고 문서를 검색하고 Tool을 실행한 뒤,
중간 결과를 다시 모델에 넣는 순간 한 사용자 요청은 여러 개의 추론 요청으로 늘어납니다.
개별 모델 호출이 빨라도 앞 단계가 끝나야 다음 단계를 시작할 수 있다면 사용자가 기다리는 시간은 계속 누적됩니다.
이런 Workload를 운영하려면 모델 실행기만으로는 부족합니다.
인증과 요청 제한, Model Routing, Queue와 Autoscaling, Cache, 관측성, 비용 통제와 장애 격리가 함께 필요합니다.
직접 구축하는 범위가 커질수록 통제권은 늘지만, 다음 날부터 직접 운영해야 할 책임도 같이 늘어납니다.
CH04에서는 제품별 설치법보다 세 가지 질문을 따라갑니다.
처음 보는 내용이라면 장애 원인을 찾아 조치해 줘라는 요청 하나를 떠올리면 됩니다.
Assistant는 먼저 질문을 해석하고,
관련 문서를 검색한 뒤,
상태를 조회할 Tool을 고르고,
결과를 읽어 다음 행동을 정합니다.
사용자 목표
→ 계획
→ 문서 검색
→ 상태 조회
→ 결과 판단
→ 조치 제안
→ 최종 응답
이 과정에서 LLM은 한 번이 아니라 여러 번 호출됩니다.
문서 검색과 Tool도 각각 다른 Service를 거치며,
조치에는 권한과 승인 경계가 필요합니다.
Step 01에서는 이 한 요청이 왜 여러 호출로 늘어나는지부터 살펴봅니다.
그다음 필요한 Platform 책임을 나누고,
Open Source Stack에서는 각 책임이 어디에 놓이는지 연결합니다.
마지막으로 어디까지 직접 소유할지 결정하고 같은 지표로 선택을 검증합니다.
기존 학습 기록에는 OpenAI API, Ray Serve, KubeRay, Bedrock, SageMaker와 GPU 실습이 섞여 있습니다.
이번 정리에서는 구조와 판단 기준을 본문에 남기고, 다시 실행하지 않은 결과는 선택 실습의 Historical evidence로 분리합니다.
본문에서 사용하는 SLO는 서비스가 지키기로 한 성능과 안정성 목표입니다.
예를 들어 Agent 요청의 95%를 8초 안에 성공시킨다처럼 측정 가능한 문장으로 적습니다.
한 번의 사용자 요청이 여러 Model·Tool·Retrieval 호출로 늘어나면 지연과 실패는 어디에서 증폭될까?
CH03의 기본 Serving 흐름은 비교적 단순했습니다.
Client가 Prompt를 보내면 Server가 모델을 실행하고 Response를 반환합니다.
Agent workflow에서는 이 직선이 Loop로 바뀝니다.
Agent는 목표를 해석하고, 다음 행동을 계획하고, 필요한 Tool이나 데이터를 선택한 뒤,
그 결과를 다시 읽고 다음 행동을 정합니다.
최종 답을 만들 때까지 같은 과정이 여러 번 반복될 수 있습니다.
여기서 Agent는 단순히 긴 Prompt를 한 번 보내는 프로그램이 아닙니다.
현재 상태를 보고 다음 행동을 결정하는 Control loop를 가진 애플리케이션입니다.
Planner는 목표를 작은 행동으로 나누고,
Tool은 검색과 API 호출처럼 Model 밖의 실제 작업을 수행합니다.
Tool 결과는 Observation이 되어 다시 다음 판단의 입력으로 들어갑니다.
Goal
→ Planner가 다음 Action 선택
→ LLM 또는 Tool 실행
→ Observation 수집
→ 목표가 끝났는지 판단
→ 끝나지 않았다면 다시 Planner로
반복 횟수는 처음부터 고정되지 않을 수 있습니다.
검색 결과가 부족하거나 Tool이 실패하면 Agent가 계획을 수정하면서 호출 수와 비용이 늘어납니다.

한 요청 안에서 모델을 다섯 번 호출한다고 해서 항상 다섯 배 느린 것은 아닙니다.
서로 독립적인 호출은 병렬로 실행할 수 있지만,
앞 단계의 전체 결과가 다음 단계의 입력이라면 각 단계의 지연시간이 순서대로 더해집니다.
예를 들어 서로 다른 문서 세 개를 독립적으로 요약하는 작업은 병렬화할 수 있습니다.
하지만 문서를 찾고 → 요약하고 → 요약 결과로 판단한다는 흐름은 앞 결과가 있어야 다음 단계가 시작되므로 직렬 경로입니다.
사용자가 기다리는 시간은 전체 Graph에서 가장 긴 Critical path에 의해 결정됩니다.
T_task ≈ T_plan + T_retrieve + T_reason + T_tool + T_synthesize
+ Queue·Network·Retry overhead
이 식은 정밀한 성능 모델이 아니라 어디를 측정해야 하는지 보여주는 분해입니다.
예를 들어 분석 단계가 요약 단계의 완성된 결과를 필요로 한다면,
요약 단계에서 첫 Token이 빨리 보이는 것만으로 분석을 일찍 시작할 수 없습니다.
이 경우 사용자가 체감하는 핵심 지표는 개별 호출의 TTFT보다 Agent Task 전체 E2E에 가깝습니다.
TTFT는 요청 뒤 첫 Token을 받을 때까지의 시간이고,
E2E는 요청을 시작해 전체 결과가 끝날 때까지의 시간입니다.
Streaming 화면에는 첫 Token이 빨리 보여도 다음 Agent Step이 완성된 JSON이나 Tool 결과를 기다리면 Task E2E는 줄지 않을 수 있습니다.
실패도 같은 방식으로 연결됩니다.
독립적인 단계가 모두 성공해야 작업이 끝나는 단순한 가정에서 각 단계 성공률이 99%라면,
5단계 작업의 성공률은 약 0.99⁵ = 95.1%, 10단계는 약 90.4%입니다.
실제 Agent에는 Retry와 Fallback이 있어 이 계산과 정확히 같지 않지만,
단계가 늘수록 Timeout, Partial Failure, 중복 Tool 실행을 다뤄야 한다는 방향은 분명합니다.
Retry는 실패한 호출을 다시 시도하는 것이고,
Fallback은 원래 경로 대신 다른 Model이나 Tool을 선택하는 것입니다.
둘은 성공 가능성을 높이지만 호출 수와 비용,
완료 시간도 늘립니다.
특히 결과를 받기 전에 Timeout이 났다고 해서 Backend 실행도 실패했다는 뜻은 아닙니다.
이미 결제가 처리된 상태에서 같은 Tool을 다시 호출하면 Side Effect가 두 번 발생할 수 있습니다.
Agent가 Tool을 호출하면 모델은 호출할 함수와 입력을 제안할 뿐,
실제 실행과 권한 확인, Timeout, 결과 검증은 애플리케이션이나 Gateway 같은 다른 계층이 담당합니다.
Tool이 결제나 배포처럼 Side Effect를 만든다면 Retry가 중복 실행으로 이어지지 않도록 Idempotency와 승인 경계도 필요합니다.
Side Effect는 조회와 달리 외부 상태를 바꾸는 작업입니다.
Idempotency는 같은 요청을 여러 번 받아도 변경이 한 번만 일어나게 만드는 성질입니다.
예를 들어 Tool 호출마다 고유한 작업 ID를 보내고,
이미 처리한 ID라면 기존 결과를 반환하도록 만들 수 있습니다.
모델이 Tool을 골랐다는 사실도 실행 권한을 의미하지 않습니다.
실행 직전 사용자와 Resource 권한을 확인하고,
위험한 변경은 승인 뒤에만 수행해야 합니다.
RAG는 질문 시점에 Retrieval과 Context 조립을 추가하며,
구현에 따라 Query Embedding이나 Rerank가 포함될 수 있습니다.
지식의 최신성과 출처를 통제하기 좋지만 검색 지연과 검색 실패가 온라인 경로에 들어옵니다.
CAG처럼 안정적인 지식을 미리 처리해 Cache로 재사용하면 검색을 줄일 수 있지만,
Cache 용량과 무효화, 모델·Tokenizer 호환성이라는 다른 운영 문제가 생깁니다.
RAG는 질문과 관련된 문서를 실행 시점에 검색해 Prompt에 붙이는 방식입니다.
Embedding은 문장의 의미를 숫자 Vector로 바꾸고,
Rerank는 처음 찾은 후보를 더 정교한 Model로 다시 정렬합니다.
CAG는 자주 쓰고 잘 변하지 않는 지식을 미리 계산해 Cache에서 재사용하는 접근입니다.
Tokenizer는 Text를 Model이 처리하는 Token ID로 바꾸는 구성 요소입니다.
Model이나 Tokenizer Version이 바뀌면 이전 Cache 표현을 그대로 사용할 수 있는지 다시 확인해야 합니다.
RAG는 최신 문서를 반영하기 쉽지만 검색 System에 의존하고,
CAG는 온라인 검색을 줄일 수 있지만 지식이 바뀔 때 Cache를 정확히 무효화해야 합니다.
두 방식의 내부 원리는 CH07에서 자세히 다루고,
여기서는 Agent의 한 행동이 모델 외부의 여러 서비스와 상태를 거친다는 점만 연결합니다.
개별 모델 Endpoint의 지표만으로는 Agent Task가 어디에서 느려졌는지 알기 어렵습니다.
최소한 하나의 Trace 안에서 다음 값을 연결해야 합니다.
Trace는 하나의 사용자 Task가 여러 Model과 Tool을 지나간 순서와 시간을 공통 ID로 연결한 기록입니다.
Agent 시대의 Serving 부하는 단순히 초당 요청 수인 RPS가 증가한 문제가 아닙니다.
요청 하나의 실행 경로와 필요한 자원이 실행 중에 달라지고,
여러 단계의 지연과 실패가 하나의 사용자 경험으로 합쳐지는 문제입니다.
그래서 다음으로 필요한 것은 모델 실행기를 둘러싼 Platform의 책임을 나누는 일입니다.
Step 01에서 기억할 흐름은 호출이 많다보다 호출이 서로 의존한다는 점입니다.
다음 Step에서는 이 의존 관계를 안전하게 운영하려면 Model Runtime 밖에 어떤 Platform Layer가 필요한지 살펴봅니다.
Model Runtime은 Token을 생성합니다.
Production Serving Platform은 누가 어떤 모델을 얼마나 사용할 수 있는지 결정하고,
필요한 자원을 준비하며 문제가 생겼을 때 영향 범위를 제한해야 합니다.
Runtime과 Platform의 차이는 식당의 주방과 운영 System에 비유할 수 있습니다.
주방이 음식을 만드는 곳이라면,
예약과 주문 접수,
좌석 배정,
재료 준비,
결제와 장애 대응까지가 식당 운영입니다.
Model이 Token을 잘 생성해도 요청을 받을 GPU가 준비되지 않았거나 권한이 틀리면 서비스는 완성되지 않습니다.
이 책임을 제품 이름으로 외우면 같은 기능이 다른 위치에 있을 때 구조를 놓치기 쉽습니다.
요청이 들어와 모델에서 나갈 때까지의 책임으로 나누어 보는 편이 낫습니다.
먼저 Data plane과 Control plane을 구분합니다.
Data plane은 실제 사용자 요청과 응답이 지나가는 경로입니다.
Control plane은 어느 Model과 Replica를 배포할지,
몇 개로 확장할지,
어떤 Version을 활성화할지 결정합니다.
Control plane이 잠시 느려져도 이미 준비된 Replica가 요청을 계속 처리할 수 있는지,
반대로 잘못된 배포 결정이 Data plane 전체에 어떤 영향을 주는지를 따로 봅니다.

외부 요청이 처음 만나는 계층은 Protocol과 정책을 안정적으로 유지해야 합니다.
Authentication, Authorization, Schema Validation, Rate Limit, Tenant 식별,
Streaming과 Error Contract가 여기에 들어갑니다.
Authentication은 누구인가를 확인하고,
Authorization은 무엇을 사용할 수 있는가를 확인합니다.
Schema Validation은 요청 형식과 크기가 계약에 맞는지 검사하며,
Rate Limit은 한 사용자가 전체 Capacity를 독점하지 못하게 제한합니다.
Tenant Identity는 인증에만 쓰고 버리면 안 됩니다.
어떤 모델을 사용할 수 있는지, 얼마만큼의 Token과 동시성을 허용할지,
어느 Region과 Data Boundary를 지켜야 하는지까지 다음 계층에 전달되어야 합니다.
Tenant는 같은 Platform을 공유하는 고객이나 조직의 경계입니다.
이 Identity가 Router나 Runtime까지 전달되지 않으면,
앞에서는 접근을 허용한 요청이 뒤에서는 다른 Tenant의 Model이나 Cache를 사용할 수 있습니다.
모든 요청에 가장 크고 비싼 모델을 보내는 것은 단순하지만 운영 가능한 기본값은 아닐 수 있습니다.
Model Selector는 품질 요구, Latency Budget, 비용 등급, Context 길이,
Tenant 정책과 현재 Backend 상태를 보고 목적지를 고릅니다.
여기서 중요한 것은 Router가 모델 이름만 바꾸는 함수가 아니라는 점입니다.
Canary, Fallback, Version Alias, Region, Queue, Cache Locality를 함께 다루면 Router의 결정이 곧 비용과 장애 반경을 바꿉니다.
선택 이유와 적용된 정책을 Trace에 남기지 않으면 결과가 나빠졌을 때 원인을 재현하기 어렵습니다.
Canary는 새 Version에 Traffic 일부만 보내 안전성을 확인하는 방식입니다.
Fallback은 선택한 Backend가 실패했을 때 사용할 대체 경로이고,
Version Alias는 production 같은 안정된 이름을 실제 Model revision에 연결합니다.
Cache Locality는 필요한 Cache가 이미 있는 Replica를 우선해 중복 계산을 줄이는 성질입니다.
이 계층은 Replica 수, GPU 배치, Queue, Autoscaling, Admission Control과 Model Residency를 관리합니다.
한 요청을 어느 Replica로 보낼지뿐 아니라,
필요한 모델이 메모리에 올라와 있는지와 새 Replica가 Ready가 될 때까지 요청을 어떻게 다룰지도 결정합니다.
Replica는 같은 Deployment를 실행하는 개별 Process나 Pod입니다.
Autoscaling은 부하에 따라 Replica 수를 바꾸고,
Admission Control은 현재 Capacity가 감당할 수 없는 요청을 대기시키거나 거절합니다.
Model Residency는 어떤 Weight가 어느 Memory에 올라와 즉시 사용할 수 있는지를 뜻합니다.
Multi-model Serving에서는 Lazy Loading이 GPU Memory를 아낄 수 있지만 Cold Start를 만듭니다.
자주 쓰는 모델을 Cache하고 오래 쓰지 않은 모델을 내리는 정책은 비용과 지연시간을 직접 바꿉니다.
Replica 수를 늘렸다는 선언만으로 Scale-out이 완료되는 것도 아닙니다.
모델 다운로드, Weight Load, Warm-up과 Health Check를 지나 실제 요청을 받을 수 있어야 합니다.
Lazy Loading은 요청이 올 때 Model을 Memory에 올리는 방식입니다.
Cold Start는 이 Download와 Weight Load,
Compile·Warm-up 때문에 첫 요청이 오래 기다리는 현상입니다.
Process가 살아 있다는 Health Check와 Model이 실제 응답 가능한 Ready 상태를 분리해야 합니다.
가장 안쪽에는 Tokenization, Batching, Scheduler, KV Cache와 Model Execution을 담당하는 Runtime이 있습니다.
vLLM, TensorRT-LLM, Triton 같은 도구는 이 경계의 일부를 구현하지만,
인증이나 Tenant별 비용 정책까지 자동으로 해결하지는 않습니다.
Quantization, Continuous Batching, Prefix Cache, Parallelism 같은 Optimization은 Runtime과 Resource Layer에 걸쳐 있습니다.
성능을 높이는 대신 품질, Memory, Queue, 통신, 운영 복잡도를 바꾸므로 독립된 기능 토글로만 보면 안 됩니다.
처음 읽을 때는 각 기법의 내부 구현보다 책임 위치를 먼저 잡으면 됩니다.
Quantization은 Weight 표현과 품질을 바꾸고,
Batching과 Prefix Cache는 Scheduler와 Memory 사용을 바꾸며,
Parallelism은 여러 GPU 사이 통신을 추가합니다.
뒤 장에서 이 기법을 자세히 볼 때도 어느 Platform Layer의 계약이 달라지는지 연결할 수 있습니다.
Security, Observability, Reliability와 Cost는 특정 Box 하나의 책임이 아닙니다.
API에서는 사용자와 Rate Limit을 보고,
Router에서는 선택된 Model과 정책을 기록하며,
Runtime에서는 Token·Queue·Cache·GPU 시간을 측정해야 하나의 요청 비용과 실패 원인을 연결할 수 있습니다.
계층형 구조의 목적은 Box를 많이 만드는 것이 아닙니다.
각 계층의 입력과 출력, 소유자, SLO, 실패 시 Fallback을 분명하게 만들어,
한 부분을 교체해도 전체 계약이 무너지지 않게 하는 데 있습니다.
다음 블록에서는 이 책임을 Kubernetes, Ray Serve와 Inference Engine에 옮겨 봅니다.
여기까지는 제품과 무관한 책임 지도입니다.
다음 Step에서는 Kubernetes와 KubeRay,
Ray Serve와 vLLM이 각각 어느 책임을 맡는지 하나의 요청 경로에 배치합니다.
Open Source로 구축한다는 말은 Kubernetes 하나를 설치한다는 뜻이 아닙니다.
요청 경로와 Control Plane을 여러 도구에 나누고,
도구 사이의 계약과 장애를 직접 운영한다는 뜻에 가깝습니다.
처음 보면 Kubernetes와 Ray,
Ray Serve,
KubeRay,
vLLM이 모두 Model Serving 도구처럼 보일 수 있습니다.
각각의 역할은 다음처럼 다릅니다.
Kubernetes : Pod와 Node, Network, Secret 같은 Container 실행 기반
KubeRay : Kubernetes에서 Ray Cluster와 RayService를 관리하는 Operator
Ray : 여러 Process와 Node에 Task·Actor를 배치하는 분산 실행 Runtime
Ray Serve : Deployment·Replica·Queue로 Online Application을 서빙하는 Layer
vLLM : Replica 안에서 LLM Token 생성과 Batching·KV Cache를 담당하는 Engine
한 도구가 다른 도구를 대체하는 관계가 아니라,
바깥 Lifecycle에서 안쪽 Model 실행으로 내려가는 조합입니다.
작은 환경에서는 일부 Layer를 생략할 수 있지만 생략한 책임이 사라지는 것은 아닙니다.
Ray의 Actor는 내부 상태를 유지하면서 여러 요청을 처리하는 Process 단위입니다.
KubeRay Operator는 Kubernetes API를 계속 관찰하며 Ray Resource를 만들고 고치는 Controller입니다.

가장 단순화한 요청 경로는 다음과 같습니다.
Client → Gateway/API → Model Router → Ray Serve Proxy
→ Deployment Replica → Inference Engine → GPU
Gateway와 API 계층은 TLS, 인증, 요청 크기, Rate Limit과 Streaming Contract를 담당합니다.
Model Router는 Tenant와 Model Policy를 Backend 선택으로 바꿉니다.
Ray Serve는 요청을 Deployment Queue와 사용 가능한 Replica로 전달하고,
Replica 안의 vLLM 같은 Inference Engine이 실제 Token 생성과 Batching을 수행합니다.
이 경로가 Data plane입니다.
반면 Kubernetes Scheduler와 KubeRay Operator,
Ray Serve Controller가 Replica를 만들고 Version을 바꾸는 경로는 Control plane입니다.
두 경로를 섞으면 요청 Timeout과 배포 장애의 원인을 같은 Log에서 찾게 됩니다.
Ray Serve의 현재 공식 Architecture 문서는 Controller, Proxy, Replica를 핵심 Actor로 설명합니다.
Request는 Proxy에서 해당 Deployment를 찾은 뒤 Queue에 들어가고,
가용한 Replica가 없으면 대기합니다.
따라서 Queue Time과 Replica 상태를 빼고 Runtime의 생성 시간만 측정하면 사용자가 기다린 원인을 놓칩니다.
Controller는 Deployment와 Replica의 원하는 상태를 관리합니다.
Proxy는 HTTP나 gRPC 요청을 받아 알맞은 Deployment Queue로 전달하고,
Replica는 실제 Application Code와 Model을 실행하는 Actor Process입니다.
현재 Ray Serve 문서는 Controller가 중단되더라도 기존 HTTP 경로가 잠시 요청을 처리할 수 있지만,
Autoscaling은 멈추고 이전 Metric이 손실될 수 있다고 설명합니다.
Control plane이 복구됐다는 사실과 Data plane이 정상적으로 응답한다는 사실을 따로 검증해야 합니다.
Ray Serve에서 Deployment는 배포·확장의 논리 단위이고,
Replica는 그 Deployment를 실행하는 개별 Actor Process입니다.
Application은 여러 Deployment를 조합한 배포 단위입니다.
예를 들어 Router → Retriever → Generator 세 Component를 하나의 Application으로 묶을 수 있습니다.
각 Component는 서로 다른 Deployment가 되고,
Generator Deployment만 GPU Replica 세 개로 확장할 수 있습니다.
Application 수와 Deployment 수,
Replica 수는 같은 값이 아닙니다.
num_replicas=3은 고정 Replica 세 개를 뜻합니다.
그 자체를 Autoscaling이라고 부르면 안 됩니다.
Autoscaling에는 최소·최대 Replica, Queue 또는 진행 중인 요청을 반영하는 정책과 Scale-out 지연을 따로 봐야 합니다.
Scale-out 결정 뒤 새 Replica가 Ready가 되기까지 Image pull과 Model download,
Weight load와 Warm-up이 필요합니다.
몇 분 뒤 Capacity가 늘어나는 System에서 현재 Queue가 그 시간을 버틸 수 있는지,
그 전에 Admission limit이나 다른 Backend가 필요한지를 같이 설계합니다.
공식 문서는 Production 설정을 Code Decorator에 모두 넣기보다 Serve Config로 관리하는 경로를 안내합니다.
하지만 구체적인 필드와 기본값은 Ray 버전에 따라 달라질 수 있으므로,
본문의 오래된 예제를 복사하기보다 설치한 버전의 API와 기동 로그를 확인해야 합니다.
한 Replica가 요청 Header의 Model ID에 따라 여러 모델을 Lazy Load할 수 있습니다.
상주 모델 수를 제한하고 LRU 방식으로 교체하면 적은 Replica로 여러 모델을 제공할 수 있지만,
Cache Miss가 발생한 요청은 Download와 Load를 기다립니다.
LRU는 가장 오랫동안 사용하지 않은 항목부터 내보내는 Cache 교체 방식입니다.
예를 들어 Replica가 Model 두 개만 담을 수 있는데 세 번째 Model 요청이 오면,
오래 사용하지 않은 Model을 내리고 새 Weight를 올립니다.
현재 Ray Serve 문서는 해당 Model이 올라간 Replica를 우선 Routing하고,
그 Replica가 과부하 상태라면 다른 Replica가 Model을 새로 Load할 수 있다고 설명합니다.
Cache Locality와 현재 부하 사이의 선택이 생기는 지점입니다.
따라서 성공 기준은 Model ID별 응답이 한 번 나오는 것이 아닙니다.
Model Load 횟수, Cache Hit와 Eviction, Process Memory, Cold Start p95,
동시에 들어온 다른 모델 요청의 지연과 실패 격리를 함께 봐야 합니다.
현재 Ray Serve의 Multiplexing API와 Header 이름도 버전 민감하므로 공식 문서로 다시 확인합니다.
Kubernetes는 Node, Pod, Service, Secret, Scheduling과 기본적인 Workload Lifecycle을 제공합니다.
KubeRay Operator는 RayCluster와 RayService 같은 Custom Resource를 Kubernetes Control Loop에 연결합니다.
RayCluster는 Ray Cluster 자체의 Lifecycle에 가깝고,
RayService는 Ray Cluster와 그 위의 Serve Application 상태를 함께 선언하는 상위 리소스입니다.
그래도 Kubernetes가 Model Selection, Token Quota, 답변 품질이나 비용 정책을 자동으로 제공하는 것은 아닙니다.
그 책임은 여전히 Gateway, Router, Application과 관측 계층에 남습니다.
Kubernetes의 CRD는 기본 Kubernetes에 없던 Resource 종류를 추가하는 정의입니다.
KubeRay Operator는 RayCluster와 RayService CR을 계속 관찰하면서,
선언한 상태와 실제 Pod·Service 상태가 같아지도록 조정합니다.
이 반복 조정 과정을 Reconciliation이라고 합니다.
KubeRay 실습은 CRD와 Operator를 설치하고 GPU Pod와 Service를 만들 수 있는 변경 작업입니다.
이번 작성 과정에서는 실행하지 않았습니다.
전용 Cluster Context, 고정된 Chart·Image·Model Revision, Secret 관리,
비용·시간 상한과 Cleanup 증거가 준비된 경우에만 선택 실습으로 진행합니다.
Open Source Stack의 장점은 Runtime과 Control Plane을 요구에 맞게 교체하고 깊게 관찰할 수 있다는 점입니다.
반대로 Version Compatibility, Upgrade, Capacity, Security Patch와 장애 대응도 직접 소유합니다.
이 질문에 답하지 못한 상태에서 도구를 많이 쌓으면 Platform이 아니라 책임의 빈틈이 늘어납니다.
Open Source Stack의 구조를 이해했다면 다음 질문은 모든 Layer를 직접 운영해야 하느냐입니다.
다음 Step에서는 통제권과 운영 책임이 함께 이동하는 Build·Buy 연속선을 살펴봅니다.
더 많은 통제권을 가져올 때 함께 떠안게 되는 운영 책임은 무엇일까?
Build와 Buy를 서로 반대편에 있는 두 선택지로 보면 실제 구조를 설명하기 어렵습니다.
관리형 API를 쓰면서 자체 Gateway와 평가 체계를 운영할 수도 있고,
직접 만든 Container를 관리형 Endpoint에 올릴 수도 있습니다.
Build는 모든 것을 처음부터 직접 개발한다는 뜻으로만 보지 않습니다.
어느 Layer의 설정과 장애 대응,
Upgrade를 우리 팀이 소유하는지가 핵심입니다.
Buy도 단순 구매가 아니라 Vendor가 운영하는 경계와 SLO를 사용하는 선택입니다.
중요한 것은 제품 이름이 아니라 어떤 책임을 Vendor에게 맡기고 어떤 책임을 직접 가져오는가입니다.

| 위치 | 주로 맡기는 것 | 직접 책임지는 것 | 대표적인 제약 |
|---|---|---|---|
| Managed Model API | Model Serving Infrastructure와 Scaling | Prompt, Application, 품질·비용 정책 | 지원 Model·기능·관측 범위 |
| Curated Model Deployment | Image와 배포 기본값 | Instance·Endpoint 설정, 사용량과 비용 | 제공 Catalog와 설정 범위 |
| Bring Your Own Model | Runtime·Endpoint 관리 일부 | Model Artifact와 호환성 | 지원 Framework·Artifact 계약 |
| Bring Your Own Code | Base Image와 Managed Endpoint | Model Load·Pre/Post-processing | Handler와 Endpoint 계약 |
| Bring Your Own Image | Endpoint Lifecycle 일부 | Image, Runtime, Patch, Serving Contract | Container 운영 책임 |
| Self-managed Platform | 거의 없음 | Cluster부터 Runtime·Policy까지 전체 | 가장 큰 운영 복잡도 |
표를 처음 볼 때는 아래 순서로 이해하면 됩니다.
아래로 내려갈수록 원하는 Kernel과 Runtime을 선택하기 쉬워집니다.
동시에 Capacity 부족과 보안 Patch,
장애 대응을 Vendor에게 넘길 수 있는 범위는 줄어듭니다.
이 표는 특정 AWS 상품의 현재 기능을 고정하는 목록이 아닙니다.
Vendor마다 경계가 다르고 같은 서비스도 기능이 바뀔 수 있습니다.
예를 들어 Amazon Bedrock도 제공된 Model만 호출하는 단순 형태를 넘어 Custom Model Import 같은 제한된 경로를 지원할 수 있으므로,
“자체 모델은 절대 불가능하다”처럼 단정하면 현재 기능과 어긋날 수 있습니다.
2026년 7월 23일에 갱신된 AWS 공식 Decision Guide는 Bedrock을 Foundation Model 기반 Application과 Agent를 위한 관리형 선택으로,
SageMaker AI를 Training, Customization과 세밀한 Inference 제어가 필요한 선택으로 설명합니다.
둘은 배타적이지 않으며 SageMaker에서 만든 Custom Model을 다른 Inference 경로로 배포하는 조합도 가능합니다.
Foundation Model은 대규모 Data로 미리 학습되어 여러 작업의 기반으로 사용하는 Model입니다.
AWS의 제품 구분을 그대로 여섯 단계 표의 정답으로 사용하지는 않습니다.
Bedrock 안에서도 Custom Model Import 같은 선택이 있고,
SageMaker AI도 관리형 Endpoint와 더 직접 통제하는 HyperPod 사이에 여러 경계가 있기 때문입니다.
표는 제품 Catalog가 아니라 책임을 질문하기 위한 학습 도구입니다.
Self-managed로 옮기기 전에 관리형 서비스가 실제로 충족하지 못하는 요구를 적어야 합니다.
막연한 Lock-in 우려나 단가 비교 하나만으로는 장기 운영비를 설명하기 어렵습니다.
예를 들어 관리형 API의 p95 TTFT가 목표를 만족하고,
필요한 Model과 Data boundary도 지원한다면 GPU 단가만 보고 Self-managed로 옮길 이유는 약합니다.
반대로 Custom Kernel이 반드시 필요하거나 요청 Trace와 배치 정책을 더 깊게 제어해야 한다면 더 낮은 Layer를 직접 소유할 근거가 생깁니다.
원문에 나온 특정 Token 가격과 3,255 TPS 손익분기점은 과거 가정의 계산입니다.
현재 가격, 입력·출력 Token 과금, Instance 비용, Utilization, Idle Time,
운영 인력과 장애 비용을 다시 넣지 않으면 지금의 선택 근거가 될 수 없습니다.
비용은 대략 다음 범위로 나누어 생각할 수 있습니다.
managed_cost
= input·output token charge
+ reserved capacity
+ network·storage·observability
self_managed_cost
= GPU·CPU·storage·network
+ idle capacity
+ platform engineering·on-call
+ upgrade·security·failure cost
두 식의 단위와 시간 범위를 맞춰야 합니다.
한쪽은 Peak Traffic의 시간당 비용으로,
다른 쪽은 평균 Token 단가로 비교하면 결론이 왜곡됩니다.
현실적인 기본 전략은 가장 적은 책임으로 요구를 충족하는 위치에서 시작하는 것입니다.
공유 API Contract와 Telemetry를 먼저 안정시키고,
관리형 경계가 실제 병목이 된 부분만 더 낮은 계층으로 가져옵니다.
예를 들어 처음에는 Managed API로 품질과 Traffic을 확인하고,
특정 Model의 호출량이 안정적으로 커졌을 때 전용 Endpoint를 검토할 수 있습니다.
그 뒤에도 Gateway와 Evaluation Dataset은 유지하면 호출 대상만 바꾸어 비교하기 쉽습니다.
반대로 직접 구축한 경계가 충분한 가치를 만들지 못한다면 다시 관리형으로 올라갈 수 있어야 합니다.
Model 호출 인터페이스, Evaluation Dataset, Trace Schema와 비용 모델을 Vendor와 분리해 두면 이동 비용을 줄일 수 있습니다.
Build vs Buy의 정답은 한 번 선택하고 끝나는 지점이 아닙니다.
Traffic, SLO, 규제, 팀 역량이 바뀔 때 소유권을 이동할 수 있도록 설계하는 문제입니다.
선택한 위치가 맞았는지는 Architecture 취향으로 증명할 수 없습니다.
다음 Step에서는 관리형과 Self-managed를 같은 Workload와 지표로 비교하는 방법을 정리합니다.
Architecture Diagram이 깔끔하다는 사실은 사용자의 요청이 빨라졌다는 증거가 아닙니다.
관리형 서비스로 옮겼거나 Replica를 늘렸다는 사실도 비용과 SLO가 좋아졌다는 증거가 아닙니다.
변경 전후를 같은 Workload와 같은 지표로 비교해야 선택의 효과를 설명할 수 있습니다.

단일 Streaming 호출에서는 다음 지표가 서로 다른 구간을 설명합니다.
Streaming은 완성된 답변을 한 번에 보내지 않고 생성되는 Token을 순서대로 전달하는 방식입니다.
사용자는 TTFT로 첫 반응을 느끼고,
ITL로 이후 글자가 나타나는 리듬을 느낍니다.
Batch 작업이나 다음 Agent Step은 대개 전체 응답이 끝나는 E2E를 기다립니다.
ITL이 일정하고 부가 시간을 단순화하면 출력 Token 수 N에 대해 다음처럼 근사할 수 있습니다.
E2E ≈ TTFT + ITL × (N - 1)
실제 측정에서는 Tokenization, Queue, Network, Streaming Buffer와 마지막 응답 처리가 들어갈 수 있습니다.
도구마다 ITL과 TPOT의 계산 방식도 다를 수 있으므로 Metric 이름만 비교하지 않고 시작·종료 Timestamp와 분모를 확인해야 합니다.
설명용으로 TTFT가 0.8초이고 ITL이 0.05초,
출력이 101 Token이라면 다음과 같습니다.
E2E ≈ 0.8 + 0.05 × (101 - 1)
= 5.8초
첫 Token을 0.4초 빠르게 만들어도 나머지 100개 Token의 간격이 같다면 전체는 5.4초입니다.
반대로 ITL을 0.01초 줄이면 전체에서 약 1초를 줄일 수 있습니다.
어떤 지표를 우선할지는 Output 길이와 사용자가 결과를 소비하는 방식에 따라 달라집니다.
Agent Task는 여러 호출과 Tool을 포함합니다.
Agent E2E = Σ(Model Queue + Model E2E)
+ Σ(Tool·Retrieval Time)
+ Orchestration·Retry overhead
병렬 Step이 있다면 단순 합이 아니라 Critical Path를 계산해야 합니다.
최종 응답이 이전 단계의 전체 출력을 기다리는 구조에서는 개별 호출의 TTFT를 개선해도 Task E2E 변화가 작을 수 있습니다.
설명용 Agent가 직렬로 1.2, 0.4, 2.3, 0.8, 1.7초인 다섯 Step을 실행하고,
Orchestration에 0.6초를 쓴다면 전체는 7.0초입니다.
가장 느린 Model 호출 하나만 보는 대신 이 일곱 초가 어느 Span에서 사용됐는지 Trace로 확인합니다.
Trace는 하나의 Task가 여러 Service를 지나는 과정을 공통 ID와 시간축으로 연결한 기록입니다.
각 Model과 Tool 호출은 Span이 되고,
부모·자식 관계를 보면 직렬 경로와 병렬 경로를 구분할 수 있습니다.
RPS는 단위 시간에 완료한 요청 수를 보여주지만 요청마다 입력과 출력 길이가 다르면 작업량이 다릅니다.
Output TPS는 생성한 Token 처리량을 보기 좋지만,
입력을 짧게 만들거나 Batch가 잘 맞는 요청만 사용하면 실제 Traffic보다 높게 보일 수 있습니다.
RPS는 Requests per second,
TPS는 Tokens per second입니다.
60초 동안 요청 120건과 Output Token 4,800개를 처리했다면 RPS는 2이고 Output TPS는 80입니다.
이 숫자만으로 각 요청이 짧았는지,
사용자가 얼마나 기다렸는지는 알 수 없습니다.
따라서 Benchmark 결과에는 최소한 다음 조건을 함께 남깁니다.
| 범주 | 고정하거나 기록할 값 |
|---|---|
| Model | ID, Revision, dtype, Runtime와 Version |
| Input | Prompt Dataset, Token 길이 분포, Context 재사용률 |
| Output | max_tokens, 실제 길이 분포, Sampling 설정 |
| Load | Request 수, Concurrency, Arrival Pattern, Duration |
| Environment | GPU·CPU, Topology, Replica, Region·Network |
| Measurement | Warm-up, 측정 구간, 반복 수, Error·Timeout 처리 |
Arrival Pattern도 중요합니다.
Closed-loop 부하는 이전 요청이 끝난 뒤 다음 요청을 보내므로 Server가 느려지면 입력률도 함께 줄어듭니다.
Open-loop 부하는 정해진 시간표대로 요청을 보내므로 Capacity를 넘을 때 Queue가 어떻게 자라는지 더 잘 드러냅니다.
두 결과를 같은 RPS 숫자로 직접 비교하지 않습니다.
평균만 보면 Queue가 길어진 일부 요청을 숨길 수 있습니다.
p50과 함께 p95·p99, Error Rate와 Timeout을 기록하고,
Agent Workflow라면 완료한 Step이 아니라 SLO 안에서 성공한 전체 Task 수도 따로 봅니다.
p50은 요청 절반이 그 값 이하였다는 중앙값이고,
p95는 95%가 그 값 이하였다는 Tail 지표입니다.
요청이 20개뿐이라면 p95가 한두 요청에 크게 흔들릴 수 있으므로 Sample 수와 최대값도 함께 남깁니다.
SLO를 모두 만족한 처리량은 Goodput으로 따로 볼 수 있습니다.
goodput = SLO 안에서 성공한 전체 Task 수 / 측정 시간
Retry 끝에 성공했더라도 Deadline과 비용 상한을 넘었다면 Goodput에서는 실패로 처리할 수 있습니다.
Streaming Chat은 사용자가 답변이 시작됐다고 느끼는 TTFT가 중요할 수 있습니다.
긴 출력을 읽는 경험에서는 ITL이 더 크게 느껴집니다.
앞 단계의 전체 결과를 받아 다음 작업을 수행하는 Agent나 Batch Pipeline은 Task E2E와 Throughput이 더 중요할 수 있습니다.
어떤 지표든 낮거나 높을수록 무조건 좋은 것은 아닙니다.
Latency를 줄이기 위해 작은 Batch만 유지하면 비용과 Throughput이 나빠질 수 있고,
Throughput을 높이기 위해 Queue를 오래 모으면 Interactive SLO를 놓칠 수 있습니다.
비교 전에는 채택 조건을 문장으로 고정합니다.
예를 들어 p95 Agent E2E 8초 이하와 Error rate 1% 미만을 지키면서 Task goodput을 높인다처럼 적습니다.
이 조건이 있어야 TPS를 높이기 위해 Tail latency나 실패를 숨기지 않습니다.
좋은 Benchmark는 가장 큰 숫자를 만드는 시험이 아닙니다.
Production Traffic을 닮은 입력에서 변경한 노브 하나가 어떤 지표와 비용을 움직였는지 설명하고,
나빠졌을 때 되돌릴 기준을 남기는 실험입니다.
이제 다섯 Step이 하나로 이어집니다.
Agent 호출 Graph를 이해하고,
Platform 책임을 나눈 뒤,
그 책임을 Open Source나 Managed 경계에 배치하고,
마지막에는 같은 SLO와 Workload로 선택을 다시 검증합니다.
CH04는 Agent Framework나 Cloud Service의 기능 목록을 고르는 장이 아닙니다.
한 번의 사용자 요청이 여러 Model, Tool과 Retrieval 호출로 이어질 때,
그 전체 경로의 SLO와 비용을 누가 책임질지 정하는 장에 가깝습니다.
Model Runtime만 빠르게 만들어도 Agent의 앞 단계가 Queue에서 기다리거나 Tool이 Timeout 나면 Task는 끝나지 않습니다.
반대로 모든 계층을 직접 소유하면 세밀하게 조정할 수 있지만,
Cluster Capacity, Upgrade, Security Patch와 장애 대응도 직접 감당해야 합니다.
그래서 선택하기 전에 다음 질문부터 남기는 편이 좋습니다.
Agent 시대의 LLM Serving은 모델 한 번을 빠르게 실행하는 문제가 아닙니다.
반복되는 호출 전체의 SLO와 비용을 지키기 위해 책임 경계를 나누고,
무엇을 직접 소유할지 계속 조정하는 Platform 설계입니다.