네. 지금 환경이라면 GPU Cluster를 단순 “vLLM 서버가 있는 K8s”가 아니라 독립적인 AI Serving Platform으로 설계하는 것이 좋습니다.
특히 중요한 전제는 다음입니다.
이 조건이라면 아래 구조를 1차 목표 아키텍처로 권합니다.
┌───────────────────────────────┐
│ USERS / APPS │
│ │
│ Jupyter / Spark / BI / Agent │
│ Discovery / Wizard / Batch │
└───────────────┬───────────────┘
│
HTTPS / OpenAI API
│
┌──────────────────── COMPUTE CLUSTER ─┼─────────────────────────┐
│ │ │
│ JupyterLab Spark StarRocks Trino Polaris Applications│
│ │ │
│ Namespace / Team Identity / ServiceAccount │
└──────────────────────────┬───────────┬─────────────────────────┘
│ │
Cilium ClusterMesh
Native Routing
│
CiliumNetworkPolicy / Identity
│
▼
┌────────────────────── GPU CLUSTER ──────────────────────────────┐
│ │
│ ┌────────────────────┐ │
│ │ Inference Gateway │ │
│ │ Gateway API │ │
│ │ Auth / Quota │ │
│ │ Rate Limit │ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ Inference Router │ │
│ │ / EPP │ │
│ │ │ │
│ │ model │ │
│ │ queue │ │
│ │ KV/cache locality │ │
│ │ endpoint load │ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌──────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ONLINE POOL SMALL MODEL DOCUMENT POOL │
│ │
│ DeepSeek V4.1 Qwen3.6 PaddleOCR │
│ GLM-5.3-Flash gpt-oss Docling │
│ │
│ │ │ │ │
│ └──────────────┼───────────────┘ │
│ │ │
│ GPU Scheduler │
│ │ │
│ ┌──────────────▼──────────────┐ │
│ │ B300 NODE #1 │ │
│ │ │ │
│ │ GPU0 GPU1 GPU2 GPU3 │ │
│ │ GPU4 GPU5 GPU6 GPU7 │ │
│ └─────────────────────────────┘ │
│ │
│ DCGM / Prometheus / vLLM metrics / Hubble / OTel │
└───────────────────────────┬────────────────────────────────────┘
│
Cilium ClusterMesh
│
▼
┌──────────────────── STORAGE CLUSTER ────────────────────────────┐
│ │
│ MinIO / AIStor │
│ │
│ model checkpoints │ documents │ results │ artifacts │ KV │
│ │
└────────────────────────────────────────────────────────────────┘
Cilium ClusterMesh는 pod-to-pod connectivity, cross-cluster service discovery/load balancing, cluster-aware network policy를 지원합니다. Native routing에서는 underlying network가 PodCIDR을 실제로 route할 수 있어야 하고 각 클러스터 PodCIDR도 겹치면 안 됩니다. Cilium 문서
이 구조는 피하는 게 좋습니다.
Jupyter ──────────────→ vLLM pod
Spark ────────────────→ vLLM pod
Discovery ────────────→ vLLM pod
Team A ───────────────→ vLLM pod
Team B ───────────────→ vLLM pod
처음에는 간단하지만 멀티테넌트 환경에서는 곧 문제가 됩니다.
대신 모든 요청을:
Client
↓
Inference Gateway
↓
Model Router
↓
InferencePool
↓
vLLM
로 통일하는 것을 권합니다.
2026년 기준 Kubernetes Gateway API Inference Extension은 self-hosted generative model용으로 Inference Gateway → InferencePool → Endpoint Picker(EPP) 구조를 제공합니다. Endpoint selection 때 serving metrics와 prefix-cache 같은 capability도 활용할 수 있도록 설계되어 있습니다. Kubernetes 게이트웨이 API 확장
예를 들면:
gpu-system
├─ NVIDIA GPU Operator
├─ DCGM Exporter
└─ node components
inference-gateway
├─ Gateway
├─ EPP / Router
├─ quota
└─ admission
inference-online
├─ deepseek-v4.1-flash
└─ glm-5.3-flash
inference-small
├─ qwen3.6-35b
└─ gpt-oss-20b
inference-document
├─ paddleocr
└─ docling
inference-batch
└─ glm-5.3
observability
├─ Prometheus
├─ Grafana
├─ OpenTelemetry
└─ alerting
validation
└─ Stage1~7 benchmark jobs
이렇게 서비스 클래스별 namespace로 분리하는 것이 팀별 namespace보다 GPU capacity 관리가 쉽습니다.
팀 identity는 Gateway/auth/quota 계층에서 따로 관리합니다.
예를 들어:
Team Discovery
│
├─ 40 RPS
├─ concurrency 20
└─ Priority HIGH
Team Ontology
│
├─ 5 RPS
├─ concurrency 4
└─ Priority LOW/BATCH
Team Wizard
│
├─ 20 RPS
├─ concurrency 10
└─ Priority NORMAL
Jupyter users
│
├─ 2 RPS/user
└─ Priority LOW
처럼 관리합니다.
GPU 자원 자체를 사용자에게 나누는 것보다:
requests/sec
concurrent requests
input tokens/min
output tokens/min
context length
queue depth
를 quota 단위로 삼는 게 inference에서는 훨씬 의미가 있습니다.
1-node 시점의 초기 qualification topology는 이런 형태가 좋습니다.
B300 NODE #1
┌────────────────────────────────────────────┐
│ │
│ GPU0 ─┐ │
│ GPU1 │ │
│ GPU2 ├── Online Primary │
│ GPU3 ─┘ DeepSeek-V4.1 / GLM-5.3-Flash │
│ TP4 candidate │
│ │
│ GPU4 ───── Qwen3.6 TP1 │
│ │
│ GPU5 ───── gpt-oss TP1 │
│ │
│ GPU6 ───── Document Pipeline │
│ PaddleOCR / Docling │
│ │
│ GPU7 ───── Burst / spare / document replica│
│ │
└────────────────────────────────────────────┘
다만 이것은 고정 configuration이 아닙니다.
v12 Stage7 결과가:
DeepSeek
TP2 FAIL
TP4 PASS + 30% headroom
TP8 PASS
→ TP4
라면 GPU0~3을 배정하는 식입니다.
1-node밖에 없는 상황에서는 GPU 8개를 평상시에 100% 예약하지 않는 것이 중요합니다.
GPU7 정도는:
burst capacity
document replica
failed workload retry
model qualification
canary
temporary batch
용으로 남기는 편이 운영상 가치가 큽니다.
즉 정상 운영 목표를:
steady state
GPU reservation ≈ 75~87.5%
peak
GPU reservation = 100%
정도로 잡는 게 낫습니다.
Ontology는 야간이라고 했으므로 이 특성을 적극적으로 이용해야 합니다.
주간:
GPU0-3 Discovery
GPU4 Qwen
GPU5 gpt-oss
GPU6 Document
GPU7 Spare/Burst
야간에는 필요하면:
drain / scale-down
↓
GPU0 ─────────────────────┐
GPU1 │
GPU2 │
GPU3 │
GPU4 ├─ GLM-5.3 TP8
GPU5 │
GPU6 │
GPU7 ─────────────────────┘
↓
ontology batch
↓
scale-down
↓
daytime topology
로 전환합니다.
이건 Kubernetes CronJob 하나보다는 queue-aware scheduler/controller 형태로 발전시키는 게 좋습니다.
우선순위는 명확하게 두는 것을 권합니다.
PriorityClass
inference-critical 100000
inference-online 50000
inference-document 20000
inference-batch 5000
inference-validation 1000
하지만 GPU pod를 무조건 Kubernetes preemption으로 죽이는 구조는 권하지 않습니다.
먼저:
Gateway admission
↓
queue control
↓
scale decision
↓
graceful drain
↓
GPU reassignment
순서로 처리하고 K8s preemption은 마지막 보호수단으로 두는 편이 안전합니다.
각 model server는 v12가 만든 production profile을 사용합니다.
Stage7
↓
TP sweep
↓
adaptive saturation
↓
SLO/headroom
↓
production profile
↓
Helm values
예를 들어:
deepseek:
gpu: 4
vllm:
tensorParallelSize: 4
maxModelLen: 131072
maxNumSeqs: 32
maxNumBatchedTokens: 8192
gpuMemoryUtilization: 0.90
prefixCaching: true
kvCacheDtype: fp8
같은 값은 Git에 사람이 작성하는 값이 아니라 Stage7 qualification 결과에서 승격된 값으로 만드는 것이 좋습니다.
추천 구조는:
ai-serving-config/
│
├── models/
│ ├── deepseek-v4.1-flash/
│ │ ├── qualification.yaml
│ │ ├── production-profile.yaml
│ │ └── values.yaml
│ │
│ ├── glm-5.3-flash/
│ ├── qwen3.6-35b/
│ └── gpt-oss-20b/
│
├── gateway/
│
├── policies/
│
├── quotas/
│
└── environments/
├── validation
└── production
가 좋습니다.
변경 절차는:
new checkpoint / vLLM
↓
Stage7
↓
qualification
↓
production-profile.yaml
↓
Git PR
↓
review
↓
Helm / GitOps
↓
canary
↓
production
가 됩니다.
이 부분은 이미 Stage3a10 설계와 잘 연결됩니다.
MinIO / AIStor
│
source of truth
│
┌──────────────┴───────────────┐
│ │
▼ ▼
model checkpoint documents/data
│
▼
GPU node local NVMe
│
model cache
│
▼
vLLM
모델을 매번 AIStor에서 직접 읽어서 시작하지 말고, GPU node local NVMe에 cache를 두는 게 좋습니다.
AIStor
↓
model sync
↓
local NVMe
↓
checksum/revision verification
↓
vLLM startup
구조를 권합니다.
초기에는:
GPU HBM KV
↓
Local NVMe KV
↓
Remote AIStor/MemKV
순으로 생각하면 됩니다.
하지만 Stage3a10/Stage7에서 GPU KV만으로 SLO와 capacity가 충분하면 굳이 remote tier를 사용하지 않습니다.
GPU-only PASS
↓
사용
GPU-only capacity 부족
↓
local NVMe
local도 부족 / reuse 요구
↓
remote
가 좋습니다.
현재 RDMA가 없으므로 inference 요청과 storage traffic을 구분해서 보는 게 중요합니다.
Ethernet Fabric
│
┌────────────┼─────────────┐
│ │ │
Service Storage ClusterMesh
traffic traffic traffic
│ │ │
HTTP/S3 S3/TCP Pod TCP/IP
가능하다면 VLAN/VRF 또는 최소한 routing/QoS 관점에서:
Management
Pod/Service
Storage
traffic을 분리하는 것을 권합니다.
3개 cluster PodCIDR은 반드시 겹치지 않게 잡습니다.
예:
Compute
10.10.0.0/16
GPU
10.20.0.0/16
Storage
10.30.0.0/16
그리고 underlying network에서 이 PodCIDR이 실제로 route되어야 합니다.
Cilium native routing은 encapsulation 없이 Linux routing을 사용하기 때문에 물리 네트워크가 PodCIDR reachability를 제공해야 합니다. ClusterMesh에서도 모든 cluster의 PodCIDR은 unique해야 하고 native-routing CIDR은 연결된 PodCIDR들을 포괄해야 합니다. Cilium 문서
ClusterMesh는 연결성을 제공할 뿐입니다.
보안은:
default deny
+
explicit allow
를 권합니다.
예:
Compute
│
│ HTTPS
▼
Inference Gateway
Inference Gateway
│
│ HTTP
▼
Inference Pool
Inference Pods
│
│ S3
▼
AIStor
만 허용합니다.
Compute → vLLM Pod 직접 접근은 차단합니다.
Cilium은 cross-cluster endpoint identity를 사용한 정책을 지원하지만, 정책 자체는 각 클러스터에 배포해야 합니다. Cilium 문서
여기서는 Hubble을 적극적으로 쓰는 것이 좋습니다.
Compute
↓
Gateway
↓
vLLM
↓
AIStor
흐름에서:
latency
drops
TCP reset
retransmission
denied flow
service dependency
를 봅니다.
Hubble Relay를 사용하면 cluster 단위뿐 아니라 ClusterMesh 환경의 multi-cluster visibility도 제공할 수 있습니다. Cilium 문서
DCGM
GPU utilization
HBM
power
temperature
XID
ECC
NVLink
TTFT
ITL
queue
running requests
waiting requests
KV usage
prefix cache hit
preemption
tokens/sec
vLLM은 /metrics를 통해 KV usage, prefix-cache query/hit, running/waiting request 등의 production metric을 제공합니다. vLLM
RPS
P50/P95/P99
429
5xx
timeout
queue depth
tokens/min
team
user
application
model
requests
input tokens
output tokens
GPU cost
latency
errors
입니다.
운영자가 가장 먼저 봐야 하는 화면은:
GPU CLUSTER CAPACITY
B300 GPUs
8 total
Allocated 6
Reserved 1
Available 1
──────────────────────
DeepSeek TP4
GPU 4
RPS 22 / capacity 31
C 24 / SLO max 48
KV 67%
P99 TTFT 1.4s
Qwen
GPU 1
RPS 6 / 20
Document
GPU 1
pages/min 180 / 350
──────────────────────
Cluster headroom
GPU 25%
Online RPS 34%
KV 33%
같은 형태여야 합니다.
GPU utilization 90% 하나만 보고 capacity를 판단하면 안 됩니다.
여기서 지금 설계를 잘해놓은 효과가 나타납니다.
GPU CLUSTER
Inference Gateway
│
Inference Router
│
┌────────┴────────┐
│ │
▼ ▼
B300 NODE #1 B300 NODE #2
8 GPU 8 GPU
Online Online
Small Batch
Document Spare
또는 online HA 관점에서는:
DeepSeek
Node1
GPU0-3
Replica A
Node2
GPU0-3
Replica B
▲
│
InferencePool
│
EPP
가 됩니다.
InferencePool은 label selector로 endpoint pod들을 묶고 EPP가 그 endpoint들 중 요청을 보낼 곳을 선택하는 모델입니다. Kubernetes 게이트웨이 API 확장
RDMA가 없는 상태라면 특히 그렇습니다.
즉:
BAD DEFAULT
Node1 GPU0-3
│
TCP/IP
│
Node2 GPU0-3
TP8
보다:
PREFERRED
Node1
TP4 replica A
Node2
TP4 replica B
를 먼저 검토합니다.
모델이 한 노드에 들어가는 한 tensor parallel communication을 node 내부 NVLink/NVSwitch domain에 유지하는 편이 일반적으로 유리합니다.
두 노드가 필요한 초대형 모델은 Stage7에서 별도로 TCP distributed inference 성능을 qualification 해야 합니다.
Gateway
│
Inference Router
│
┌──────────────┴──────────────┐
│ │
NODE #1 NODE #2
8 × B300 8 × B300
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ │ │ │
Online A Small Online B Batch
TP4 models TP4 GLM
│ │ │ │
└─────────────┴──────┬───────┴─────────────┘
│
AIStor
이렇게 하면 node 한 대가 장애 나더라도 주요 online service를 다른 node에서 유지할 수 있는 구조로 발전시킬 수 있습니다.
제가 이 환경에서 가장 중요하게 보는 구조는 이것입니다.
┌──────────────┐
│ Real Traffic │
└──────┬───────┘
│
▼
Prometheus / OTel
│
┌─────────────────┼─────────────────┐
│ │ │
SLO Capacity Quality
│ │ │
└─────────────────┼─────────────────┘
▼
Stage7
│
TP / C / KV tuning
│
▼
production-profile.yaml
│
▼
Helm values
│
▼
GitOps
│
▼
GPU Deployment
│
▼
Stage7D validation
즉 Stage7이 단순 benchmark 도구가 아니라 GPU Serving Platform의 capacity planner 역할을 하게 만드는 겁니다.
현재 1-node에서는 GPU를 Kubernetes scheduler에게 완전히 자유롭게 맡기기보다 Stage7에서 검증된 “GPU allocation profile” 단위로 운영하는 것을 권합니다.
그리고 architecture를 크게 세 plane으로 분리하면 관리하기 좋습니다.
┌──────────────── CONTROL PLANE ────────────────┐
│ │
│ Gateway API / InferencePool / EPP │
│ GitOps / Helm │
│ Quota / Admission / Priority │
│ Stage7 Qualification │
└──────────────────────┬────────────────────────┘
│
┌──────────────── DATA PLANE ───────────────────┐
│ │
│ vLLM / PaddleOCR / Docling │
│ B300 GPU │
│ Local NVMe model/KV cache │
└──────────────────────┬────────────────────────┘
│
┌──────────── OBSERVABILITY PLANE ──────────────┐
│ │
│ DCGM │
│ vLLM metrics │
│ Prometheus / Grafana │
│ OpenTelemetry │
│ Cilium Hubble │
│ Tenant usage / capacity / SLO │
└───────────────────────────────────────────────┘
이 구조라면 지금 8 GPU 한 노드에서도 과하지 않고, 6개월 뒤 16 GPU/2-node가 되었을 때 Gateway/EPP/InferencePool 아래에 replica와 capacity만 추가하면 되기 때문에 아키텍처를 다시 설계할 필요가 없습니다.
다음 단계에서는 이 아키텍처를 실제 배포 구조로 내려서 namespace 설계 + Gateway API/InferencePool + CiliumNetworkPolicy + PriorityClass/ResourceQuota + Prometheus/DCGM/Hubble + 모델별 GPU 배치 + 주간/야간 전환까지 포함한 K8s 논리/물리 아키텍처 다이어그램과 YAML 설계로 구체화하는 것이 좋습니다.
네. 그러면 이전 설계에서 Cilium ClusterMesh를 클러스터 간 연결의 전제로 둔 부분을 빼는 것이 맞습니다.
현재 네트워크를 이렇게 이해하겠습니다.
Compute Cluster ─┐
├── Private Network ── Storage Cluster
Storage Cluster ─┘
AI/GPU Cluster
B300 Node
├─ Private Network 없음
└─ Public Network만 존재
이 경우 GPU Cluster를 기존 클러스터와 억지로 하나의 flat pod network로 만들기보다, GPU Cluster를 독립된 서비스 존(Service Zone) 으로 두고 L3/L7 API + Storage endpoint를 통해 연결하는 구조를 권합니다.
Existing Private Network
┌─────────────────────────────────────────────────────────┐
│ │
│ ┌──────────── COMPUTE CLUSTER ──────────────┐ │
│ │ JupyterLab / Spark / Trino / StarRocks │ │
│ │ Polaris / Applications │ │
│ └─────────────────┬─────────────────────────┘ │
│ │ │
│ │ Private NW │
│ │ │
│ ┌─────────────────▼─────────────────────────┐ │
│ │ STORAGE CLUSTER │ │
│ │ │ │
│ │ MinIO / AIStor │ │
│ └─────────────────┬─────────────────────────┘ │
│ │ │
└────────────────────┼────────────────────────────────────┘
│
Controlled L3/L4
TCP/IP / TLS
│
───────── Firewall ─────────
│
│ Public/Service Network
│
┌────────────────────▼────────────────────────────────────┐
│ GPU / AI CLUSTER │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Inference Gateway │ │
│ │ TLS / Auth / Quota / Rate Limit │ │
│ └──────────────────────┬────────────────────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Inference Router │ │
│ │ EPP / Model Route │ │
│ └─────────┬─────────┘ │
│ │ │
│ ┌───────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ Online Pool Small LLM Document │
│ DeepSeek Qwen PaddleOCR │
│ GLM Flash gpt-oss Docling │
│ │
│ B300 × 8 / Local NVMe │
│ │
│ DCGM / Prometheus / OTel / Hubble │
└────────────────────────────────────────────────────────┘
핵심 변화는 Compute → GPU Pod 직접 연결을 없애는 것입니다.
Compute 쪽에서는 오직:
https://ai-api.company.internal/v1/...
같은 Inference Gateway endpoint 하나만 알도록 만드는 편이 좋습니다.
GPU Cluster와 기존 Compute/Storage Cluster 사이에는 사용하지 않습니다.
대신 각 클러스터 내부에서는 Cilium을 계속 사용합니다.
Compute K8s
└─ Cilium native
Storage K8s
└─ Cilium native
GPU K8s
└─ Cilium native
Compute ↔ Storage가 현재 ClusterMesh로 잘 연결되어 있다면 그 부분은 유지할 수 있습니다.
Compute ←── ClusterMesh/Private NW ──→ Storage
X
│
ClusterMesh
│
X
GPU
GPU Cluster는 별도 security/network domain으로 봅니다.
사용자가 GPU pod까지 직접 접근할 필요가 없기 때문입니다.
Jupyter ──────┐
Spark ────────┤
Discovery ────┤
Wizard ───────┼──→ AI Gateway ─→ Router ─→ vLLM
Ontology ─────┤
Team A ───────┤
Team B ───────┘
이렇게 하면 Gateway에서:
Identity
↓
Authentication
↓
Team quota
↓
Rate limit
↓
Concurrency limit
↓
Model authorization
↓
Inference routing
을 모두 통제할 수 있습니다.
즉 private network가 없는 것이 inference API 관점에서는 치명적인 문제가 아닙니다.
Inference API는 HTTPS라 public/service network를 거쳐도 상대적으로 부담이 적지만, GPU node가 MinIO/AIStor에서 대형 checkpoint를 읽는 것은 얘기가 다릅니다.
예를 들어:
GPU Node
│
│ TCP/IP
│
▼
Firewall/Public NW
│
▼
AIStor
에서 300~700GB 모델을 pod restart 때마다 가져오면 문제가 됩니다.
그래서 GPU node의 Local NVMe cache가 더 중요해집니다.
AIStor
│
TCP/IP + TLS
│
▼
GPU Local NVMe
Model Cache
│
┌───────────┴──────────┐
│ │
model A model B
│ │
▼ ▼
vLLM vLLM
AIStor는 source of truth이고, 실제 serving은 local NVMe를 사용합니다.
Model Registry / AIStor
│
│ download once
▼
GPU Local NVMe
/models/
├─ deepseek-v4.1-flash/<revision>
├─ glm-5.3-flash/<revision>
├─ qwen3.6/<revision>
└─ gpt-oss/<revision>
↓
checksum / revision validation
↓
vLLM
그래야 network 장애가 발생해도 이미 cache된 모델은 계속 serving할 수 있습니다.
이전 Stage3a10의:
GPU HBM
↓
Local NVMe
↓
Remote MinIO/MemKV
중 Remote tier의 우선순위를 더 낮추는 게 좋습니다.
현재 topology라면:
GPU
│
│ public/service TCP/IP
▼
AIStor
이므로 KV path에 network latency와 bandwidth 영향을 직접 받습니다.
따라서 초기 production은:
1순위 GPU HBM KV
↓
2순위 GPU node Local NVMe
↓
3순위 Remote KV
(Stage3a10 PASS 시에만)
를 권합니다.
특히 online Discovery는 remote KV 없이 SLO가 나오면 그대로 두는 게 좋습니다.
GPU node에 NIC가 하나뿐이어도 traffic class는 분리해서 관리할 필요가 있습니다.
GPU Node
│
┌───────────┼───────────┐
│ │
▼ ▼
Inference Traffic Storage Traffic
HTTPS/API S3/TCP
small packets large sequential
latency sensitive bandwidth sensitive
│ │
▼ ▼
Gateway AIStor/MinIO
즉 같은 물리 NIC라도 firewall/QoS/monitoring에서 별도 flow로 봐야 합니다.
가능하다면 장기적으로 GPU node에 private/storage NIC 추가를 가장 우선적인 네트워크 개선으로 권합니다.
"public network"가 실제 인터넷 접근 가능한 public IP 의미라면 특히 중요합니다.
구조를:
Internet/Public
│
▼
Firewall / LB
│
▼
Inference Gateway
│
X
vLLM 직접 접근 불가
로 만들어야 합니다.
다음 포트들은 외부에 직접 노출하지 않는 것을 권합니다.
vLLM :8000
Prometheus metrics
DCGM metrics
Kubernetes API
kubelet
etcd
Hubble
NodePort
MinIO credentials/API
외부에 노출되는 것은 가능하면 443/TCP → Gateway 하나로 제한합니다.
Compute Cluster에서는:
Jupyter Pod
│
│ HTTPS
▼
AI Gateway VIP/FQDN
│
▼
GPU Cluster
만 허용합니다.
예:
api.ai.example.internal
DNS는 private DNS에 있어도 괜찮습니다.
중요한 것은 그 FQDN이 GPU cluster의 Gateway/LB endpoint를 가리키도록 하는 것입니다.
GPU가 Storage를 호출합니다.
GPU Node
│
│ TLS / S3
▼
AIStor endpoint
따라서 firewall은 최소한:
Compute → GPU
HTTPS 443
GPU → Storage
S3 HTTPS 443
GPU → DNS
DNS
GPU → NTP
NTP
GPU → Registry
HTTPS
GPU → Observability
필요 포트
정도로 좁힙니다.
Storage → GPU inbound는 기본적으로 필요 없습니다.
Prometheus가 기존 Compute Cluster에 있다고 GPU Cluster의 private pod IP를 scrape하려고 하면 문제가 생깁니다.
따라서 중앙 observability가 필요하다면:
GPU Cluster
DCGM
vLLM
Cilium
K8s
↓
Local Prometheus
↓
remote_write
↓
Central Metrics
가 좋습니다.
즉:
BAD
Central Prometheus
│
└────→ 모든 GPU Pod scrape
보다:
GOOD
GPU Local Prometheus
│
│ remote_write TLS
▼
Central Metrics
를 권합니다.
로그/trace도 비슷하게:
GPU OTel Collector
│
▼
Central OTel / Logging
형태로 GPU → 중앙 outbound 중심으로 만듭니다.
중요한 점은 외부 연결 구조가 바뀌었을 뿐, GPU cluster 내부 architecture는 거의 바뀌지 않습니다.
┌──────────── GPU CLUSTER ────────────────┐
│ │
│ External HTTPS │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Gateway API │ │
│ │ Auth / Quota │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Inference EPP │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────┼─────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Online Small Document │
│ │
│ ┌─────────────────────────────────┐ │
│ │ B300 × 8 │ │
│ │ │ │
│ │ TP4 │ TP1 │ TP1 │ Document │ │ │
│ └─────────────────────────────────┘ │
│ │ │
│ Local NVMe │
│ │
│ Prometheus / DCGM / OTel / Hubble │
│ │
└────────────────────────────────────────┘
두 번째 node는 같은 GPU Kubernetes cluster에 넣습니다.
AI Gateway
│
Inference EPP
│
┌────────┴────────┐
│ │
▼ ▼
B300 Node #1 B300 Node #2
8 GPU 8 GPU
TP4 A TP4 B
│ │
└──── replicas ───┘
두 GPU node가 같은 L2/L3 network에서 직접 통신 가능하게 구성하는 것은 매우 중요합니다.
특히 Node #2를 추가할 때는 GPU node 간 전용 private/backend network를 같이 추가하는 것을 강하게 권합니다.
장기 목표는:
GPU NODE #1 GPU NODE #2
Public/Service NIC Public/Service NIC
│ │
└────── API traffic ───────────┘
Backend/Private NIC Backend/Private NIC
│ │
├──── K8s / Cilium ────────────┤
├──── NCCL ────────────────────┤
├──── model distribution ──────┤
└──── Storage ─────────────────┘
가 되어야 합니다.
RDMA까지 당장 갈 필요는 없더라도 GPU node간 backend Ethernet은 만드는 편이 좋습니다.
따라서 전체 시스템을 다음처럼 잡겠습니다.
┌─────────────────────┐
│ Users / Applications│
└──────────┬──────────┘
│
┌──────── PRIVATE ─────────────┼─────────────────────────┐
│ │ │
│ Compute Cluster │
│ Jupyter/Spark/Trino/... │
│ │ │
│ Private Network │
│ │ │
│ Storage Cluster │
│ MinIO/AIStor │
│ │ │
└──────────────────────────────┼─────────────────────────┘
│
Controlled TCP/TLS
│
Firewall / Routing
│
┌──────── GPU SERVICE ZONE ────▼─────────────────────────┐
│ │
│ AI Gateway :443 │
│ │ │
│ Auth / Quota / Rate │
│ │ │
│ Inference EPP │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ Online Small Document │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ B300 × 8 │
│ │ │
│ Local NVMe │
│ │ │
│ Model / KV local cache │
│ │
│ DCGM / Prometheus / OTel / Hubble │
│ │ │
│ remote_write │
└─────────────────────────┼─────────────────────────────┘
│
outbound TLS
│
▼
Central Observability
정리하면 ClusterMesh를 포기한다고 전체 아키텍처가 약해지는 게 아닙니다. 오히려 GPU Cluster를 API 기반의 별도 security zone으로 만들고, 443 Gateway를 명확한 ingress 경계로 두는 것이 현재 물리 네트워크에는 더 자연스럽습니다.
그리고 지금 환경에서 네트워크 투자 우선순위를 하나만 고른다면 RDMA보다 먼저 GPU node에 private/backend network를 추가하는 것을 권합니다. Node #2가 들어오기 전에 해두면 GPU↔GPU, GPU↔Storage, K8s/Cilium control/data traffic의 기반이 생기고, 이후 필요하면 그 backend network를 RDMA/RoCE로 발전시킬 수 있습니다.
네. 핵심부터 말하면 “기본 Kubernetes만으로 구현”하기보다는, Kubernetes Gateway API + Gateway API Inference Extension + 이를 지원하는 Gateway 구현체를 사용하는 구조를 권합니다.
2026년 현재 Kubernetes Gateway API Inference Extension은 inference-specific routing을 위한 표준 API를 제공하며, 핵심 리소스가 InferencePool이고 실제 endpoint 선택 로직은 EPP(Endpoint Picker) 가 담당합니다.
전체 request path는 이렇게 생각하면 됩니다.
Jupyter / Spark / Application / User
│
│ HTTPS
▼
┌─────────────────────┐
│ Gateway / Gateway API│
│ │
│ TLS termination │
│ Auth │
│ Rate limit │
│ HTTP routing │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ InferencePool │
│ │
│ "이 모델의 serving │
│ endpoint 집합" │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ EPP │
│ Endpoint Picker │
│ │
│ 어느 vLLM pod로 │
│ 보낼 것인가 결정 │
└──────────┬──────────┘
│
┌────────┴────────┐
▼ ▼
vLLM Pod A vLLM Pod B
B300 Node1 B300 Node2
여기서 흔히 말하는 Inference Router는 별도의 네 번째 필수 Kubernetes 객체라고 생각하지 않는 게 좋습니다.
실질적으로는:
Inference-aware routing
=
Gateway + InferencePool + EPP
라고 보는 편이 정확합니다.
Gateway는 외부 세계와 GPU serving platform 사이의 문입니다.
현재 환경에서는 특히 중요합니다.
Compute Cluster
Jupyter
Spark
Application
│
│ HTTPS :443
▼
━━━━━━━━ Security Boundary ━━━━━━━━
GPU Cluster
Gateway
│
▼
inference
Gateway에서 담당할 것은 크게 다음입니다.
예를 들어 외부에는:
https://ai.company/v1/chat/completions
하나만 공개합니다.
사용자는:
{
"model": "deepseek-v4.1-flash",
...
}
처럼 OpenAI-compatible API를 호출하게 합니다.
사용자가:
http://vllm-deepseek-pod-7f9xxx:8000
같은 내부 주소를 알아서는 안 됩니다.
이 부분이 꽤 중요합니다.
Gateway API는 Kubernetes에 다음 같은 리소스를 추가하는 API/CRD입니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
하지만 CRD를 설치했다고 packet을 실제로 처리해주는 것은 아닙니다.
실제 구현체가 필요합니다.
구조가:
Kubernetes
GatewayClass
Gateway
HTTPRoute
InferencePool
│
│ desired configuration
▼
Gateway implementation
│
▼
실제 network traffic
입니다.
이미 GPU K8s에서 Cilium을 사용할 예정이므로 자연스러운 선택입니다.
Cilium은 Kubernetes Gateway API를 지원하고 Envoy를 이용해 L7 Gateway 기능을 제공합니다.
즉 별도의 NGINX ingress를 또 집어넣지 않고:
Cilium
│
├─ CNI
├─ NetworkPolicy
├─ Service
├─ Gateway API
└─ Envoy
형태를 고려할 수 있습니다.
다만 여기서 주의할 게 있습니다.
Cilium Gateway API 지원과 Gateway API Inference Extension 지원은 같은 의미가 아닙니다.
일반:
Gateway
HTTPRoute
GRPCRoute
지원과
InferencePool
EPP
integration은 별도로 확인해야 합니다.
따라서 production 구현을 결정할 때는 사용하는 정확한 Cilium/Gateway 버전에서 Inference Extension 호환성을 검증해야 합니다.
InferencePool은 일반 Kubernetes Service와 약간 다르게 생각하면 됩니다.
예를 들어:
DeepSeek service
가 있고 실제 endpoint가:
DeepSeek
Pod A
Node1 / GPU0-3
TP4
Pod B
Node2 / GPU0-3
TP4
라고 합시다.
일반 Service라면 대략:
request
│
▼
Service
│
├─ Pod A
└─ Pod B
이고 endpoint 선택이 inference workload를 깊게 이해하지 않습니다.
InferencePool은:
InferencePool: deepseek-v4.1
│
├─ Pod A
│
└─ Pod B
라는 동일 inference workload를 처리할 수 있는 endpoint pool을 정의합니다.
공식 spec에서도 InferencePool은 inference workload endpoint의 논리적 그룹이며 selector로 serving pod를 식별합니다.
LLM은 일반 web server와 특성이 다르기 때문입니다.
예를 들어:
Pod A
running requests = 25
KV cache = 91%
queue = 18
Pod B
running requests = 7
KV cache = 42%
queue = 1
라고 합시다.
일반 round-robin이면:
A → B → A → B
할 수 있습니다.
하지만 inference router는:
A
queue 18
KV 91%
B
queue 1
KV 42%
↓
Pod B
를 선택할 수 있습니다.
EPP = Endpoint Picker입니다.
InferencePool에 Pod가 4개 있다고 하면:
InferencePool
│
┌───┼───┬───┐
A B C D
EPP가 request마다:
A/B/C/D 중 어느 endpoint가 이 요청에 가장 적절한가?
를 결정합니다.
Gateway API Inference Extension architecture에서도 EPP가 inference-specific metrics를 이용해 endpoint를 선택하고 Gateway가 그 선택에 따라 request를 전달하는 구조입니다.
여기가 LLM serving에서 중요합니다.
예를 들어:
Request
model = DeepSeek
prompt tokens = 24K
tenant = Discovery
priority = high
가 들어왔다고 합시다.
EPP 입장에서는 endpoint마다:
Pod A
running 18
waiting 12
KV usage 82%
prefix match HIGH
Pod B
running 8
waiting 2
KV usage 43%
prefix match LOW
같은 정보가 있을 수 있습니다.
단순 load 관점에서는 B인데, A가 동일한 system prompt/prefix KV를 이미 가지고 있다면 A가 더 유리할 수도 있습니다.
그래서 판단은 개념적으로:
Score(endpoint) =
load score
+ queue score
+ cache locality score
+ model compatibility
+ request priority
같아집니다.
이것이 일반 HTTP load balancer와 inference-aware router의 가장 큰 차이입니다.
맞습니다.
현재:
DeepSeek
│
▼
TP4 Pod 1개
라면 EPP가 선택할 endpoint가 하나뿐입니다.
그래서 당장은:
Gateway
↓
InferencePool
↓
EPP
↓
vLLM #1
이 다소 과해 보일 수 있습니다.
그런데 6개월 뒤:
DeepSeek Pool
┌──────────┴──────────┐
│ │
Node #1 Node #2
TP4 replica A TP4 replica B
가 되면 EPP가 의미를 갖기 시작합니다.
따라서 지금부터 API 구조를 만들어 두되 routing algorithm을 복잡하게 만들 필요는 없습니다.
Cilium Gateway API
│
▼
InferencePool
│
▼
Default EPP
│
▼
vLLM
EPP custom logic을 직접 만들지 않습니다.
이 상태에서 먼저:
Gateway
auth
quota
rate limit
metrics
model routing
을 안정화합니다.
6개월 후:
Gateway
│
▼
InferencePool
│
▼
EPP
│
┌──────────┴──────────┐
│ │
Node #1 Node #2
│ │
DeepSeek A DeepSeek B
에서 EPP가:
queue depth
KV cache state
active requests
request cost
등을 보고 선택하게 합니다.
이 단계에서 prefix-aware routing이 가치가 있는지 Stage7 workload로 검증하면 됩니다.
이게 설계상 중요합니다.
사용자가:
{
"model": "deepseek-v4.1-flash"
}
라고 요청했을 때 먼저 하는 것은 Model Routing입니다.
request
│
│ model=deepseek
▼
DeepSeek InferencePool
그 다음이 Endpoint Routing입니다.
DeepSeek InferencePool
│
▼
EPP
┌───┴───┐
▼ ▼
Pod A Pod B
즉:
MODEL ROUTING
deepseek
↓
DeepSeek Pool
ENDPOINT ROUTING
DeepSeek Pool
↓
EPP
↓
Pod B
두 단계를 분리해서 생각해야 합니다.
예를 들어:
Discovery Team
40 RPS
C20
Wizard Team
10 RPS
C8
이런 것은 EPP 책임이 아닙니다.
Gateway/admission 계층에서 처리합니다.
Client
│
▼
┌───────────────────────┐
│ Gateway │
│ │
│ Auth │
│ Tenant │
│ Quota │
│ Rate limit │
│ Concurrency limit │
└───────────┬───────────┘
│
▼
Model Routing
│
▼
InferencePool
│
▼
EPP
│
▼
vLLM
이렇게 책임을 나누는 것이 좋습니다.
Gateway에서 모든 요청을 무한정 받아 vLLM queue에 밀어 넣으면 안 됩니다.
예를 들어 Stage7에서 DeepSeek TP4가:
SLO capacity
C = 40
production headroom 적용
recommended admission = 30
으로 결정됐다고 합시다.
그러면:
running + admitted
0 ─────────── 30 ───── 40 ─────────→
│ │
admission absolute
limit limit
식으로 운영합니다.
31번째부터는 정책에 따라:
short queue
429
retry-after
등을 사용합니다.
GPU가 처리하지 못할 request를 Gateway에서 미리 제어하는 것이 tail latency 폭증을 막는 데 중요합니다.
현재 만든 구조가 여기서 상당히 유용합니다.
Stage7
TP4
max SLO concurrency = 48
production peak = 30
headroom = 30%
required = 39
48 >= 39
↓
TP4 selected
production profile에:
capacity:
maxSloConcurrency: 48
productionConcurrency: 30
headroomPercent: 30
같은 정보를 넣고,
Gateway policy에서는:
model=deepseek
global concurrency ≤ 30
를 설정합니다.
그러면 benchmark 결과가 실제 admission control로 연결됩니다.
전부 custom 개발할 필요는 없습니다.
제가 현재 구조에서 검토할 stack은 대략:
Kubernetes
│
├── Cilium
│ ├─ CNI
│ ├─ NetworkPolicy
│ ├─ Hubble
│ └─ Gateway API / Envoy
│
├── Kubernetes Gateway API
│
├── Gateway API Inference Extension
│ ├─ InferencePool CRD
│ └─ EPP
│
├── NVIDIA GPU Operator
│ └─ DCGM Exporter
│
├── vLLM
│
├── Prometheus
│
├── Grafana
│
└── OpenTelemetry
입니다.
즉 Ingress NGINX + custom Python router + custom LB부터 직접 만드는 방향은 권하지 않습니다.
Gateway API Inference Extension은 GPU quota/billing platform이 아닙니다.
따라서:
API Key
OIDC
Team identity
rate limit
token quota
billing
는 별도의 API-management/auth 계층이 필요할 수 있습니다.
구조적으로:
External
│
▼
┌───────────────────┐
│ API Management │
│ │
│ Auth / API Key │
│ Team / Quota │
│ Rate limit │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Inference Gateway │
│ Gateway API │
└─────────┬─────────┘
│
▼
InferencePool
│
EPP
│
▼
vLLM
형태도 충분히 가능합니다.
초기에는 두 계층을 Gateway 구현체에서 최대한 같이 처리하고, 요구가 복잡해지면 API management를 분리하는 방식이 좋습니다.
한 번에 모든 것을 넣지 않는 것이 좋습니다.
1단계 — 지금 8×B300
Cilium
+
Gateway API
+
Inference Extension
+
Default EPP
+
vLLM
Prometheus/DCGM/Hubble
여기까지 먼저 구축합니다.
Request path:
Compute
│
HTTPS
▼
GPU Gateway
│
InferencePool
│
EPP
│
vLLM
그리고 Stage7 결과를 이용해:
TP
max concurrency
context
KV
GPU allocation
을 정합니다.
2단계 — Multi-tenant control
그 위에:
OIDC/API key
team
model ACL
RPS quota
token quota
concurrency admission
priority
를 붙입니다.
3단계 — B300 Node #2
그때:
InferencePool
├─ replica Node1
└─ replica Node2
↓
load-aware EPP
를 본격 사용합니다.
4단계 — 실제 데이터가 쌓인 뒤
필요성이 확인되면:
prefix-aware routing
KV locality routing
request-cost routing
dynamic autoscaling
까지 발전시키는 게 좋습니다.
| 구성요소 | 책임 |
|---|---|
| Cilium | Pod networking, NetworkPolicy, Hubble, Gateway dataplane |
| Gateway API | Gateway/HTTP routing을 Kubernetes 리소스로 선언 |
| Gateway implementation | 실제 L4/L7 traffic 처리 |
| InferencePool | 같은 모델을 serving할 endpoint 집합 정의 |
| EPP | 그 pool에서 이번 요청을 처리할 최적 endpoint 선택 |
| API/Auth layer | 사용자/팀 인증, quota, rate limit, model ACL |
| vLLM | 실제 LLM inference |
| Stage7/v12 | TP/Concurrency/KV/SLO capacity를 측정하고 production config 결정 |
| Prometheus/DCGM | GPU와 inference 성능/용량 측정 |
| Hubble | network flow/latency/drop 관측 |
따라서 현재 환경에서 “별도 Inference Router를 하나 개발해야 하나?”에 대한 답은 일단 No입니다. Gateway API + Inference Extension의 InferencePool/EPP를 먼저 사용하고, 기본 EPP로 해결되지 않는 실제 routing 문제가 Stage7/production metric에서 확인됐을 때만 custom routing policy를 추가하는 게 가장 관리하기 좋은 방향입니다.
Gateway API Inference Extension 공식 문서
Cilium Gateway API 공식 문서
필요할 가능성이 높습니다. 다만 LiteLLM과 Gateway API Inference Extension은 경쟁 관계가 아니라 담당 계층이 다릅니다. 지금처럼 여러 팀이 여러 모델을 공유하는 환경이라면 저는 LiteLLM 계열의 LLM 관리/API Gateway 계층을 추가하는 쪽을 권합니다.
제가 추천하는 구조는 이렇습니다.
Compute Cluster / Users / Applications
Jupyter / Spark / Discovery / Wizard
│
│ HTTPS :443
▼
┌─────────────────────────────────────┐
│ LiteLLM / LLM Management Gateway │ ← 사용자/팀 관리
│ │
│ Auth / Virtual Key │
│ Team / Model ACL │
│ RPM / TPM / Concurrent Request │
│ Usage / Budget / Accounting │
│ Model Alias │
│ Retry / Fallback Policy │
└──────────────────┬──────────────────┘
│
│ OpenAI API
▼
┌─────────────────────────────────────┐
│ K8s Inference Gateway │ ← GPU endpoint 관리
│ Gateway API │
└──────────────────┬──────────────────┘
│
▼
InferencePool
│
▼
EPP ← 어느 replica?
│
┌──────────┴──────────┐
▼ ▼
vLLM Replica A vLLM Replica B
Node #1 Node #2
LiteLLM은 virtual key와 팀별 model access, RPM/TPM/max-parallel-request 제한, budget/spend tracking 같은 기능을 제공합니다. 특히 virtual key와 budget 같은 관리 기능은 PostgreSQL DB가 필요합니다. LiteLLM
둘 다 "routing"이라는 표현을 쓰기 때문에 헷갈리기 쉽습니다.
LiteLLM은 논리적인 모델/API routing을 담당하게 하는 게 좋습니다.
사용자 요청
model = discovery
↓
LiteLLM
"이 팀은 discovery 사용 가능?"
"quota가 남았나?"
"어떤 logical model인가?"
↓
deepseek-v4.1-flash
반면 EPP는 물리적인 inference replica 선택을 담당합니다.
deepseek-v4.1-flash
↓
InferencePool
↓
EPP
Node1 replica
queue=12
KV=85%
Node2 replica
queue=2
KV=45%
↓
Node2 선택
InferencePool은 동일 모델 서버의 Pod들을 논리적인 pool로 묶고, EPP가 KV-cache utilization, pending queue 같은 inference 상태를 추적하여 적절한 replica를 선택하도록 설계되어 있습니다. Kubernetes 게이트웨이 API 확장
그래서 LiteLLM에서 GPU replica의 KV-cache 상태까지 보고 직접 LB하게 만들지는 않는 것을 권합니다.
맞습니다.
논리적으로는 두 gateway 계층입니다.
North-South / Management
Client
↓
LiteLLM
↓
────────────────────────────
GPU Serving
────────────────────────────
↓
Inference Gateway
↓
InferencePool / EPP
↓
vLLM
하지만 역할이 확실히 다릅니다.
| 계층 | 주 질문 |
|---|---|
| LiteLLM | 누가 어떤 모델을 얼마나 사용할 수 있나? |
| Gateway API | 어느 InferencePool로 traffic을 보낼까? |
| InferencePool | 이 모델을 제공하는 endpoint는 무엇인가? |
| EPP | 그중 지금 가장 적합한 replica는 무엇인가? |
| vLLM | 실제로 inference를 어떻게 실행할까? |
| Stage7 | 몇 GPU/TP/C가 필요한가? |
이렇게 책임을 나누면 깔끔합니다.
사용자가 여러 팀이라고 했기 때문입니다.
예를 들어:
LiteLLM
┌──────────┼──────────┐
│ │ │
Discovery Wizard Ontology
│ │ │
key/team key/team key/team
│ │ │
100k TPM 50k TPM 500k TPM
C=20 C=8 C=4
│ │ │
DeepSeek/GLM Qwen/OCR GLM-5.3
처럼 중앙에서 관리할 수 있습니다.
LiteLLM은 현재 team/key/user 수준의 TPM/RPM, max parallel requests 및 model-specific limits를 지원합니다. LiteLLM
그리고:
Team Discovery
Allowed:
✓ deepseek-v4.1-flash
✓ glm-5.3-flash
Denied:
✗ glm-5.3-nightly
같은 model access control도 virtual key 계층에서 관리할 수 있습니다. LiteLLM
Application에 실제 checkpoint 이름을 박지 않는 게 좋습니다.
나쁜 구조:
Jupyter application
model =
deepseek-ai/Deepseek-V4.1-Flash
좋은 구조:
model = discovery
그리고 LiteLLM에서:
discovery
↓
deepseek-v4.1-flash
로 mapping합니다.
나중에 Stage7 결과에서 GLM Flash가 더 적합하면:
discovery
↓
glm-5.3-flash
로 변경할 수 있습니다.
Application 코드는 그대로입니다.
LiteLLM 자체도 여러 모델/provider를 하나의 OpenAI-compatible API 뒤에 두는 AI Gateway를 지향하고 있습니다. LiteLLM
예를 들어:
Discovery
Primary
DeepSeek-V4.1-Flash
│
│ unavailable
▼
Fallback
GLM-5.3-Flash
같은 정책입니다.
LiteLLM은 key/team 수준에서도 routing strategy, fallback, timeout, retry 같은 router 설정을 지원합니다. LiteLLM
다만 모델 간 fallback은 quality가 동등하다는 Stage7 검증 후에만 활성화하는 것을 권합니다.
이건 지금 만든 v12와 굉장히 잘 맞습니다.
예를 들어 Stage7 결과가:
DeepSeek TP4
SLO max concurrency = 48
Production peak = 30
Required headroom = 30%
production admission
≈ 30 concurrent requests
라고 나오면 LiteLLM에:
DeepSeek total
max parallel ≈ 30
이라는 상한을 만들 수 있습니다.
그리고 그 30을 팀별로:
DeepSeek
capacity 30
┌─────────┼─────────┐
│ │ │
Discovery Jupyter Wizard
C=20 C=4 C=6
처럼 나눌 수 있습니다.
즉 우리가 지금까지 만든:
Stage7
↓
TP candidate sweep
↓
saturation
↓
SLO
↓
headroom
↓
production capacity
결과가 LiteLLM의 tenant quota/admission 설정으로 이어질 수 있습니다.
이건 다음 v13에서 자동 생성 대상으로 넣어도 꽤 좋습니다.
이 구조는 피하는 게 좋습니다.
LiteLLM
├─ vLLM Pod A
├─ vLLM Pod B
├─ vLLM Pod C
└─ vLLM Pod D
그리고 LiteLLM이 직접 replica load balancing까지 전부 담당하는 형태입니다.
LiteLLM 자체에도 load balancing/routing 기능은 있지만, Kubernetes GPU serving을 장기적으로 운영할 거라면:
LiteLLM
│
logical model
▼
InferencePool
│
EPP
│
physical endpoint
▼
vLLM
으로 책임을 분리하는 쪽을 권합니다.
EPP는 애초에 inference endpoint의 queue/KV 상태 등을 이용한 endpoint 선택을 위해 설계된 계층입니다. Kubernetes 게이트웨이 API 확장
처음부터 모든 계층을 production complexity로 만들 필요는 없습니다.
Compute
│
▼
LiteLLM
│
▼
Cilium Gateway
│
▼
InferencePool
│
▼
EPP
│
▼
vLLM
EPP 뒤에 replica가 하나라 사실상 pass-through입니다.
하지만 이렇게 만들어 놓으면 6개월 후:
LiteLLM
│
▼
Inference Gateway
│
▼
InferencePool
│
EPP
┌────┴────┐
▼ ▼
Node1 Node2
TP4 A TP4 B
로 자연스럽게 확장됩니다.
LiteLLM을 단순 OpenAI proxy로만 쓰면 DB 없이도 가능하지만, 우리가 원하는 것은 그 수준이 아닙니다.
우리가 원하는 기능이:
Virtual Key
Team
Quota
Budget
Usage
Model ACL
이므로 PostgreSQL을 같이 두는 구조로 보는 것이 맞습니다. LiteLLM 공식 문서도 virtual keys와 budget 기능에 DB가 필요하다고 명시하고 있습니다. LiteLLM
그래서:
GPU Cluster
┌──────────────────────┐
│ LiteLLM replicas │
└──────────┬───────────┘
│
├──────── PostgreSQL
│
└──────── Redis (필요 시)
정도로 생각하면 됩니다.
PostgreSQL을 GPU node에 꼭 같이 둘 필요는 없습니다. 기존 DB infrastructure가 있다면 그쪽을 사용하는 편이 운영상 더 좋을 수 있습니다.
USERS / APPLICATIONS
│
HTTPS :443
│
▼
┌────────────────────────────────────────────┐
│ LiteLLM Gateway │
│ │
│ Authentication / Virtual Key │
│ Team / Model ACL │
│ RPM / TPM / Concurrency │
│ Usage / Accounting │
│ Model Alias │
│ Model Fallback │
└─────────────────────┬──────────────────────┘
│
logical model
│
▼
┌────────────────────────────────────────────┐
│ Kubernetes Inference Gateway │
│ Gateway API │
└─────────────────────┬──────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ InferencePool │
│ DeepSeek / GLM / Qwen / ... │
└─────────────────────┬──────────────────────┘
│
EPP
│
load / queue / KV locality
│
┌────────┴────────┐
▼ ▼
vLLM Replica A vLLM Replica B
B300 Node #1 B300 Node #2
│ │
└────────┬────────┘
│
GPU / Local NVMe
그래서 제 추천은 LiteLLM을 넣습니다. 다만 LiteLLM = inference router로 설계하는 게 아니라 “AI API Management / Multi-tenant Management Plane”으로 두고, GPU replica의 실제 inference-aware endpoint 선택은 InferencePool + EPP에 맡깁니다.
이렇게 하면 우리가 만든 v12의 Stage7 capacity 결과 → LiteLLM team/model quota + production profile → Helm → InferencePool까지 하나의 운영 체계로 연결할 수 있습니다. LiteLLM
LiteLLM AI Gateway 공식 문서
Gateway API Inference Extension 공식 문서