4.5 LSTM을 이용한 네이버 영화 리뷰 분류 이번에는 LSTM(Long Short-Term Memory) 을 이용해 네이버 영화 리뷰가 긍정인지 부정인지 분류해보았다. 사용할 데이터 : 네이버 영화 리뷰 데이터(NSMC) 전체 데이터 : 200,000개 Train : 150,000개 Test : 50,000개 Label : 긍정 1 , 부정 0 전체 과정은 다음과 같다. 영화 리뷰 ↓ 데이터 정제 ↓ 형태소 분석 ↓ 정수 인코딩 ↓ Padding ↓ Embedding ↓ LSTM ↓ 긍정 / 부정 분류 1. 데이터 이해와 전처리 1-1. 라이브러리 설정 import pandas as pd import numpy as np import matplotlib.pyplot as plt import urllib.request from konlpy.tag import Mecab from tqdm import tqdm from sklearn.model_selection import train_test_split from collections import Counter 이번 실습에서는 특히 Mecab : 한국어 형태소 분석 Counter : 단어 등장 빈도 계산 train_test_split : Train / Validation 데이터 분리 를 사용한다. 1-2. 데이터 로드 urllib.request.urlretrieve( "https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt", filename="ratings_train.txt" ) urllib.request.urlretrieve( "https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt", filename="ratings_test.txt" ) train_data = pd.read_table('ratings_train.txt') test_data = pd.read_table('ratings_test.txt') 데이터 개수를 확인하면 Train : 150,000 Test : 50,000 이고 데이터는 id / document / label 세 개의 컬럼으로 구성되어 있다. 여기서 document → 모델의 입력 label → 모델이 맞혀야 할 정답 이라고 이해하면 된다. 2. 데이터 정제 먼저 중복된 리뷰를 확인한다. train_data['document'].nunique() 결과 146182 150,000개의 데이터 중 중복된 리뷰가 존재하기 때문에 제거한다. train_data.drop_duplicates( subset=['document'], inplace=True ) Null 데이터도 제거한다. train_data = train_data.dropna(how='any') 그리고 한글과 공백을 제외한 문자를 제거한다. train_data['document'] = train_data['document'].str.replace( "[^ᄀ-하-ᅵ가-힣 ]", "", regex=True ) 특수문자를 제거하면서 아무 내용도 남지 않은 리뷰가 새롭게 생길 수 있으므로 빈 문자열도 제거한다. train_data['document'].replace('', np.nan, inplace=True) train_data = train_data.dropna(how='any') 전처리 후 Train 데이터 145,393개 처음에는 150,000개였지만 중복, Null, 의미 없는 데이터를 제거하면서 데이터가 줄어들었다. 3. 형태소 분석과 토큰화 한국어는 영어처럼 단순히 띄어쓰기만으로 단어를 나누기 어렵다. 예를 들어 나는 영화를 봤다 에서 나는 , 영화를 , 봤다 에는 조사와 어미가 붙어 있다. 그래서 Mecab 형태소 분석기 를 이용한다. mecab = Mecab() mecab.morphs( '와 이런 것도 영화라고 차라리 뮤직비디오를 만드는 게 나을 뻔' ) 결과 ['와', '이런', '것', '도', '영화', '라고', '차라리', '뮤직', '비디오', '를', '만드', '는', '게', '나을', '뻔'] 문장이 형태소 단위로 분리되는 것을 확인할 수 있다. 불용어 제거 분류에 큰 의미가 없다고 판단되는 조사 등의 단어는 제거한다. stopwords = [ '도', '는', '다', '의', '가', '이', '은', '한', '에', '하', '고', '을', '를', '인', '듯', '과', '와', '네', '들', '지', '임', '게' ] 전체 리뷰에 형태소 분석과 불용어 제거를 적용한다. X_train = [] for sentence in tqdm(train_data['document']): tokenized_sentence = mecab.morphs(sentence) stopwords_removed_sentence = [ word for word in tokenized_sentence if word not in stopwords ] X_train.append(stopwords_removed_sentence) 결과적으로 문장 ↓ 형태소들의 리스트 형태로 변환된다. 4. 단어 집합 만들기 컴퓨터는 영화 , 재밌다 같은 문자열을 그대로 계산할 수 없다. 따라서 먼저 학습 데이터에 어떤 단어들이 존재하는지 확인한다. word_list = [] for sent in X_train: for word in sent: word_list.append(word) word_counts = Counter(word_list) print(len(word_counts)) 결과 45,296 약 45,000개의 서로 다른 형태소가 존재했다. 하지만 이 중에는 한두 번밖에 등장하지 않는 단어도 많았다. 2회 이하 등장한 희귀 단어를 확인한 결과 희귀 단어 비율 : 약 57.63% 전체 등장 빈도에서 차지하는 비율 : 약 2.28% 였다. 즉 단어 종류에서는 절반 이상이지만 실제 문장에서는 거의 등장하지 않는다. 그래서 희귀 단어를 제외하고 단어 집합을 만들었다. Vocabulary : 19,191개 여기에 <PAD> = 0 <UNK> = 1 을 추가한다. 최종 Vocabulary 19,193개 <UNK> 는 학습할 때 보지 못한 단어, <PAD> 는 문장 길이를 맞추는 데 사용한다. 5. 정수 인코딩 이제 단어를 숫자로 바꾼다. 예를 들어 영화 → 2 좋 → 6 너무 → 11 와 같은 방식이다. def texts_to_sequences(tokenized_data, word_to_index): encoded_data = [] for sent in tokenized_data: encoded_sent = [] for word in sent: encoded_sent.append( word_to_index.get( word, word_to_index['<UNK>'] ) ) encoded_data.append(encoded_sent) return encoded_data 이를 통해 ['영화', '정말', '재밌'] 같은 문장은 [2, 53, 142] 와 같은 숫자의 나열로 바뀐다. 중요한 것은 숫자의 크기 자체에 의미가 있는 것이 아니라 단어를 구별하기 위한 번호 라는 것이다. 6. Padding 문제는 리뷰마다 길이가 다르다는 것이다. 리뷰 A → 7개 단어 리뷰 B → 15개 단어 리뷰 C → 30개 단어 신경망에서 Batch 단위로 처리하기 위해서는 길이를 동일하게 맞출 필요가 있다. Train 데이터의 길이를 확인해보면 최대 길이 : 74 평균 길이 : 약 12.3 이었다. 길이를 30 으로 설정하면 약 92.5%의 리뷰를 포함 할 수 있기 때문에 max_len = 30 으로 설정하였다. Padding 이후 데이터 크기는 Train : (116314, 30) Validation : (29079, 30) Test : (48852, 30) 이 된다. 예를 들어 [924, 1866, 128, 7, 80, 48, 34] 라는 리뷰는 [924, 1866, 128, 7, 80, 48, 34, 0, 0, 0, 0, ...] 처럼 길이 30까지 PAD = 0 으로 채워진다. 7. LSTM 모델 이제 실제 감성 분류 모델을 만든다. 모델 구조는 생각보다 간단하다. Integer Encoding ↓ Embedding ↓ LSTM ↓ Linear ↓ 긍정 / 부정 코드는 다음과 같다. class TextClassifier(nn.Module): def __init__( self, vocab_size, embedding_dim, hidden_dim, output_dim ): super().__init__() self.embedding = nn.Embedding( vocab_size, embedding_dim ) self.lstm = nn.LSTM( embedding_dim, hidden_dim, batch_first=True ) self.fc = nn.Linear( hidden_dim, output_dim ) def forward(self, x): embedded = self.embedding(x) lstm_out, (hidden, cell) = self.lstm( embedded ) last_hidden = hidden.squeeze(0) return self.fc(last_hidden) 사용한 주요 설정은 Vocabulary Size : 19,193 Embedding Dimension : 100 Hidden Dimension : 128 Output Dimension : 2 Batch Size : 32 Epoch : 5 Learning Rate : 0.001 이다. 모델 내부 데이터 변화 이 부분이 개인적으로 가장 중요했다. Batch Size가 32라면 Input (32, 30) ↓ Embedding (32, 30, 100) ↓ LSTM (32, 128) ↓ Linear (32, 2) 가 된다. 즉 Embedding Layer에서는 각각의 단어를 100차원의 벡터 로 표현한다. LSTM은 문장의 순서를 따라 정보를 읽고 마지막에 128차원의 Hidden State 로 문장의 정보를 요약한다. 마지막 Linear Layer는 부정 점수 / 긍정 점수 두 값을 출력한다. 8. 모델 학습 Loss Function은 criterion = nn.CrossEntropyLoss() Optimizer는 optimizer = torch.optim.Adam( model.parameters(), lr=0.001 ) 을 사용한다. 하나의 Batch가 학습되는 흐름은 Forward ↓ Loss 계산 ↓ Gradient 초기화 ↓ Backward ↓ Parameter Update 이다. 코드로 보면 logits = model(batch_X) loss = criterion( logits, batch_y ) optimizer.zero_grad() loss.backward() optimizer.step() 이 과정이 반복되면서 LSTM의 Parameter가 업데이트된다. 9. Validation과 Test 결과 학습 과정에서는 Train 데이터만 보는 것이 아니라 Validation 데이터의 Loss를 확인해서 가장 좋은 모델을 저장한다. if val_loss < best_val_loss: best_val_loss = val_loss torch.save( model.state_dict(), 'best_model_checkpoint.pth' ) 최종적으로 가장 좋은 모델을 불러와 평가한 결과 데이터 Loss Accuracy Validation 0.3392 84.90% Test 0.3435 84.92% Test Accuracy는 약 84.92% 가 나왔다. Validation과 Test Accuracy가 거의 비슷하게 나왔다는 것도 확인할 수 있었다. 10. 실제 문장 예측 학습한 모델에 직접 문장을 입력해보았다. test_input = "이 영화 개꿀잼 ᄏᄏᄏ" 결과 긍정 반대로 test_input = "이딴게 영화냐 ᄍᄍ" 결과 부정 실제 인터넷에서 사용할 법한 표현도 어느 정도 긍정과 부정을 구분하는 것을 확인할 수 있었다. 11. 공부하면서 이해한 부분 LSTM 모델 자체만 보는 것이 아니라 자연어가 모델에 입력되기까지의 과정 문장 ↓ 형태소 분석 ↓ 불용어 제거 ↓ Vocabulary 생성 ↓ 정수 인코딩 ↓ Padding ↓ Embedding ↓ LSTM ↓ Linear ↓ 긍정 / 부정 한국어는 조사와 어미 때문에 형태소 분석이 중요하다. 단어는 그대로 신경망에 들어가는 것이 아니라 정수 → Embedding Vector 로 변환된다. 서로 다른 문장 길이를 맞추기 위해 Padding 을 사용한다. LSTM은 단어를 순서대로 읽으면서 문장의 정보를 Hidden State에 저장한다. 이번 모델에서는 마지막 Hidden State를 이용해 긍정/부정을 분류하였다.
A — B300 Multi-Model Serving Architecture 1. 목적 본 문서는 NVIDIA B300 8-GPU 단일 노드 환경에서 여러 LLM 모델을 동시에 서비스하기 위한 Multi-Model Inference Serving Architecture 를 정의한다. 대상 환경은 다음과 같다. NVIDIA B300 8-GPU 단일 서버 Kubernetes 기반 운영 NVIDIA GPU Operator / Device Plugin vLLM 기반 LLM inference 여러 모델의 동시 serving 여러 사용자 및 팀(tenant)의 공동 사용 Keycloak / Active Directory 기반 인증 LiteLLM 기반 LLM Gateway 및 quota 관리 Redis 기반 distributed rate limiting / state Prometheus / Grafana / DCGM 기반 observability Cilium Gateway API / Envoy 기반 Kubernetes network ingress 향후 여러 B300 노드 및 ClusterMesh 환경으로 확장 가능 핵심 설계 원칙은 다음과 같다. Kubernetes가 GPU 자원을 할당하고, vLLM이 GPU에서 inference를 수행하며, LiteLLM이 사용자/팀/모델 단위의 API 정책과 quota를 관리한다. 2. Architecture Overview 전체 논리 구조는 다음과 같다. flowchart TB U[Users / Applications] IDP[Keycloak / Active Directory] GW[Cilium Gateway API / Envoy] LL[LiteLLM Proxy] R[(Redis)] DB[(PostgreSQL)] subgraph B300["B300 8-GPU Kubernetes Node"] V70["vLLM 70B<br/>TP=4"] V32["vLLM 32B<br/>TP=2"] V8["vLLM 8B<br/>TP=1"] VE["Embedding / Reranker<br/>TP=1"] G0["GPU 0-3"] G1["GPU 4-5"] G2["GPU 6"] G3["GPU 7"] V70 --> G0 V32 --> G1 V8 --> G2 VE --> G3 end PROM[Prometheus] DCGM[DCGM Exporter] GRAF[Grafana] U -->|OIDC / JWT| IDP U -->|API Request| GW IDP -->|Identity / Claims| GW GW --> LL LL --> R LL --> DB LL -->|model=70b| V70 LL -->|model=32b| V32 LL -->|model=8b| V8 LL -->|embedding/reranker| VE DCGM --> PROM V70 --> PROM V32 --> PROM V8 --> PROM VE --> PROM PROM --> GRAF 이 구조에서 각 계층의 책임을 명확하게 분리한다. 계층 주요 컴포넌트 책임 Identity Keycloak / AD 사용자 및 서비스 인증 Network Gateway Cilium Gateway API / Envoy TLS, JWT 검증, network/L7 policy AI Gateway LiteLLM Model routing, tenant quota, RPM/TPM, concurrency State Redis Distributed rate-limit 및 shared state Persistence PostgreSQL 사용자/팀/API key/budget 등의 영속 데이터 Inference vLLM Model execution, batching, KV Cache, TP GPU Resource NVIDIA GPU Operator GPU allocation 및 device management Hardware Monitoring DCGM Exporter GPU utilization, HBM, power, temperature, NVLink Platform Monitoring Prometheus/Grafana 통합 모니터링 및 dashboard 3. B300 GPU Resource Partitioning 3.1 기본 원칙 8-GPU B300 노드는 여러 inference instance가 동시에 동작할 수 있는 하나의 GPU resource pool이다. 예시적인 초기 partition은 다음과 같다. B300 Node │ ├── GPU 0 ─┐ ├── GPU 1 │ ├── GPU 2 ├── vLLM 70B / TP=4 ├── GPU 3 ─┘ │ ├── GPU 4 ─┐ ├── GPU 5 ─┘── vLLM 32B / TP=2 │ ├── GPU 6 ──── vLLM 8B / TP=1 │ └── GPU 7 ──── Embedding / Reranker / Spare 이는 초기 serving profile의 예시 이며 고정된 최적 배치로 간주하지 않는다. 실제 배치는 다음 항목의 benchmark 결과를 기준으로 결정한다. GPU memory usage KV Cache capacity TTFT ITL output token throughput request concurrency GPU utilization NVLink traffic CPU/NUMA locality model size context length workload mix 3.2 Tensor Parallelism 후보 TP 구성은 다음과 같다. Model class 후보 TP GPU 수 용도 Lightweight model TP=1 1 7B~14B, embedding, reranker Mid-size LLM TP=2 2 32B급 Large LLM TP=4 4 70B급 Very large model TP=8 8 대형 MoE / full-node serving TP 크기를 1/2/4/8 과 같이 구성하면 운영 및 benchmark matrix를 단순화할 수 있다. 다만, TP는 반드시 2의 거듭제곱이어야 한다는 절대적인 규칙은 아니다. 실제 B300/NVLink topology와 모델의 communication pattern에 따라 TP=2/4/8 각각을 benchmark하여 결정한다. 4. Kubernetes GPU Allocation GPU 자원 할당의 책임은 LiteLLM이나 vLLM이 아니라 Kubernetes + NVIDIA Device Plugin 에 둔다. 예: resources: requests: nvidia.com/gpu: "4" limits: nvidia.com/gpu: "4" 70B TP4 instance: Kubernetes │ │ nvidia.com/gpu = 4 ▼ vLLM Pod │ └── tensor-parallel-size = 4 32B: resources: requests: nvidia.com/gpu: "2" limits: nvidia.com/gpu: "2" 8B: resources: requests: nvidia.com/gpu: "1" limits: nvidia.com/gpu: "1" 중요한 운영 원칙 GPU 번호를 운영자가 직접 다음과 같이 고정하는 방식은 기본 설계로 사용하지 않는다. CUDA_VISIBLE_DEVICES=0,1,2,3 대신 Kubernetes GPU resource allocation을 기준으로 한다. CUDA_VISIBLE_DEVICES 는 컨테이너 내부에서 NVIDIA runtime이 할당된 GPU를 노출하기 위한 실행 환경의 결과로 취급한다. 5. vLLM Instance Architecture 각 모델은 독립적인 vLLM deployment/service로 관리한다. flowchart LR subgraph K8s["Kubernetes"] S70["Service<br/>vllm-70b"] S32["Service<br/>vllm-32b"] S8["Service<br/>vllm-8b"] V70["vLLM 70B<br/>TP=4"] V32["vLLM 32B<br/>TP=2"] V8["vLLM 8B<br/>TP=1"] S70 --> V70 S32 --> V32 S8 --> V8 end G70["GPU x4"] G32["GPU x2"] G8["GPU x1"] V70 --> G70 V32 --> G32 V8 --> G8 각 vLLM instance는 다음을 독립적으로 관리한다. Model weights CUDA context Tensor Parallelism KV Cache Continuous batching Request scheduling Maximum context Maximum concurrent sequences Prefix Cache Prometheus metrics 6. vLLM Resource Configuration 예시: apiVersion: apps/v1 kind: Deployment metadata: name: vllm-70b namespace: llm spec: replicas: 1 selector: matchLabels: app: vllm-70b template: metadata: labels: app: vllm-70b model: llama-70b spec: nodeSelector: accelerator: b300 containers: - name: vllm image: vllm/vllm-openai:<validated-version> args: - "meta-llama/Llama-3.1-70B-Instruct" - "--tensor-parallel-size" - "4" - "--gpu-memory-utilization" - "0.90" - "--max-model-len" - "32768" ports: - containerPort: 8000 resources: requests: cpu: "16" memory: "64Gi" nvidia.com/gpu: "4" limits: cpu: "32" memory: "128Gi" nvidia.com/gpu: "4" gpu-memory-utilization 0.85~0.90 은 초기 tuning 범위로 사용할 수 있지만 모든 모델에 적용되는 고정 권장값은 아니다. 최종값은 다음 결과를 기반으로 결정한다. Model + Context Length + Concurrency + KV Cache + Prefix Cache ↓ GPU Memory Requirement ↓ OOM / latency / throughput benchmark 7. CPU / NUMA Affinity GPU inference는 GPU memory만 고려해서는 안 된다. 다음 자원의 locality를 함께 고려한다. GPU │ ├── PCIe topology ├── NVLink topology ├── CPU socket ├── NUMA node ├── NIC └── memory 예시: CPU NUMA 0 ├── CPU cores ├── Memory └── GPU 0-3 CPU NUMA 1 ├── CPU cores ├── Memory └── GPU 4-7 Kubernetes에서는 다음 기능을 함께 검토한다. CPU Manager Topology Manager NVIDIA Device Plugin Node Feature Discovery Guaranteed QoS CPU pinning NUMA alignment 예를 들어 특정 serving workload에서는: resources: requests: cpu: "16" memory: "64Gi" nvidia.com/gpu: "4" limits: cpu: "16" memory: "64Gi" nvidia.com/gpu: "4" 처럼 CPU request/limit을 동일하게 설정하여 Guaranteed QoS를 사용하는 방식을 검토할 수 있다. 8. Model Serving Gateway 모든 vLLM Service를 외부에 직접 노출하지 않는다. 권장 구조: Client │ ▼ Cilium Gateway API / Envoy │ ▼ LiteLLM │ ├── llama-70b ──> vllm-70b ├── qwen-32b ──> vllm-32b ├── llama-8b ──> vllm-8b └── embedding ──> embedding service 클라이언트는 backend의 IP/port를 알 필요가 없다. 예: client = OpenAI( base_url="https://llm.example.internal/v1", api_key="..." ) response = client.chat.completions.create( model="llama-70b", messages=[ {"role": "user", "content": "Hello"} ] ) 9. LiteLLM as AI Gateway LiteLLM은 GPU scheduler가 아니다. 역할을 다음과 같이 정의한다. LiteLLM │ ├── Model routing ├── Deployment routing ├── User / Team identification ├── API key management ├── RPM ├── TPM ├── Budget ├── Max concurrent requests ├── Model access control ├── Fallback └── Usage accounting 반면 vLLM은: vLLM │ ├── Model execution ├── Tensor Parallelism ├── Continuous batching ├── KV Cache ├── Prefix Cache └── GPU scheduling inside inference engine 을 담당한다. 10. LiteLLM Model Deployment 예: model_list: - model_name: llama-70b litellm_params: model: openai/llama-70b api_base: http://vllm-70b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 8 - model_name: qwen-32b litellm_params: model: openai/qwen-32b api_base: http://vllm-32b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 16 - model_name: llama-8b litellm_params: model: openai/llama-8b api_base: http://vllm-8b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 32 여기서 max_parallel_requests 는 GPU 메모리를 직접 제한하는 값이 아니다. 예: LiteLLM max_parallel_requests = 8 │ ▼ vLLM request scheduler │ ▼ GPU KV Cache / batching 따라서 LiteLLM concurrency와 vLLM의 sequence/concurrency 설정은 별도로 benchmark하여 결정한다. 11. Multi-Deployment Routing 향후 동일 모델의 replica가 증가하면 하나의 logical model에 여러 deployment를 연결할 수 있다. model = llama-70b │ LiteLLM Router / \ / \ ▼ ▼ vLLM-70B-A vLLM-70B-B TP4 TP4 GPU 0-3 GPU 4-7 이 구조에서는 LiteLLM이 다음과 같은 routing을 수행할 수 있다. Load balancing Least-loaded deployment 선택 RPM/TPM-aware routing Retry Fallback Deployment health 기반 제외 따라서 향후 B300 여러 대로 확장할 때도 동일한 논리 구조를 유지할 수 있다. 12. Authentication Architecture 기업 환경에서는 사용자 인증의 중심을 Keycloak / AD에 둔다. 권장 흐름: sequenceDiagram participant U as User / Application participant K as Keycloak / AD participant E as Envoy Gateway participant L as LiteLLM participant V as vLLM U->>K: OIDC authentication K-->>U: Access Token (JWT) U->>E: API request + JWT E->>E: JWT signature / issuer / audience validation E->>L: Authenticated request L->>L: User / Team / Model policy L->>L: RPM / TPM / concurrency check L->>V: OpenAI-compatible request V-->>L: Inference response L-->>E: Response E-->>U: Response 13. Keycloak / AD Claims JWT에는 다음과 같은 정보를 사용할 수 있다. { "sub": "user-1234", "preferred_username": "user-a", "groups": [ "ai-research", "data-platform" ], "roles": [ "llm-user" ] } 이를 다음과 같이 policy에 연결한다. JWT │ ├── sub │ ↓ │ User │ ├── groups │ ↓ │ Team │ └── roles ↓ Permission 예: ai-research ├── llama-70b ├── qwen-32b └── llama-8b general-users ├── qwen-32b └── llama-8b 14. Tenant / User / Model Quota 사용자에게 GPU를 직접 할당하기보다는 API capacity를 할당 한다. 권장 policy hierarchy: Organization │ ▼ Team │ ▼ User │ ▼ API Key / Client │ ▼ Model 예: Team 70B TPM 32B TPM 8B TPM Max Concurrent AI Research 30K 50K 100K 10 AI Engineering 10K 30K 50K 5 General - 10K 30K 3 이러한 quota는 GPU의 물리적 할당과 독립적이다. 15. Rate Limit LLM workload에서는 RPM만으로는 충분하지 않다. 최소한 다음을 고려한다. 항목 의미 목적 RPM Requests/minute Request burst 제어 TPM Tokens/minute 실제 inference load 제어 TPD Tokens/day 장기 사용량 제어 Budget 비용/사용량 팀별 resource accounting Concurrency 동시 request Backend queue 보호 Context limit 요청당 최대 context KV Cache 보호 Output limit 최대 생성 토큰 장시간 generation 방지 16. Noisy Neighbor Protection 멀티테넌트 환경에서 가장 중요한 보호 장치 중 하나다. 예: User A │ └── 200K token request │ ▼ KV Cache pressure │ ▼ User B latency ↑ 따라서 다음과 같은 다단계 제한을 적용한다. Client │ ▼ ┌────────────────┐ │ Gateway │ │ JWT / Auth │ └───────┬────────┘ │ ┌───────▼────────┐ │ LiteLLM │ │ RPM / TPM │ │ Team quota │ │ concurrency │ └───────┬────────┘ │ ┌───────▼────────┐ │ vLLM │ │ context limit │ │ sequence limit │ │ KV Cache │ └───────┬────────┘ │ ▼ GPU 특히 --max-model-len 은 단일 요청이 과도한 KV Cache를 사용하는 것을 방지하기 위한 중요한 보호 장치다. 17. Prefix Caching 동일한 system prompt 또는 반복되는 prefix가 많은 workload에서는 vLLM Prefix Ca
네. 다만 “여러 사용자가 GPU를 공유한다 = LiteLLM이 표준”이라고 보기는 어렵습니다. 정확히는 LiteLLM은 GPU를 공유하는 계층이 아니라, 여러 사용자의 LLM 요청을 관리하는 AI Gateway 계층 입니다. 현재 말씀하신 B300 1대 / 8 GPU / 여러 모델 / 여러 사용자 구조라면 다음처럼 나누는 것이 일반적입니다. Users / Applications │ Keycloak / AD │ ▼ Cilium Gateway API │ ▼ ┌─────────────┐ │ LiteLLM │ ← 사용자/팀/모델/API 관리 │ Proxy │ Rate limit / quota / routing └──────┬──────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ vLLM-70B vLLM-32B vLLM-8B GPU 0-3 GPU 4-5 GPU 6 TP4 TP2 TP1 │ │ │ └────────────┴────────────┘ │ GPU 0 ~ 7 역할을 구분하면 계층 담당 핵심 역할 Kubernetes + NVIDIA GPU Operator GPU 자원 GPU 할당/격리, scheduling vLLM 추론 엔진 batching, KV cache, prefix cache, TP LiteLLM AI Gateway 사용자 인증, API key, quota, RPM/TPM, model routing Envoy Gateway/Cilium 네트워크 Gateway TLS, JWT/OIDC, L7 routing, NetworkPolicy Keycloak Identity 사용자/그룹/서비스 인증 Prometheus/DCGM Observability GPU/LLM/사용자별 사용량 모니터링 LiteLLM 자체도 공식적으로 Gateway/Proxy 를 중심으로 authentication, logging, cost tracking, rate limiting을 제공하고, virtual key/user/team 단위의 관리가 가능합니다. :chatgpt-content-reference{index="0"} 특히 현재 LiteLLM은 team/user/key별로 RPM, TPM, max parallel requests 를 설정할 수 있고, 모델별 제한도 가능합니다. :chatgpt-content-reference{index="1"} 그런데 중요한 부분이 하나 있습니다 LiteLLM이 GPU를 "공유"하는 것은 아닙니다. 예를 들어: User A ─┐ User B ─┼─> LiteLLM ──> vLLM 70B ──> GPU 0-3 User C ─┘ 이 경우 LiteLLM은 A: max 10 concurrent B: max 5 concurrent C: max 20 concurrent 처럼 요청을 제어 합니다. 하지만 GPU 0~3에서 실제로 A 요청 B 요청 C 요청 을 어떻게 batching하고 KV cache를 어떻게 사용하는지는 vLLM이 담당 합니다. 즉, LiteLLM = GPU scheduler가 아니라 LLM request scheduler/gateway 라고 이해하는 게 정확합니다. 그렇다면 GPU 자체를 여러 사용자가 공유하려면? 여기서 Kubernetes/NVIDIA 쪽이 필요합니다. 대표적인 방법은: 1. GPU를 모델별로 고정 할당 현재 B300 테스트에는 이 방법이 가장 자연스럽습니다. GPU 0-3 → 70B TP4 GPU 4-5 → 32B TP2 GPU 6 → 8B TP1 GPU 7 → embedding/reranker 또는 spare 그리고 여러 사용자가 LiteLLM을 통해 같은 모델을 호출합니다. User A ─┐ User B ─┼──> LiteLLM ──> vLLM 70B ──> GPU 0-3 User C ─┘ 이때 GPU는 공유되지만 모델 프로세스가 GPU를 점유하고 , vLLM이 여러 요청을 continuous batching으로 처리합니다. 저라면 지금 진행 중인 B300 POC에서는 이 구조를 1차 기준선으로 잡겠습니다. 2. GPU 하나를 여러 Pod가 time-slicing Kubernetes NVIDIA GPU Operator에는 GPU time-slicing 기능이 있습니다. GPU를 여러 replica처럼 노출하여 여러 Pod가 하나의 GPU를 공유할 수 있습니다. 다만 NVIDIA 문서에서도 설명하듯이 MIG와 달리 메모리/장애 격리가 없습니다. :chatgpt-content-reference{index="2"} 예: GPU 6 ├── Pod A ├── Pod B ├── Pod C └── Pod D 이것은 작은 모델이나 개발/테스트 환경 에는 유용하지만, 70B inference + 32B inference + 8B inference 처럼 HBM 사용량이 큰 production inference를 하나의 GPU에 무작정 섞는 방식은 별개의 문제입니다. 3. MIG MIG가 가능한 GPU에서는 GPU를 하드웨어 파티션으로 나눌 수도 있습니다. 하지만 현재 B300 8-GPU inference benchmark에서는 저는 MIG를 기본 구조로 두기보다는 MIG OFF 상태에서 먼저 검증 하는 쪽이 좋습니다. 특히 TP4/TP8 모델은 여러 GPU를 하나의 inference instance로 묶어야 하므로 MIG와 궁합이 좋지 않은 경우가 많습니다. 그러면 LiteLLM은 왜 필요한가? 여러 사용자가 들어오면 단순히 vLLM만 노출하는 것보다 관리할 것이 급격히 늘어납니다. 예를 들어: LiteLLM │ ┌───────────────┼────────────────┐ │ │ │ User A User B User C Team-A Team-A Team-B │ │ │ 100K TPM 50K TPM 200K TPM │ │ │ └───────────────┼────────────────┘ │ Model routing │ ┌────────────┼────────────┐ ▼ ▼ ▼ 70B 32B 8B LiteLLM은 현재 virtual key → user → team 단위로 모델 접근, rate limit, budget 등을 관리할 수 있습니다. :chatgpt-content-reference{index="3"} 따라서 기업 내부 AI 플랫폼에서는 상당히 유용한 계층입니다. 다만 지금 환경에서는 LiteLLM 하나로 끝내면 안 됩니다 제가 권하는 구조는 오히려 다음에 가깝습니다. ┌──────────────┐ │ Keycloak/AD │ └──────┬───────┘ OIDC │ ▼ ┌─────────────────┐ │ Cilium Gateway │ │ API / Envoy │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ LiteLLM │ │ │ │ Auth / Quota │ │ User / Team │ │ Model routing │ │ RPM / TPM │ │ concurrency │ └────────┬────────┘ │ ┌────────────┼────────────┐ │ │ │ ▼ ▼ ▼ vLLM-70B vLLM-32B vLLM-8B TP4 TP2 TP1 │ │ │ ▼ ▼ ▼ GPU 0-3 GPU 4-5 GPU 6 그리고 LiteLLM 아래에 vLLM replica가 여러 개 생기면 LiteLLM의 routing/load balancing이 의미가 더 커집니다. LiteLLM Router는 같은 model group에 여러 deployment를 두고 load balancing/fallback/retry를 수행할 수 있습니다. :chatgpt-content-reference{index="4"} 예를 들어: LiteLLM │ model = llama-70b │ ┌─────────┴─────────┐ ▼ ▼ vLLM-70B-A vLLM-70B-B GPU 0-3 GPU 4-7 이 경우에는 LiteLLM이 단순 인증 gateway를 넘어서 inference deployment 간 traffic distribution 까지 담당할 수 있습니다. 그래서 "표준"이라는 관점에서 보면 제가 표현한다면 이렇게 정리하겠습니다. 질문 답 여러 사용자가 GPU를 공유할 때 LiteLLM이 GPU를 관리하나? ❌ 여러 사용자의 LLM API를 하나로 관리하는 데 LiteLLM을 많이 쓰나? 예 사용자별 API key/quota/RPM/TPM 관리 LiteLLM이 적합 모델별 routing LiteLLM이 적합 여러 vLLM deployment load balancing LiteLLM이 가능 GPU 자체 할당 Kubernetes + NVIDIA GPU Operator GPU isolation MIG / time-slicing / Pod allocation inference batching/KV cache vLLM 인증/SSO Keycloak/AD + Gateway network isolation Cilium 현재 B300 환경이라면 저는 "LiteLLM이 GPU를 공유한다"는 설계보다는 Kubernetes가 GPU 자원을 나누고 → vLLM이 GPU 안에서 inference concurrency를 관리하고 → LiteLLM이 사용자/팀/모델 단위의 API 및 quota를 관리한다 라는 3계층 구조로 잡는 것을 권합니다. 특히 지금처럼 8-GPU B300 한 대에서 70B/32B/8B 등 서로 다른 모델을 여러 사용자가 호출하는 구조 라면, 다음 단계에서는 "GPU allocation → vLLM instance → LiteLLM model deployment → 사용자 quota"를 실제 Kubernetes YAML 수준으로 어떻게 구성할지 정하는 것이 핵심입니다. 네. 지금 말씀하신 B300 8-GPU / Kubernetes / vLLM / Keycloak / 여러 사용자 환경이라면, 저는 아래처럼 설계하는 것을 권합니다. 핵심은 GPU를 사용자별로 직접 나누지 않고, GPU를 모델 serving instance에 할당한 다음 LiteLLM이 사용자 요청을 그 instance로 제어 하는 것입니다. Keycloak / AD │ OIDC / JWT │ ▼ Cilium Gateway API / Envoy │ ▼ ┌──────────────┐ │ LiteLLM │ │ Proxy │ └──────┬───────┘ │ model_name / quota / routing │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ llama-70b qwen-32b llama-8b TP4 TP2 TP1 GPU 0-3 GPU 4-5 GPU 6 │ │ │ └─────────────┴─────────────┘ GPU 7 spare / embedding Kubernetes의 NVIDIA device plugin은 nvidia.com/gpu 리소스를 Pod에 할당하고, vLLM은 할당받은 GPU 안에서 tensor parallelism과 batching을 수행합니다. vLLM의 Kubernetes 예제도 nvidia.com/gpu 를 resource limit으로 지정하는 방식을 사용합니다. :chatgpt-content-reference{index="0"} LiteLLM은 그 위에서 model routing, RPM/TPM, max parallel requests, user/team budget 등을 담당합니다. :chatgpt-content-reference{index="1"} 1. 먼저 GPU allocation 예를 들어 B300 노드가: b300-01 ├─ GPU 0 ├─ GPU 1 ├─ GPU 2 ├─ GPU 3 ├─ GPU 4 ├─ GPU 5 ├─ GPU 6 └─ GPU 7 이라고 하면 Kubernetes 입장에서는 특별히 GPU 0-3 GPU 4-5 GPU 6 이라는 자원 풀이 존재하는 것이 아닙니다. 각 vLLM Pod가: resources: limits: nvidia.com/gpu: 4 처럼 GPU 개수를 요청합니다. 따라서 GPU ID를 직접 지정하는 것보다 Kubernetes가 GPU allocation을 담당하도록 하는 것이 기본 입니다. 2. 70B TP4 vLLM 예를 들어 70B 모델을 GPU 4장으로 서비스한다고 하겠습니다. apiVersion: apps/v1 kind: Deployment metadata: name: vllm-70b namespace: llm spec: replicas: 1 selector: matchLabels: app: vllm-70b template: metadata: labels: app: vllm-70b model: llama-70b spec: nodeSelector: accelerator: b300 containers: - name: vllm image: vllm/vllm-openai:0.9.x args: - "meta-llama/Llama-3.1-70B-Instruct" - "--tensor-parallel-size" - "4" - "--gpu-memory-utilization" - "0.90" - "--max-model-len" - "32768" - "--host" - "0.0.0.0" - "--port" - "8000" ports: - containerPort: 8000 resources: requests: cpu: "16" memory: "64Gi" nvidia.com/gpu: "4" limits: cpu: "32" memory: "128Gi" nvidia.com/gpu: "4" volumeMounts: - name: shm mountPath: /dev/shm readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 120 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 180 periodSeconds: 20 volumes: - name: shm emptyDir: medium: Memory sizeLimit: 16Gi 그리고 Service: apiVersion: v1 kind: Service metadata: name: vllm-70b namespace: llm spec: selector: app: vllm-70b ports: - port: 8000 targetPort: 8000 이렇게 됩니다. vllm-70b Pod │ ├── nvidia.com/gpu = 4 │ └── vLLM │ └── TP=4 │ ├── GPU ├── GPU ├── GPU └── GPU 여기서 중요한 점은 CUDA_VISIBLE_DEVICES 를 사용자가 직접 0,1,2,3 으로 고정하는 방식으로 운영하지 않는 것 입니다. Kubernetes/NVIDIA device plugin이 Pod에 할당한 GPU를 컨테이너에 노출시키고, vLLM은 그 GPU 집합을 대상으로 TP=4를 수행하게 하는 구조가 좋습니다. 3. 32B TP2 같은 방식입니다. apiVersion: apps/v1 kind: Deployment metadata: name: vllm-32b namespace: llm spec: replicas: 1 selector: matchLabels: app: vllm-32b template: metadata: labels: app: vllm-32b model: qwen-32b spec: nodeSelector: accelerator: b300 containers: - name: vllm image: vllm/vllm-openai:0.9.x args: - "Qwen/Qwen3-32B" - "--tensor-parallel-size" - "2" - "--gpu-memory-utilization" - "0.90" - "--max-model-len" - "32768" ports: - containerPort: 8000 resources: requests: cpu: "8" memory: "32Gi" nvidia.com/gpu: "2" limits: cpu: "16" memory: "64Gi" nvidia.com/gpu: "2" Service: apiVersion: v1 kind: Service metadata: name: vllm-32b namespace: llm spec: selector: app: vllm-32b ports: - port: 8000 targetPort: 8000 4. 8B TP1 apiVersion: apps/v1 kind: Deployment metadata: name: vllm-8b namespace: llm spec: replicas: 1 selector: matchLabels: app: vllm-8b template: metadata: labels: app: vllm-8b model: llama-8b spec: nodeSelector: accelerator: b300 containers: - name: vllm image: vllm/vllm-openai:0.9.x args: - "meta-llama/Llama-3.1-8B-Instruct" - "--tensor-parallel-size" - "1" - "--gpu-memory-utilization" - "0.90" - "--max-model-len" - "32768" ports: - containerPort: 8000 resources: requests: cpu: "4" memory: "16Gi" nvidia.com/gpu: "1" limits: cpu: "8" memory: "32Gi" nvidia.com/gpu: "1" 그러면 대략: B300 ┌─────────────────────────────────────┐ │ GPU 0 ─┐ │ │ GPU 1 │ │ │ GPU 2 ├── vLLM 70B TP4 │ │ GPU 3 ─┘ │ │ │ │ GPU 4 ─┐ │ │ GPU 5 ─┘── vLLM 32B TP2 │ │ │ │ GPU 6 ──── vLLM 8B TP1 │ │ │ │ GPU 7 ──── spare │ └─────────────────────────────────────┘ 가 됩니다. 단, 실제 GPU 번호가 이렇게 배정된다고 가정해서는 안 됩니다. Kubernetes가 어느 GPU를 해당 Pod에 제공하는지는 device plugin에 맡기고, 실제 TP 그룹과 NUMA/NVLink topology는 nvidia-smi topo -m 및 실제 Pod allocation으로 검증하는 것이 좋습니다. 5. 이제 LiteLLM이 등장합니다 LiteLLM에서는 Kubernetes Service를 deployment로 등록합니다. 예를 들어: model_list: # ------------------------------------------------ # 70B # ------------------------------------------------ - model_name: llama-70b litellm_params: model: openai/llama-70b api_base: http://vllm-70b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 8 # ------------------------------------------------ # 32B # ------------------------------------------------ - model_name: qwen-32b litellm_params: model: openai/qwen-32b api_base: http://vllm-32b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 16 # ------------------------------------------------ # 8B # ------------------------------------------------ - model_name: llama-8b litellm_params: model: openai/llama-8b api_base: http://vllm-8b.llm.svc.cluster.local:8000/v1 api_key: dummy max_parallel_requests: 32 router_settings: num_retries: 0
느낀점: 아니 이번 신문읽기는 정말 재밌기도 했지만 웃기기도 했다. 똥을로 만들었는데 그냥 콘크리트보다 더 강하다는 걸 듣고 웃기도 하고 깜짝놀라기도 했다. 석회석: 콘크리트를 만들는데 사용되는 석회석은 구하기 어렵당.. 배출: 우리 학교에선 분리배출에 대해 알아보고 실천하는걸 하고 있다. 유기물: 몸에서 나오는 배설물은 모두 유기물이다..
기록은 중요하다. 머릿속에는 그동안 공부하며 배운 내용과 경험들이 꽤 많이 쌓여 있다. 하지만 막상 누군가에게 설명하려고 하면 생각보다 잘 정리되어 있지 않다는 걸 자주 느낀다. 분명 알고 있다고 생각했는데, 정확히 설명하려고 하면 막히는 순간도 많았다. 그래서 이제부터는 내가 배운 것들을 하나씩 기록해보려고 한다. 단순히 공부한 내용을 옮겨 적는 것이 아니라, 내가 무엇을 알고 있고 무엇을 제대로 이해하지 못하고 있는지 직접 확인하면서 정리해보고 싶다. 궁금한 부분이 생기면 조금 더 깊게 파고들고, 그 과정에서 알게 된 것들도 함께 남겨보려 한다. 그 시작으로 가장 익숙하면서도, 막상 정확히 설명하려면 조금 애매한 개념부터 다뤄보려고 한다. 서버란 무엇이고, 백엔드란 무엇일까? 서버(Server)란 무엇일까? 서버(Server)는 다른 프로그램이나 컴퓨터에게 어떤 서비스나 자원을 제공하는 쪽 이다. 여기서 가장 중요하다고 생각한 것은 바로 ‘제공하는 쪽’이라는 말이다. 어떤 브라우저에서 링크를 눌러 하나의 웹 서비스에 접속했다고 생각해보자. 브라우저는 서버에게 요청을 보낸다. "이 페이지 주세요." 그러면 서버는 요청을 받아 HTML이나 필요한 데이터를 브라우저에게 돌려준다. 이 관계에서 브라우저처럼 무언가를 ‘ 요청’하는 쪽 을 ‘클라이언트(Client)’라고 하고, 그 요청을 받아 서비스나 자원을 ‘ 제공’하는 쪽 을 ‘서버(Server)’라고 한다. server 라는 단어 자체도 영어 serve , 즉 무언가를 제공하거나 응대한다는 말과 연결되어 있다. 식당에서 손님에게 음식을 제공하는 사람을 server라고 부르는 것을 생각하면 조금 더 쉽게 이해할 수 있다. 그래서 서버라는 개념은 특정한 컴퓨터를 가리키는 말이라기보다, 먼저 역할에서 시작하는 개념 이라고 보는 것이 좋다. 예를 들어 어떤 프로그램이 GET /hello 라는 요청을 받았을 때 Hello 라고 응답한다고 해보자. 이 프로그램은 요청을 받아 무언가를 제공하고 있으므로 서버의 역할을 하고 있다고 볼 수 있다. 그런데 우리는 왜 서버를 컴퓨터라고 생각할까? 흔히 이런 말을 한다. 어떤 회사의 서버 게임 서버 서버가 터졌다. 서버가 불탔다. 이런 표현들을 많이 접하다 보니 자연스럽게 서버를 하나의 컴퓨터라고 생각하기 쉽다. 하지만 개발에서 이야기하는 서버는 흔히 ‘서버 프로그램(Server Software)’을 의미한다. 실제로 요청을 받고, 처리하고, 결과를 제공하는 것은 프로그램이기 때문이다. 다만 현실에서는 그 서버 프로그램이 실행되는 컴퓨터 자체 도 서버라고 부른다. 그래서 우리가 흔히 사용하는 ‘서버’라는 단어에는 크게 세 가지 의미가 섞여 있다. 역할 요청을 받아 서비스나 자원을 제공하는 쪽 소프트웨어 실제로 요청을 받고 처리하는 프로그램 자체 컴퓨터 그 서버 프로그램이 실행되는 물리적인 컴퓨터나 가상 머신 문맥에 따라서 이 셋을 모두 ‘서버’라고 부를 수 있는 것이다. 그렇다면 서버는 꼭 요청을 받고 응답하는 것일까? 웹을 기준으로 생각하면 보통 이런 모습을 떠올릴 수 있다. Client → Request → Server Client ← Response ← Server 클라이언트가 ‘요청’하면 서버가 ‘응답’한다. 틀린 설명은 아니다. 하지만 서버의 본질을 HTTP의 ‘요청과 응답’만으로 정의하면 범위가 조금 좁아진다. 서버에는 웹 서버만 있는 것이 아니기 때문이다. 데이터를 제공하는 데이터베이스 서버 도 있고, 파일을 제공하는 파일 서버 도 있으며, 이메일 송수신 기능을 제공하는 메일 서버 도 있다. 각 서버가 제공하는 서비스나 자원의 종류가 다를 뿐이다. 따라서 서버를 조금 더 일반화해서 정의해보면 다음과 같이 생각할 수 있다. 서버란 네트워크를 통해 다른 프로그램이나 시스템에게 특정 기능, 데이터 또는 자원을 제공하는 프로그램이나 시스템이다. 무엇을 제공하는지는 서버마다 다르다. 하지만 결국 무언가를 ‘제공’하는 쪽 이라는 서버의 기본적인 역할은 변하지 않는다. 여기서 한 가지 짚고 넘어갈 점은 클라이언트와 서버는 고정된 신분이 아니라 역할이라는 것 이다. 예를 들어 웹 브라우저가 어떤 웹 서비스에 요청을 보낼 때는 브라우저가 클라이언트이고 웹 서비스가 서버이다. 하지만 그 웹 서비스가 다시 데이터베이스에 데이터를 요청한다면, 이번에는 웹 서비스가 클라이언트가 되고 데이터베이스가 서버가 된다. 즉 하나의 프로그램도 어떤 관계에서는 서버가 될 수 있고, 다른 관계에서는 클라이언트가 될 수 있다. 결국 중요한 것은 프로그램의 이름이 아니라 누가 ‘요청’하고, 누가 ‘제공’하는가 이다. 그렇다면 백엔드는 무엇인가 핵심부터 이야기하면, 백엔드(Backend)는 이름에서도 알 수 있듯이 서비스의 뒤쪽(Back) 영역이다. 사용자의 눈에 직접 보이지 않는 부분이라고 생각할 수도 있다. 하지만 단순히 보이지 않는다는 것 보다 중요한 것은 백엔드가 서비스의 데이터와 로직을 처리한다는 점 이다. 예를 들어 쇼핑몰을 생각해보자. 사용자는 화면에서 상품 목록을 보고, 장바구니 버튼을 누르고, 결제 버튼을 누르고, 자신의 주문 내역을 확인한다. 이처럼 사용자가 직접 보고 상호작용하는 화면과 UI는 주로 프론트엔드의 영역이다. 그런데 사용자가 결제 버튼을 누르면 어떻게 될까? 먼저 프론트엔드는 사용자의 클릭이라는 입력을 처리하고, 필요한 경우 백엔드에 요청을 보낸다. 그러면 백엔드에서는 여러 가지 일이 일어날 수 있다. 로그인한 사용자가 맞는지 확인하고, 상품의 재고를 확인하고, 가격을 계산하고, 쿠폰을 적용하고, 결제를 처리하고, 주문 정보를 저장한다. 우리가 서비스를 이용하면서 당연하게 생각했던 수많은 흐름이 뒤에서 처리되고 있는 것이다. 이러한 비즈니스 로직과 데이터 처리 를 담당하는 부분이 백엔드이다. 그렇다면 백엔드는 서버인가? 완전히 같은 말은 아니다. 앞서 이야기했듯 서버는 어떤 서비스를 ‘ 제공’하는 역할 을 중심으로 바라보는 개념이다. 반면 백엔드는 웹이나 앱 서비스에서 특정한 일을 담당하는 ‘ 영역’ 에 가까운 개념이다. 조금 단순하게 생각하면, 서버는 ‘누가 서비스를 제공하는가’를 바라보는 개념이고,백엔드는 ‘서비스 뒤에서 어떤 일을 처리하는가’를 바라보는 개념이다. 처음에는 이 구분이 조금 헷갈렸다. 예를 들어 어떤 프레임워크로 만든 프로그램이 회원가입 API를 제공한다고 해보자. POST /users 이 프로그램은 클라이언트의 요청을 받아 회원가입이라는 서비스를 제공한다. 그렇기 때문에 서버 역할을 하는 프로그램 이라고 할 수 있다. 동시에 그 내부에서는 입력된 데이터를 검증하고, 회원 정보를 데이터베이스에 저장하고, 회원가입에 필요한 여러 로직을 처리한다. 따라서 이 프로그램은 백엔드 애플리케이션 이기도 하다. 즉 하나의 프로그램을 서로 다른 관점에서 바라보는 것이다. Server의 관점 → 클라이언트의 요청을 받고 서비스를 제공한다. Backend의 관점 → 서비스에 필요한 로직과 데이터를 처리한다. 그래서 실제 개발에서는 같은 대상을 두고 "서버를 개발한다." 라고 말하기도 하고, "백엔드를 개발한다." 라고 말하기도 한다. 두 개념이 자주 함께 등장하고 실제로 겹치는 부분이 많기 때문에 비슷한 의미로 사용되지만, 개념적으로 완전히 같은 말은 아니다. 지금처럼 바이브 코딩을 통해 누구나 빠르게 서비스를 만들 수 있는 환경에서는 코드를 직접 하나하나 작성하지 않고도 꽤 그럴듯한 서비스를 만들어낼 수 있다. 하지만 내가 만들고 있는 서비스가 어떤 구조로 동작하는지 , 사용자의 요청이 어떤 로직을 거쳐 처리되는지 , 데이터가 어디에 저장되고 어떻게 사용되는지 , 그리고 그 데이터를 어떻게 안전하게 다뤄야 하는지 를 이해하려면 결국 서비스의 뒤쪽을 알아야 한다고 생각한다. 단순히 ‘동작하는 프로그램’을 만드는 것을 넘어, 내가 무엇을 만들고 있는지 이해하는 사람이 되고 싶다. 좋은 개발자로, 그리고 좋은 기획자로 성장하기 위해 앞으로도 내가 당연하게 사용해왔지만 정확하게 설명하지 못했던 것들을 하나씩 공부하고 기록해보려고 한다. 최대한 꾸준히.
NVIDIA B300(Blackwell Ultra, 단일 GPU당 HBM3e 288GB 탑재) 8장으로 구성된 단일 노드는 총 VRAM만 약 2.3TB 에 달하며, 5세대 NVLink(양방향 1.8TB/s per GPU)로 풀메시(Full-mesh) 연결되어 있습니다. 단일 노드 내에서 여러 모델(예: DeepSeek 계열, Qwen3-32B, 경량 임베딩/리랭커 등)을 동시에 서빙할 때 NVLink 토폴로지 유지, VRAM/KV Cache 고립, 엔진 서빙 아키텍처 및 메트릭 관측성 관점에서의 베스트 프랙티스는 다음과 같습니다. 1. GPU 토폴로지 기반 자원 파티셔닝 (Tensor Parallelism 분할) B300 8-way 노드는 NVLink Switch를 통해 8장이 단일 패브릭으로 묶여 있지만, 여러 모델을 동시에 쪼개어 서빙할 때는 NUMA 노드와 PCIe/NVLink 도메인 에 맞춰 2의 거듭제곱 단위( TP=1 , 2 , 4 , 8 )로 격리해야 All-Reduce 병목이 발생하지 않습니다. 권장 모델 배치 시나리오 케이스 A: 초대형 MoE 1개 + 중소형 모델 병행 (가장 흔한 멀티모델 패턴) GPU 0~3 (TP=4): DeepSeek-R1-Distill-70B 또는 중대형 MoE 모델 (4장 × 288GB = 1.15TB VRAM 풀) GPU 4~5 (TP=2): Qwen3-32B / Llama 계열 메인 LLM 추론 (2장 × 288GB = 576GB VRAM) GPU 6 (TP=1): 경량 7B~14B 모델 또는 코드 전용 LLM GPU 7 (TP=1): 임베딩(Embedding) + 리랭커(Reranker) 전용 인스턴스 (vLLM 또는 TEI) 케이스 B: DeepSeek-R1 Full (FP8/FP4) 전용 서빙 DeepSeek 671B MoE 가중치(FP8 기준 약 700GB)는 단일 노드 8장(TP=8, EP=8)으로 VRAM에 완전히 올릴 수 있으므로, 멀티 테넌트보다는 단일 노드 전체를 전용으로 할당하는 것이 성능상 유리합니다. CPU & NUMA Affinity 바인딩 (필수) 8장 노드는 통상 듀얼 소켓 CPU(NUMA 0, NUMA 1) 구조입니다. 모델 서빙 프로세스가 GPU뿐만 아니라 대응하는 NUMA 노드의 CPU 코어/메모리에 정확히 고정되어야 인터커넥트 병목이 없습니다. # 예: GPU 0~3번은 NUMA node 0번에 고정하여 vLLM 인스턴스 기동 numactl --cpunodebind=0 --membind=0 vllm serve /models/Qwen-70B \ --tensor-parallel-size 4 \ --gpu-memory-utilization 0.90 \ --port 8001 2. 추론 엔진(Engine) 및 메모리 격리 전략 여러 추론 프로세스가 동일 물리 노드에 공존할 때 OOM(Out of Memory) 연쇄 장애를 막는 설정입니다. CUDA_VISIBLE_DEVICES 하드웨어 격리: 각 서빙 컨테이너(또는 프로세스)에는 사용할 GPU 인덱스만 노출시킵니다. Container A: CUDA_VISIBLE_DEVICES=0,1,2,3 Container B: CUDA_VISIBLE_DEVICES=4,5 --gpu-memory-utilization 고정 및 KV Cache 버퍼 제어: vLLM/SGLang은 기본적으로 GPU 메모리의 90% 이상을 가중치 + KV Cache 블록으로 사전 선점(Pre-allocate)합니다. 멀티 프로세스 환경에서는 프레임워크 오버헤드, PyTorch CUDA 컨텍스트, PagedAttention 관리 버퍼를 고려하여 0.85 ~ 0.90 수준으로 설정해 메모리 스파이크를 방지합니다. MIG(Multi-Instance GPU) vs 프로세스 기반 분할: B300에서는 가급적 MIG 대신 vLLM 단일 컨테이너 단위 분할 권장: MIG를 활성화하면 NVLink 대역폭 활용이 제한되고 Tensor Parallelism 제약이 생깁니다. LLM 추론은 NVLink를 온전히 활용할 수 있도록 베어메탈/컨테이너 레벨에서 GPU 카운트 단위( TP=1, 2, 4 )로 물리 카드를 분배하는 것이 좋습니다. 3. 라우팅 및 동적 부하 분산 (Front Gateway) 노드 1대 내부에서 모델별로 각기 다른 포트(8001, 8002, 8003...)로 엔진이 뜨게 되므로, 앞단에 경량 AI 게이트웨이를 배치해야 클라이언트 관리가 단순해집니다. Reverse Proxy / Gateway: Litellm , Envoy , 또는 Traefik 포트 8000 단일 엔드포인트 노출 후 요청 헤더/바디의 model 필드에 따라 로컬 포트로 라우팅: "model": "qwen3-32b" $\rightarrow$ localhost:8001 "model": "deepseek-r1-distill-70b" $\rightarrow$ localhost:8002 "model": "bge-reranker" $\rightarrow$ localhost:8003 Prefix Caching 활성화: 동일 모델에 반복되는 시스템 프롬프트(System Prompt)가 많다면 --enable-prefix-caching 을 켜서 중복 토큰 연산과 KV Cache 소비를 40~60% 절감합니다. 4. 모니터링 및 관측성(Observability) 파이프라인 단일 노드 멀티 모델 환경에서는 "어느 모델이 어느 GPU 대역폭을 잠식하고 있는지"를 즉각 분리해 관측해야 합니다. [B300 GPUs] ──> DCGM Exporter (포트 9400) ──┐ [vLLM #1] ──> Prometheus 메트릭 (8001) ──┼──> Prometheus Server ──> Grafana Dashboard [vLLM #2] ──> Prometheus 메트릭 (8002) ──┘ A. 하드웨어 레벨: NVIDIA DCGM Exporter (Blackwell 지원 버전) 포트: 기본 9400 필수 감시 메트릭: DCGM_FI_DEV_GPU_UTIL : GPU 연산 코어 가동률 DCGM_FI_DEV_MEM_COPY_UTIL : HBM3e 메모리 대역폭 포화 여부 DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL : GPU 간 All-Reduce 교환량 (TP 분할 모델 정상 동작 검증) DCGM_FI_DEV_POWER_USAGE / DCGM_FI_DEV_GPU_TEMP : B300 노드 고전력/열 부하 감시 B. 서빙 엔진 레벨: vLLM / SGLang 내장 Prometheus 메트릭 각 vLLM 인스턴스는 자체 포트(예: :8001/metrics , :8002/metrics )로 상세 메트릭을 노출합니다. KV Cache 포화도: vllm:gpu_cache_usage_factor : 가장 중요. 0.85 이상 지속 시 새 요청이 큐에 쌓이며 TTFT(Time to First Token) 지연 급증. 대기열 병목: vllm:num_requests_waiting : 요청이 처리되지 못하고 큐에 머무는 수. 0보다 크면 동시 요청 처리 한계 도달을 의미. vllm:num_requests_running : 현재 PagedAttention으로 동시 디코딩 중인 배치 크기. 처리 성능(Latency & Throughput): vllm:time_to_first_token_seconds : 초기 Prefill 지연 시간. vllm:time_per_output_token_seconds : 디코딩 토큰 생성 간격 (사용자 체감 속도). 5. 핵심 운용 체크리스트 점검 영역 권장 설정값 / 액션 목적 NUMA 바인딩 numactl --cpunodebind=X --membind=X 소켓 간 PCIe 크로스오버 통신 억제 GPU 격리 컨테이너별 CUDA_VISIBLE_DEVICES 명시 프로세스 간 VRAM 침범 및 충돌 방지 KV Cache --gpu-memory-utilization 0.85~0.90 PagedAttention 할당 버퍼 확보로 OOM 방지 멀티 모델 분할 TP 크기는 2의 제곱수( 1, 2, 4, 8 ) 유지 NVLink 전송 효율 최적화 및 비대칭 링 방지 로그 수집 vLLM Engine 에러 로그와 DCGM XID 수집 하드웨어 ECC 에러 및 엔진 Crash 빠른 격리 LLM 서빙 환경에서 사용자/팀(Tenant)별 인증·인가 및 사용량 제한(Rate Limiting & Quota)을 vLLM 같은 백엔드 추론 엔진에 직접 맡기는 것은 불가능합니다(대부분의 오픈소스 추론 엔진은 OpenAI 규격의 API 서빙 기능만 제공하며 Auth/Accounting 레이어가 없음). 따라서 추론 엔진 앞단에 LLM 전용 AI Gateway 를 배치하고, 백엔드로 Redis 및 저장소를 연동하는 아키텍처가 표준 베스트 프랙티스입니다. 가장 널리 쓰이는 표준 조합은 LiteLLM Proxy , Envoy / Kong , 또는 Portkey 기반 아키텍처입니다. 1. 표준 아키텍처 구성 [Client / Tenant A] ──(API Key A)──┐ ▼ [Client / Tenant B] ──(API Key B)──▶ [ AI Gateway (LiteLLM Proxy) ] ──▶ [ Redis ] (토큰 버킷/사용량 계량) │ ├── /v1/chat/completions (model: qwen3-32b) ──▶ vLLM #1 (GPU 0~3, :8001) └── /v1/chat/completions (model: deepseek) ──▶ vLLM #2 (GPU 4~7, :8002) 2. 컴포넌트별 구현 방안: 인증 및 인가 (AuthN / AuthZ) 1 API Key 기반 테넌트 인증 (가장 일반적) 게이트웨이가 고유한 가상 API Key( sk-team-a-... , sk-team-b-... )를 발급하고, 이 키를 특정 팀/유저 메타데이터와 매핑합니다. 인증(AuthN): Authorization: Bearer sk-... 헤더 검증 인가(AuthZ - 모델 접근 제어): 테넌트 A는 qwen3-32b , bge-m3 모델만 호출 가능 테넌트 B(연구팀)는 고비용의 deepseek-r1 까지 호출 가능하도록 라우팅 제어 2 사내 IdP 연동 (SSO / OAuth2 / JWT) 사내 Keycloak, Okta, 또는 LDAP/Active Directory와 연동하는 경우: 클라이언트는 Bearer JWT 토큰을 전달하고, Gateway가 JWT의 Claim(예: groups , sub , roles )을 파싱하여 테넌트를 식별하고 인가를 수행합니다. 3. 사용량 제한 (Rate Limiting & Quota) 설계 LLM의 부하 제어는 일반 웹 API(RPM 중심)와 완전히 다릅니다. RPM(Requests Per Minute), TPM(Tokens Per Minute), TPD(Tokens Per Day) / 비용 한도 를 계층적으로 통제해야 합니다. 제어 항목 목적 기준 RPM (Requests Per Min) 추론 엔진의 큐 폭주(Queue Starvation) 방지 동시 처리 요청 수 제어 TPM (Tokens Per Min) KV Cache 고갈 및 순간 연산량 폭증 방지 입력 프롬프트 + 출력 생성 토큰 합산 TPD / Budget ($/일, $/월) 특정 팀의 일방적인 GPU 자원 독점 방지 일간/월간 누적 토큰 또는 환산 비용 4. 구현 실전 예시: LiteLLM Proxy 설정 오픈소스 기반으로 가장 빠르고 안정적으로 멀티테넌시를 구축할 수 있는 LiteLLM Proxy + Redis 기준 설정 예시입니다. 1) config.yaml 정의 model_list: # 백엔드 vLLM #1 (Qwen3-32B) - model_name: qwen3-32b litellm_params: model: openai/qwen3-32b api_base: http://localhost:8001/v1 api_key: "none" # 백엔드 vLLM #2 (DeepSeek-R1) - model_name: deepseek-r1 litellm_params: model: openai/deepseek-r1 api_base: http://localhost:8002/v1 api_key: "none" general_settings: master_key: "sk-admin-master-key-here" # 게이트웨이 관리자 키 database_url: "postgresql://user:pass@localhost:5432/litellm" # 키/유저 영구 저장소 redis_url: "redis://localhost:6379" # 분산 Rate Limiting용 캐시 2) 게이트웨이 기동 docker run -d \ -v $(pwd)/config.yaml:/app/config.yaml \ -p 8000:4000 \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml 3) 관리자 API로 테넌트별 API Key 및 쿼터 생성 팀 A (일반 서비스팀 - Qwen만 사용 가능, 엄격한 쿼터 적용): curl -X POST 'http://localhost:8000/key/generate' \ -H 'Authorization: Bearer sk-admin-master-key-here' \ -H 'Content-Type: application/json' \ -d '{ "user_id": "service_team_a", "models": ["qwen3-32b"], "max_budget": 100, # 예산 한도 ($ 또는 환산 단위) "rpm_limit": 60, # 분당 60회 요청 제한 "tpm_limit": 100000, # 분당 10만 토큰 제한 "duration": "30d" }' # 반환된 가상 키(sk-...)를 팀 A에 배포 팀 B (연구/코어팀 - 전 모델 허용, 높은 쿼터): curl -X POST 'http://localhost:8000/key/generate' \ -H 'Authorization: Bearer sk-admin-master-key-here' \ -H 'Content-Type: application/json' \ -d '{ "user_id": "research_team_b", "models": ["qwen3-32b", "deepseek-r1"], "rpm_limit": 300, "tpm_limit": 500000 }' 5. 선점 방지(Noisy Neighbor)와 백엔드 보호 기법 게이트웨이의 계량 기능 외에, 특정 테넌트가 B300 노드의 VRAM 전체를 고갈시키는 것을 막기 위해 추론 엔진 레벨에서도 방어벽을 세워야 합니다. 최대 컨텍스트 길이( --max-model-len ) 강제: 아무리 쿼터를 걸어도 단일 요청에서 수십만 토큰짜리 프롬프트를 밀어 넣으면 엔진의 KV Cache가 즉시 고갈되어 타 테넌트 요청이 큐에 대기(Waiting)됩니다. vLLM 기동 시 --max-model-len 32768 등으로 현실적인 한도를 강제합니다. 동시 디코딩 시퀀스 상한( --max-num-seqs ): 한 번에 처리할 수 있는 최대 배치 크기를 128~256 수준으로 제한하여 스케줄러 큐의 공평성(Fairness)을 유지합니다. HTTP 429 Retry-After 헤더 활용: 게이트웨이에서 특정 테넌트가 TPM/RPM 한도에 도달하면 429 Too Many Requests 와 함께 Retry-After: 3 헤더를 내려주어 클라이언트 SDK가 지수 백오프(Exponential Backoff)를 수행하도록 유도합니다. 네, 가능합니다. 엔터프라이즈 환경에서는 개발자/서비스팀마다 수동으로 API Key를 발급하기보다 사내 IdP(Keycloak, Microsoft Entra ID / Active Directory, Okta 등)와 연동하여 OAuth2/OIDC JWT Bearer 토큰 으로 인증 및 인가를 처리하는 구조가 표준입니다. 구현 방식은 크게 AI Gateway 내장 OIDC 기능 활용 과 앞단 Reverse Proxy(Envoy/Kong) 계층 분리 의 두 가지 패턴으로 나뉩니다. 아키텍처 흐름 (OIDC / OAuth2 Bearer Flow) [클라이언트/사내 유저] │ │ 1. 인증 요청 (Client Credentials 또는 사내 계정 SSO) ▼ [Keycloak / AD (IdP)] ──(유저 그룹/권한/부서 클레임 포함)──▶ Access Token (JWT) 발급 │ │ 2. API 호출 (Authorization: Bearer <JWT>) ▼ [AI Gateway (LiteLLM Proxy 등)] │ ├─▶ 3. JWKS 검증 (서명 검증, 만료 확인) ├─▶ 4. JWT Claim 파싱 (sub=팀ID, groups=부서명, scope 등) ├─▶ 5. Redis 기반 Dynamic Quota 체크 (해당 부서/팀의 TPM/RPM 잔여량) │ ▼ (정상 통과 시) [vLLM 추론 인스턴스 (B300)] 1. Keycloak / AD 사전 세팅 (IdP 설정) 사내 Keycloak(또는 Azure AD / On-Prem AD FS)에서 다음을 구성합니다. Client 생성: Client ID : ai-inference-platform Client Authentication : On (Service Account 기반 머신-투-머신 통신 시) Standard Flow (유저 인터랙티브 로그인용) 또는 Service Accounts Roles (서버-투-서버 연동용 client_credentials ) Token Mapper (Claim) 추가: JWT 페이로드에 팀명, 부서 코드, 또는 그룹 정보가 들어가도록 설정합니다. 예: groups: ["data-platform-team", "ai-research"] 또는 department: "sre" 2. 구현 방식 A: LiteLLM Proxy 자체 OIDC/JWT 검증 연동 LiteLLM은 수신된 Authorization: Bearer <JWT> 헤더를 사내 IdP의 JWKS(Public Key) 엔드포인트로 직접 검증할 수 있습니다. config.yaml 설정 예시 model_list: - model_name: qwen3-32b litellm_params: model: openai/qwen3-32b api_base: http://localhost:8001/v1 - model_name: deepseek-r1 litellm_params: model: openai/deepseek-r1 api_base: http://localhost:8002/v1 general_settings: master_key: "sk-admin-master-key" redis_url: "redis://localhost:6379" router_settings: # Keycloak / AD의 OIDC 설정 jwt_auth: # Keycloak의 JWKS URL (서명 공개키 자동 갱신) jwks_url: "https://keycloak.company.com/realms/corp-realm/protocol/openid-connect/certs" # 토큰의 Issuer 검증 expected_issuer: "https://keycloak.company.com/realms/corp-realm" # 토큰의 Audience 검증 expected_audience: "ai-inference-platform" # 테넌트 식별자로 사용할 JWT Claim 필드 (예: client_id, sub, 또는 커스텀 클레임) user_id_jwt_field: "preferred_username" # 팀/부서 식별용 Claim (그룹 매핑) team_id_jwt_field: "department" 동적 인가 및 사용량 정책 (Tier 매핑) JWT의 특정 Claim(예: department )에 따라 허용 모델과 쿼터를 Gateway 데이터베이스에 사전 정의해 둘 수 있습니다. 사전 등록 정책 예시 (API로 등록): # 'data-sre' 부서에 대한 기본 쿼터 및 접근 모델 정의 curl -X POST 'http://localhost:8000/team/new' \ -H 'Authorization: Bearer sk-admin-master-key' \ -H 'Content-Type: application/json' \ -d '{ "team_id
k8s 환경에서의 테스트 추가 고려사항 K8s + Cilium ClusterMesh 조합이면, 지금까지 bare-metal에서 검증한 걸 "그대로 믿으면 안 되는" 항목들이 꽤 생김. 단순히 새 테스트를 추가하는 것보다, 컨테이너화/오버레이 네트워크가 기존 검증 결과를 몰래 깨뜨리지 않는지 재검증하는 항목 과 K8s+ClusterMesh 고유 신규 항목 을 나눠서 보는 게 정확함. A. 가장 먼저 봐야 할 것 — NCCL 재검증 Stage1에서 NCCL All-Reduce로 NVLink 대역폭을 검증했는데, K8s Pod 내부에서 반드시 재검증 필요. 현재 Cilium이 Native Routing을 사용하는 환경이어서 Pod의 eth0는 veth 기반이지만 Cilium eBPF와 Linux native routing을 거쳐 물리 NIC으로 전달됨. Single-node 8-GPU AllReduce에서는 NVLink/NVSwitch 경로가 핵심이므로 Cilium networking 영향은 제한적일 가능성이 높음. 반면 multi-node NCCL에서는 Pod → veth → Cilium → native routing → BGP/ECMP → physical NIC 경로가 실제 성능에 영향을 줄 수 있으므로 별도의 검증이 필요. 따라서 K8s 검증에서는 Bare Metal → hostNetwork Pod → Cilium Pod를 비교하고, NCCL 로그에서 실제 transport/interface 선택을 확인(NCCL_SOCKET_IFNAME이 올바른 물리 NIC을 가리키는지). Multus/SR-IOV는 성능 저하가 확인될 경우 비교 대상으로 추가. MTU 불일치 확인: Native Routing 환경이어도 물리 스위치(Jumbo Frame 9000), 노드 인터페이스, Pod veth 간 MTU가 일치하지 않으면 TCP 패킷 단편화로 NCCL 링 레이턴시가 급증합니다. Socket Buffer Sizing: RDMA가 없는 TCP NCCL 환경에서는 NCCL_BUFFSIZE 및 리눅스 커널의 net.ipv4.tcp_rmem/wmem 파라미터가 Pod 네트워크 네임스페이스에 어떻게 전파되는지(sysctl allowlist) 확인해야 제 성능이 나옵니다. B. Cilium ClusterMesh 자체 RDMA가 없는 상황에서는 TCP 경로에 추가 오버헤드가 조금이라도 붙으면 그대로 성능 손실 로 직결됨. Cilium VXLAN/Geneve 캡슐화 모드 대비 Native Routing(BGP로 Pod CIDR을 직접 광고, 캡슐화 없음) 모드가 오버헤드가 훨씬 적음. 재검증 필요 : Stage2의 iperf3 단일/멀티 스트림, LACP 25G/50G 테스트를 Pod-to-Pod(특히 클러스터 간)로 재실행 해서 bare-metal 대비 손실폭을 측정. ClusterMesh 경유 시 홉이 늘어나는 만큼 지연(RTT)이 늘어나는지도 별도 측정. BGP 라우트 수렴 시간(BGP BFD(Bidirectional Forwarding Detection) 연계 여부에 따른 수렴 시간 검증): 노드 장애/재기동 시 라우트가 얼마나 빨리 갱신되는지(수 초 vs 수십 초) — 이게 느리면 장애 시 트래픽 블랙홀 구간이 생김 Conntrack 테이블 포화: All-Reduce나 분산 추론 시 다수의 TCP 세션이 폭발할 때 eBPF/Linux conntrack 테이블(sysctl net.netfilter.nf_conntrack_max) 한도에 걸리지 않는지 확인해야 함. eBPF Host-Routing 우회 여부: Cilium이 Host-Routing 모드로 동작하여 Pod veth의 TCP 스택을 바이패스(tc BPF)하고 있는지 cilium status --verbose로 체크 리스트에 포함. C. GPU 스케줄링이 NVLink 토폴로지를 깨지 않는지 NVIDIA GPU Operator + Node Feature Discovery + (필요시) Topology Manager가 제대로 구성되어 있는지. TP=8 파드가 8장을 요청했을 때 실제로 이 8장이 물리적으로 같은 NVSwitch 도메인 안에 있는 GPU들인지 (멀티 NVSwitch 보드 구성이면 쪼개질 수 있음) 확인이 필요. MIG를 켰다면 MIG 슬라이스 간 NVLink 특성이 완전히 달라지므로 MIG 미사용/사용 여부를 먼저 확정. NUMA-NIC-GPU Alignment: 8장이 통째로 할당되는 전체 점유 상황 외에도, 부분 할당이나 PCIe/NIC 인터페이스가 특정 NUMA 노드에 바인딩되어 있을 때 GPU와 NIC 간 CPU 소켓 간 트래픽(UPI/QPI traversal)이 발생하는지 Kubelet TopologyManager 정책(single-numa-node 또는 restricted)을 통해 확실히 묶어야 함. D. 기존 Compute 클러스터(Spark/StarRocks/Trino/JupyterLab) 특화 부하 패턴 지금까지의 stage3a6 (포화점), stage5b (부하테스트)는 "점진적으로 동시성을 올리는" 패턴이었는데, 실제 이 엔진들의 트래픽은 성격이 다름. 엔진 실제 패턴 추가로 봐야 할 것 Spark 수백 개 executor가 동시에 폭발적으로 추론 요청을 쏨(스텝 함수형 버스트) 점진적 동시성 램프업이 아니라 즉각적 스파이크 (예: 0→256 동시성이 수 초 내) 테스트, LiteLLM/Envoy 레벨 커넥션 풀 고갈 여부 Trino/StarRocks 쿼리 페더레이션 중 GPU 노드로의 소수의 긴 요청(UDF 스타일) 쿼리 타임아웃과 vLLM 응답시간 불일치 시 커넥션 누수 여부 JupyterLab 사람이 쓰는 인터랙티브 — 낮은 동시성, 매우 불규칙, 장시간 유휴 후 갑작스런 요청 Stage1에서 짚었던 "유휴 후 첫 요청 지연(warm-up latency)"이 K8s 파드 레벨에서도 재현되는지 — 특히 GPU가 아니라 파드 자체가 스케일다운되어 있다가 콜드 스타트 하는 경우(오토스케일링 쓴다면) 지연이 수십 초까지 갈 수 있음 신규 필요 : Spark 스타일 "스텝 버스트" 부하 생성기 — 기존 stage3a6 (점진적 램프업)와 별개로, 순간적으로 목표 동시성에 도달하는 패턴 전용 스크립트. SYN Queue / Listen Backlog: Spark executor 수백 개가 동시에 vLLM 엔드포인트(혹은 앞단의 Gateway/Envoy)로 TCP 커넥션을 맺을 때 net.core.somaxconn 및 tcp_max_syn_backlog 제한으로 인한 패킷 드롭이 발생하기 쉽습니다. vLLM Continuous Batching 큐잉 지연: 스텝 버스트 인입 시 vLLM의 KV Cache가 포화되어 대기 큐(Waiting Queue)에 머무르는 시간이 길어지며 발생하는 Time-to-First-Token(TTFT)의 P99 스파이크를 측정 지표로 명시하면 좋음. E. MinIO AIStor 접근 경로 변화 기존엔 STORAGE_NODE_IP 고정 IP로 접근했는데, ClusterMesh에서는 Global Service (같은 이름의 서비스가 여러 클러스터에 존재할 때 자동 로드밸런싱/장애조치)로 접근하게 됨. 이 서비스 디스커버리 자체의 지연과, 실제로 "가까운"(같은 클러스터) MinIO 백엔드를 우선 선택하는지(Cilium의 로컬리티 우선순위 설정) 확인 필요. 재검증 필요 : Stage2의 MinIO Cold Load 테스트를 ClusterMesh 경유로 재실행해서 대역폭 손실 여부 확인. Cilium Topology Aware Routing: service.kubernetes.io/topology-mode: Auto 또는 Cilium 서비스 어노테이션을 통해 Compute 파드가 속한 클러스터/존의 MinIO 파드로 트래픽을 강제하는지 검증 필요. 로컬 노드 백엔드가 준비 상태임에도 ClusterMesh 터널을 타고 원격 클러스터 MinIO로 트래픽이 새어나가지 않는지 패킷 경로 확인이 필수. F. 회복탄력성 (K8s라서 새로 생기는 항목) 노드 드레인/코든 : 유지보수 시 GPU 노드를 드레인하면 서빙 중인 vLLM 파드가 얼마나 우아하게 종료되는지(진행 중인 요청 처리 후 종료 vs 강제 킬), PodDisruptionBudget 동작 확인 Readiness Probe 오탐 : DeepSeek-R1처럼 로딩에 수십 분 걸리는 모델은, 기본 HTTP readiness probe가 "포트는 열렸지만 모델 로딩 안 끝남" 상태에서 트래픽을 조기에 흘려보낼 위험 — initialDelaySeconds / startupProbe 설정 검증 필수 ClusterMesh 단절 시뮬레이션 : 클러스터 간 연결이 일시적으로 끊겼을 때 GPU 노드가 로컬 트래픽은 정상 처리하는지, Compute 클러스터 쪽은 재시도/서킷브레이커로 우아하게 실패하는지(캐스케이딩 장애 방지) StartupProbe 분리: Readiness 대신 startupProbe(failureThreshold × periodSeconds를 30분 이상으로 넉넉히 설정)를 적용하여 모델 체크포인트 다운로드 및 메모리 로딩 중 Kubelet이 컨테이너를 OOM/Restart 루프에 빠뜨리지 않도록 구성해야 합니다. preStop Hook 구현: Kubelet 드레인 시 vLLM 프로세스가 SIGTERM을 받기 전 인그레스/엔드포인트 슬라이스에서 먼저 제외되도록 5~10초간 sleep을 주는 preStop 훅 유무를 검증 항목에 추가하세요. G. 관측성 통합 Stage5e에서 독립적으로 띄운 Prometheus/Grafana를, 기존 클러스터에 이미 있는 모니터링 스택과 federation 할지 별도로 유지할지 결정 필요 Cilium Hubble 로 클러스터 간 트래픽 플로우/정책 드롭을 시각화 — 특히 네트워크 정책으로 막힌 트래픽을 조기에 발견하는 용도로 유용 H. 보안/네트워크 정책 기존엔 firewalld 규칙이었는데, K8s에서는 CiliumNetworkPolicy / CiliumClusterwideNetworkPolicy 로 전환 — Compute 클러스터의 어떤 워크로드 아이덴티티(namespace/서비스어카운트)만 GPU 서비스에 접근 가능한지 를 IP 기반이 아니라 아이덴티티 기반으로 재설계·검증 I. 리소스 경합 (노이즈 네이버) ClusterMesh로 물리 네트워크 팹릭을 공유하게 되므로, Spark의 대용량 셔플/StarRocks 쿼리가 네트워크를 점유할 때 GPU 노드의 외부 통신(모델 다운로드, KV 티어 트래픽)이 밀리는지 — Cilium Bandwidth Manager(대역폭 제한/QoS) 설정 여부에 따라 결과가 크게 갈림. Cilium Bandwidth Manager는 Linux 커널 FQ(Fair Queueing)와 BPF 기반 패킷 스케줄링을 사용하므로, 노드 커널 버전 호환성과 활성화 여부(enable-bandwidth-manager: true)를 사전 확인해야 합니다. CiliumNetworkPolicy 적용 시 eBPF 맵 엔트리가 과도하게 늘어나 데이터 플레인 지연(BPF Map lookup overhead)이 생기지 않는지 부하 상태에서 CPU/지연을 함께 관측하는 것이 좋음. 우선순위 영역 테스트 시나리오 사전 조건 및 필수 설정 핵심 측정 지표 합격 기준 (Pass Criteria) P0 (Critical) 네트워크 기반 Host vs hostNetwork vs Pod veth TCP 성능 및 RTT 비교 MTU 9000 통일(스위치·노드·Pod veth); Cilium Native Routing 및 eBPF Host Routing 활성화 iperf3 단일·멀티 Throughput; ping·sockperf RTT; CPU softirq 점유율 Bare-metal 대비 처리량 손실률 5% 이하; Pod 간 RTT 증가 0.1ms 이하 P0 (Critical) GPU 통신 Pod 환경 NCCL All-Reduce 대역폭 및 인터페이스 바인딩 검증 NCCL_SOCKET_IFNAME 물리 NIC 지정; Pod net.ipv4.tcp_rmem/wmem 튜닝; NVIDIA Operator 및 Topology Manager 구성 Bus Bandwidth (GB/s); NCCL 커널 로그 인터페이스 확인; NUMA 소켓 간 트래픽 여부 단일 노드 8-GPU: Bare-metal 대비 NVLink 98% 이상 유지; 멀티 노드: Bare-metal 대비 TCP 90% 이상 유지 P0 (Critical) 수명주기/안정성 대형 모델(R1 등) Pod 콜드 스타트 및 Startup/Readiness 프로브 오탐 검증 startupProbe 분리(failureThreshold 넉넉히 설정); Ingress/Service 라우팅 연계 모델 가중치 메모리 적재 완료 시간; Probe 실패로 인한 컨테이너 비정상 재시작 횟수 초기 모델 로딩 중 비정상 재시작 0회; 실제 서빙 준비 완료 전 트래픽 유입 0건 P1 (High) 서비스 디스커버리 ClusterMesh Global Service 경유 MinIO Cold Load 및 로컬리티 라우팅 검증 Cilium ClusterMesh 피어링 정상 상태; service.kubernetes.io/topology-mode: Auto 적용 MinIO 읽기·쓰기 Throughput (MB/s); 클러스터 간 Egress/Ingress 트래픽 발생 비율 동일 클러스터 내 백엔드로 100% 로컬 라우팅 유지; 고정 IP 직접 접근 대비 전송 속도 저하 3% 이하 P1 (High) 워크로드 부하 Spark 대규모 동시 요청(스텝 버스트: 0→256) 급증 시 TCP 커넥션 처리 somaxconn 및 tcp_max_syn_backlog 상향; Envoy/LiteLLM 연결 풀 설정 완료 SYN Drop 횟수 (netstat -s); Conntrack 테이블 사용률; TTFT P95/P99 레이턴시 SYN 드롭 및 커넥션 거부 0건; Conntrack 테이블 포화율 70% 이하 P1 (High) 회복탄력성 GPU 워커 노드 유지보수(Drain/Cordon) 시 Graceful Shutdown 및 PDB 동작 preStop 훅(5~10초 sleep); PodDisruptionBudget(minAvailable) 설정 처리 중이던 In-flight 요청 실패율; 엔드포인트 슬라이스 동기화 지연 노드 드레인 시 처리 중인 요청의 502/504 에러 0건; PDB 위반 없는 롤링 퇴거 확인 P2 (Medium) 라우팅 수렴 ClusterMesh 장애·단절 시뮬레이션 및 BGP 라우트 수렴 시간 측정 BGP 라우팅 피어링 및 BFD 연동; 장애 주입 툴 또는 인터페이스 down 스크립트 BGP Route Convergence Time (초); 클러스터 단절 중 로컬 Pod 간 요청 성공률 BGP 라우트 재수렴 3초 이하 (BFD 적용 시 1초 이하); 원격 클러스터 단절 시 로컬 트래픽 영향 0% P2 (Medium) 노이즈 네이버 Spark 대용량 셔플·분산 쿼리와 GPU 통신 간 네트워크 경합 완화 Cilium Bandwidth Manager 활성화; Linux 커널 FQ(Fair Queueing) 스케줄러 적용 백그라운드 셔플 중 vLLM 토큰 생성 속도 저하율; 네트워크 패킷 지연 편차(Jitter) 대역폭 포화 상태에서도 GPU 외부 통신 Throughput 저하 15% 이내 방어 P2 (Medium) 보안/관측성 CiliumNetworkPolicy 적용 및 Hubble 기반 클러스터 간 트래픽 플로우 추적 Hubble Relay 및 UI/CLI 구동; Workload Identity(Namespace/SA) 기반 정책 배포 eBPF 맵 엔트리 크기 및 메모리 점유율; 정책 파싱으로 인한 데이터 플레인 레이턴시 정책 적용 전후 RTT 차이 오차범위(0.05ms 이하) 유지; 비인가 접근 즉각 DROP 및 감사 로그 적재 전체적으로 매우 좋은 구성 입니다. 특히 “bare-metal 테스트를 K8s에서 반복한다”와 “K8s/ClusterMesh에서만 생기는 신규 failure mode를 별도로 검증한다”를 분리한 것이 맞습니다. 다만 실무 관점에서는 몇 군데 중요한 수정/보강이 필요합니다. 특히 B의 ClusterMesh 경로 , E의 Global Service , C의 GPU topology , 그리고 I의 네트워크 경합 은 조금 더 정확하게 정의하는 것이 좋습니다. 제가 보면 현재 문서는 약 85~90% 완성된 상태 이고, 아래처럼 다듬으면 실제 K8s validation plan으로 바로 사용할 수 있습니다. 1. 전체 평가 영역 평가 수정 필요성 A. NCCL 재검증 ★★★★★ 거의 그대로 사용 가능 B. ClusterMesh ★★★★☆ Native routing/ClusterMesh 경로 표현 보강 C. GPU topology ★★★★☆ Topology Manager에 대한 표현 수정 D. 실제 workload ★★★★☆ 일부 workload 특성 재검토 E. AIStor ★★★★☆ Global Service와 AIStor 고정 IP를 분리해서 정의 F. Resilience ★★★★★ 매우 중요 G. Observability ★★★★☆ federation보다 correlation이 더 중요 H. Security ★★★★★ 반드시 포함 I. Resource contention ★★★★★ 매우 중요한 신규 항목 그리고 하나 더 추가하고 싶습니다. J. Kubernetes scheduling / lifecycle / CPU-memory overhead 현재 문서에는 GPU와 network는 잘 들어가 있지만, K8s가 GPU inference 성능에 추가하는 CPU, memory, IRQ, cgroup, NUMA, eviction 등의 영향 이 별도 항목으로 빠져 있습니다. 2. A — NCCL 재검증: 거의 정확함 이 부분은 그대로 가져가도 됩니다. 다만 문장을 하나 수정하는 것을 권합니다. 현재: NCCL_SOCKET_IFNAME이 올바른 물리 NIC을 가리키는지 이렇게 쓰면 Pod 내부에서 물리 NIC 이름을 지정해야 한다 는 의미로 오해할 수 있습니다. 일반 Pod에서는 Pod namespace 안에 물리 NIC이 직접 보이지 않고 보통 eth0 가 보입니다. 따라서: NCCL이 의도한 Pod interface/IP 경로를 선택하는지 확인하고, hostNetwork/일반 Pod에서 실제 물리 NIC으로 연결되는 경로를 검증한다. 가 더 정확합니다. 예를 들어: Pod eth0 │ ▼ veth │ ▼ Cilium eBPF │ ▼ Linux routing │ ▼ Intel E810 Native routing은 encapsulation을 하지 않고 Linux routing을 이용합니다. :chatgpt-content-reference{index="0"} 그리고 ClusterMesh에서도 cross-cluster Pod-to-Pod traffic은 별도의 proxy/gateway를 반드시 거치는 구조가 아닙니다. Cilium 문서도 노드 간, 클러스터 간 Pod traffic이 직접 forwarding된다고 설명합니다. :chatgpt-content-reference{index="1"} 따라서 A는: Bare Metal ↓ hostNetwork Pod ↓ Cilium Pod 비교가 매우 좋습니다. 3. B — ClusterMesh: 방향은 맞지만 중요한 수정 여기서 가장 중요한 것은: "ClusterMesh라서 TCP 경로에 추가 오버헤드가 붙는다" 라고 일반화하면 안 된다는 것입니다. 현재 구성: Cilium routing-mode=native BGP ECMP ClusterMesh 이라면: Pod A ↓ Cilium ↓ native routing ↓ Node NIC ↓ network ↓ Node NIC ↓ native routing ↓ Cilium ↓ Pod B 형태가 될 수 있습니다. Native routing은 기본적으로 encapsulation을 하지 않습니다. :chatgpt-content-reference{index="2"} 따라서 B에서는 "ClusterMesh overhead"를 하나의 숫자로 측정하지 말고 경로별로 분리 하는 것이 좋습니다. 제가 추천하는 비교 Test Source → Destination 목적 B1 Host → Host 물리 네트워크
로컬 LLM용 그래픽카드, "성능"이 아니라 VRAM 1GB당 가격 으로 사세요. 10월 1일 한국 실거래가 기준으로 계산했습니다. 왜 GB당 가격인가 LLM 추론에서 카드의 가치는 토큰/초로 갈리지만, "돌아가는 모델의 크기"는 VRAM이 결정합니다. 같은 예산이면 더 많은 VRAM = 더 큰 모델 = 더 좋은 출력. 그래서 예산 대비 모델 크기를 극대화하는 게 목적함수입니다. 2026년 10월 1일, VRAM 1GB당 가격 (한국 실거래) 카드 VRAM 실거래 중앙값 GB당 RTX 3060 12GB 12GB 539,000원 44,900원 RTX 3090 24GB (중고) 24GB 2,075,000원 86,500원 RX 9070 XT 16GB 16GB 1,509,000원 94,300원 RTX 5070 12GB 12GB 1,371,020원 114,300원 RTX 5070 Ti 16GB 16GB 1,991,000원 124,400원 RTX 4090 24GB (중고) 24GB 3,682,000원 153,400원 RTX 5080 16GB 16GB 2,678,510원 167,400원 RTX 5090 32GB 32GB 9,088,000원 284,000원 가격 출처: 다나와 + 검색엔진 실시간 수집 (샘플 2~16건), 중고는 당근/번개장터 시세 반영. 읽는 법 1위 RTX 3060 12GB (44,900원/GB) — 놀랍지 않나요? 신형 대비 성능은 절반이지만, "12GB에 도는 모델" 범위는 거의 동일합니다. 8B~14B 양자화 추론만 한다면 예산의 절반으로 같은 모델을 돌립니다. 앰배르(Ampere) 세대의 저주: 성능은 느려도 VRAM은 그대로. 2위 RTX 3090 중고 (86,500원/GB) — 24GB를 200만원대에. 27B~35B 양자화가 현실적으로 도는 최저 진입점. 146 tok/s 실측 qwen3-coder:30b도 18GB 안에서 돕니다. 단, 소비전력 350W는 전기세 표에서 확인하세요. 5090은 왜 최악인가 — 성능이 나쁜 게 아니라 "GB당"으로 보면 5070 Ti의 2.3배. 32GB가 꼭 필요한 경우(70B 추론, 파인튜닝)가 아니면 VRAM 과잉입니다. 이중 카드 구성 시나리오 3090 중고 2장 = 48GB, 약 415만원. GB당 86,500원 유지하면서 70B급이 들어옵니다. 단일 5090(32GB, 909만원)보다 GB당 3배 싸고 총 VRAM도 많습니다. 단, PCIe 레인/전원/발열 확인 필수. 결론 예산 50만원: 3060 12GB. 8B~14B 입문용으로 충분. 예산 200만원: 3090 중고 24GB. 로컬 LLM 가성비 왕좌, 3년째 유효. 예산 400만원+: 3090 2장(48GB) vs 4090 중고 1장(24GB) — 용량 vs 속도(스루풋 두 배) 취사선택. 전체 실측 데이터와 카드별 모델 적합 판정표: VRAM Fit Matrix
전체 Stage 요약 Stage 핵심 질문 대표 검증 Stage 1 GPU 8장이 정상인가? PCIe, NVLink, HBM, DCGM, NCCL, FP8/FP4, Stress Stage 2 Network/Storage가 충분히 빠르고 안정적인가? NIC, LACP, iperf3, Optical, MinIO/AIStor, NVMe Stage 3a 실제 AI inference가 빠르고 안정적인가? vLLM, TTFT, ITL, Throughput, KV Cache, Concurrency 3a2~3a10 실서비스 환경에서도 최적/안정적인가? Tuning, Mixed Load, Soak, Quantization, Scaling, RAG, MemKV 각 단계 별 세부 테스트 항목 Stage 1 — GPU Hardware & System Health Step 항목 주요 확인 내용 주요 도구/명령 결과/판정 0 Persistence Mode / Fabric Manager GPU Persistence Mode 활성화 및 Fabric Manager 정상 동작 확인 nvidia-smi , systemctl status nvidia-fabricmanager 8-GPU Fabric 정상 1 GPU 인식 / Driver GPU 8장 모두 인식되는지, GPU/Driver/CUDA 버전 호환성 확인 nvidia-smi , nvidia-smi -q GPU 8/8 인식, Driver 정상 2 PCIe Link GPU별 PCIe Bus, Slot, Link Width, Max/Current Gen 확인 nvidia-smi -q , lspci -vv 예상 PCIe Gen/Width와 실제 Link 일치 3 NVLink / GPU Topology GPU 간 NVLink 연결 및 GPU topology 확인 nvidia-smi topo -m , nvidia-smi nvlink -s NVLink 연결 정상, 예상 topology와 일치 4 HBM ECC Corrected / Uncorrected ECC Error 확인 nvidia-smi -q , DCGM 신규 Uncorrected Error 없음 5 XID / Row Remapping GPU XID Error 및 불량 메모리 Row Remapping 상태 확인 dmesg , journalctl , nvidia-smi -q 신규 XID 없음, 이상 Row Remap 없음 6 DCGM Diagnostic GPU, PCIe, Memory, NVLink 등 종합 진단 dcgmi diag 모든 진단 Test PASS 7 NCCL 8-GPU 8 GPU 간 AllReduce/AllGather 등 실제 통신 성능 측정 NCCL Tests 예상 NVLink/NVSwitch 성능 달성 8 FP8 / FP4 Compute Tensor Core 기반 FP8/FP4 연산 성능 및 안정성 검증 CUDA/TensorRT/NCCL 또는 자체 workload GPU 간 성능 편차 및 오류 없음 9 GPU Stress / Power 8 GPU 동시 Full Load에서 전력/온도/안정성 확인 gpu_burn , nvidia-smi Crash/XID 없이 Stress 통과 10 Post-Stress Check Stress 전후 XID, ECC, Temperature, Power 등 비교 nvidia-smi , dmesg , DCGM Stress 후 신규 오류 없음 이 단계의 큰 그림: "GPU 8장이 진짜 정상 제품이고, 제대로 조립·연결됐는가"를 확인. 0. Persistence Mode / Fabric Manager 켜기 GPU는 안 쓰면 저절로 절전모드로 들어가는데, 이걸 꺼서 항상 "깨어있게" 만드는 작업임. Fabric Manager는 8장의 GPU가 서로 통신하는 통로(NVLink, 아래 3번 참고)를 관리해주는 프로그램인데, 이게 안 켜져 있으면 뒤 단계들이 다 이상하게 나옴. 그래서 제일 먼저 확인. 1. GPU가 다 인식되는지, 드라이버가 맞는지 확인 nvidia-smi 라는 명령어로 "지금 이 컴퓨터가 GPU 몇 장을 인식하고 있나"를 확인. 드라이버가 이 최신 GPU를 지원하는 버전인지도 같이 확인. 2. PCIe 슬롯이 제대로 꽂혀있는지 PCIe는 GPU를 메인보드에 꽂는 슬롯/통로임. "Gen5"니 "Gen6" 등이 있고, GPU가 낼 수 있는 최대 속도와 지금 실제로 나오는 속도가 같은지 확인. 다르면 슬롯이 헐겁게 꽂혔거나 설정이 잘못된 것임. 3. NVLink(GPU끼리 직통 연결)가 잘 붙었는지 GPU 8장이 협업할 때는 PCIe보다 훨씬 빠른 전용 직통선(NVLink)으로 서로 대화함. 이게 하나라도 안 붙어서 일반 PCIe로 돌아가면(코드에서 SYS 나 PHB 로 표시) 속도가 확 떨어짐. 8장 전부 NVLink로 붙어있는지, 그 연결선에 통신 오류가 없는지 확인. 4. HBM(GPU 메모리)에 에러가 없는지 HBM은 GPU 전용 초고속 메모리임. 메모리 칩이 미세하게 불량이면 "에러"가 찍히는데, 스스로 고칠 수 있는 작은 에러(Corrected)와 못 고치는 심각한 에러(Uncorrected)가 있음. 후자가 하나라도 있으면 그 GPU는 불량 의심 대상임. 5. XID 에러 / 불량 메모리 자동 우회 확인 XID는 GPU가 "나 방금 이상했어"라고 컴퓨터 로그에 남기는 에러 코드임. Row-Remapping은 GPU가 불량난 메모리 한 줄을 스스로 발견해서 안 쓰는 곳으로 우회시킨 기록인데, 이게 쌓이고 있으면 그 GPU 수명이 걱정되는 신호임. 6. DCGM으로 종합 진단 DCGM은 엔비디아가 공식으로 만든 "건강검진 종합세트" 도구임. PCIe, 메모리, 연산 유닛 등을 한 번에 훑어서 합격/불합격을 판정해 줌. 7. NCCL로 GPU 8장 협업 속도 측정 NCCL은 GPU들이 서로 데이터를 주고받을 때 쓰는 통신 라이브러리임. All-Reduce는 "8장이 각자 계산한 결과를 모아서 합치고 다시 나눠주는" 대표적인 협업 패턴으로, 3번에서 "선이 잘 꽂혔는지"만 봤다면, 여기서는 "실제로 그 선으로 얼마나 빠르게 데이터가 오가는지" 진짜 속도를 확인함. 8. FP8/FP4 연산 부하 테스트 FP8, FP4는 숫자를 얼마나 정밀하게(몇 비트로) 표현하느냐를 뜻함(정밀도를 살짝 낮추는 대신 훨씬 빠르게 계산). B300 GPU는 이 저정밀도 계산에 특화된 전용 회로(텐서코어)가 핵심 강점인데, 앞의 7번까지는 이 회로를 전혀 테스트 안 했어서 따로 부하를 줘봄. 9. 전력/발열 스트레스 테스트 ( gpu_burn ) GPU 8장을 동시에 최대로 굴려서 일부러 뜨겁게, 전력을 많이 먹게 만듬. 이때 죽거나( DIED ) 멈추는 GPU가 있는지, 온도나 전력이 위험 수준까지 가는지 확인. 10. 스트레스 후 재점검 9번 테스트로 GPU를 혹사시킨 "직후"에 5번(XID)이랑 온도 경고를 다시 확인해서, "혹사시켰더니 새로 문제가 생겼는지" 전후 비교를 함. Stage 2 — Network & Storage Step 항목 주요 확인 내용 주요 도구/명령 결과/판정 A0 NVMe Inventory NVMe 개수, 모델, 용량, PCIe 연결 상태 확인 nvme list , lspci 예상 NVMe 전체 인식 A1 NIC / PCIe / NUMA NIC Link Speed, PCIe Link, NUMA 위치 및 GPU와의 affinity 확인 ethtool , lspci , numactl , nvidia-smi topo -m 100Gbps 및 예상 NUMA topology A2 Network Throughput Single/Multi Stream에서 TX/RX throughput 측정 iperf3 Multi-stream에서 목표 50Gbps 수준 달성 A3 Bidirectional Traffic TX/RX 동시 부하에서 throughput 및 packet loss 확인 iperf3 -d 양방향 성능 저하/packet loss 없음 A4 Optical / NIC Health Optical signal, CRC, FEC, RX/TX Error 등 확인 ethtool -m , ethtool -S 신호/에러 정상 B1 MinIO / AIStor Access 외부 Object Storage에 대한 실제 PUT/GET throughput 측정 mc , aws s3 , benchmark tool(warp) Network + Storage 포함 목표 성능 달성 C1 NVMe SMART Baseline 테스트 전 NVMe Health / SMART 정보 수집 nvme smart-log , smartctl Media Error 등 이상 없음 C2 Sequential Write 대용량 연속 Write 성능 및 sustained throughput 확인 fio 초기/지속 성능 모두 기준 충족 C3 Sequential Read 대용량 Sequential Read 성능 측정 fio 모델 Load에 필요한 Read 성능 확보 C4 NVMe SMART Post-Test Disk Stress 이후 SMART/Health 재확인 nvme smart-log , smartctl 테스트 후 신규 오류 없음 D Result Summary GPU/NIC/NVMe/MinIO 전체 결과 종합 Benchmark 결과 수집 검수용 Summary Table 생성 이 단계의 큰 그림: "다른 서버들과 데이터를 주고받는 속도"와 "로컬 하드디스크(NVMe)에 파일을 읽고 쓰는 속도"를 확인. A0. 로컬 NVMe 디스크 목록 확인 NVMe가 몇 개가 꽂혀 있는지 먼저 확인. A1. 네트워크 카드 속도 및 위치 확인 서버가 다른 서버와 통신할 때 쓰는 네트워크 카드(NIC-ConnectX6)가 제 속도(100Gbps)로 붙어있는지 확인. NUMA는 "이 카드가 CPU 몇 번 소켓 옆에 붙어있나"인데, GPU랑 너무 멀리 떨어져 있으면 미세하게 느려질 수 있어서 위치도 같이 확인. A2. 혼자 통신 vs 여럿이 동시에 통신 (단일/멀티 스트림) 기존 Cluster는 네트워크 선이 2개 묶여서(LACP 본딩, 마치 2차선 도로를 하나처럼 쓰는 것) 총 50Gbps를 내는 구조임. 그런데 연결을 1개만 열면 구조상 25Gbps(한 차선)에 묶이고, 여러개를 동시에 열어야 두 차선을 다 써서 50Gbps 가까이 나옴. 이걸 실제로 숫자로 증명하는 단계임. 현재 비대칭 구조여서 GPU Node 쪽은 단순 참고용으로 확인. A3. 양방향 동시 테스트 데이터를 "보내기만" 또는 "받기만"이 아니라 동시에 보내고 받을 때도 속도가 잘 나오는지 확인(실운영에서는 업로드/다운로드가 동시에 일어나는 경우가 많음) A4. 광케이블/신호 품질 확인 네트워크 카드가 광케이블로 연결됐다면, 그 광신호 자체가 깨끗한지(신호 세기, 에러 카운트) 확인. 이건 "속도가 빠른가"와 별개로 "신호 품질이 나빠서 나중에 문제 생길 씨앗이 있는가"를 미리 보는 것임. B1. MinIO AIStor 접속 속도 순수 네트워크 속도(A2)와 달리, 여기는 "실제로 파일을 읽고 쓸 때"의 속도라 디스크 처리 부담까지 포함된 더 현실적인 숫자임 -> 해당 테스트는 개발서버 쪽과 우선 진행 C1~C4. 로컬 NVMe 디스크 심층 테스트 C1 (SMART 상태) : SMART는 디스크가 스스로 보고하는 건강 상태임. 테스트 전에 미리 확인. C2 (200GB 연속 쓰기) : NVMe는 처음엔 엄청 빠르다가(SLC 캐시라는 임시 고속 저장공간 덕분) 용량이 어느 정도 차면 갑자기 느려지는 경우가 많음. 실제로 큰 파일(수백GB짜리 AI 모델)을 저장할 때의 "진짜" 속도를 보려면 크게 넣어 봐야 함. C3 (읽기 테스트) : 모델을 처음 불러올 때(콜드 스타트)의 최대 읽기 속도를 확인. C4 (SMART 재확인) : 이렇게 혹사시킨 다음에 디스크 건강 상태가 나빠졌는지 다시 확인. Stage 3a — vLLM Inference Performance Step 시나리오 주요 확인 내용 주요 측정 지표 목적 3a1 Dense 70B BF16 70B 모델을 BF16으로 서빙하고 1K/32K Context 비교 TTFT, ITL, Input/Output tok/s, GPU Memory Baseline 성능 확보 3a1 Dense 70B FP8 동일 모델을 FP8으로 서빙 TTFT, ITL, tok/s, GPU Memory FP8 성능 향상 및 비용 효과 확인 3a1 Prefix Caching 반복되는 동일 Prefix에 대한 Cache 효과 측정 TTFT, Prefill Time, Cache Hit Prefix Cache 효과 확인 3a1 DeepSeek-R1 초대형 MoE 모델의 8-GPU 서빙 가능 여부 및 성능 확인 TTFT, ITL, tok/s, GPU Memory 대형 모델 실서비스 가능성 확인 3a1 Small 8B 8B 모델로 GPU의 고성능 inference capability 측정 Output tok/s, GPU Utilization, ITL GPU/Engine 상한 성능 확인 3a2 Engine / Parallelism Tuning vLLM 설정 및 TP8 vs TP4×2 등 비교 Throughput, TTFT, ITL, Memory 최적 serving configuration 탐색 3a3 Mixed Context Short/Long Context 요청 혼합 TTFT, ITL, Queue Time 서로 다른 workload 간 간섭 확인 3a4 Soak Test 일정 시간 지속적인 inference workload 수행 Throughput, Latency, Memory, GPU Temp 장시간 안정성 및 성능 저하 확인 3a5 Quantization Quality BF16 vs FP8의 성능뿐 아니라 출력 품질 비교 Accuracy / Quality, TTFT, tok/s 양자화에 따른 품질 영향 확인 3a6 Concurrency / Saturation 동시 요청 수를 단계적으로 증가 Concurrency, TTFT, ITL, tok/s, Queue 최대 처리점 및 Saturation Point 확인 3a7 GPU Scaling TP1/TP2/TP4/TP8 성능 비교 tok/s, tok/s/GPU, TTFT, ITL GPU scaling efficiency 확인 3a8 Prefix Cache A/B 동일 workload를 Cache ON/OFF로 각각 수행 TTFT, Prefill, Cache Hit, tok/s 순수 Prefix Cache 효과 정량화 3a9 Realistic RAG 실제 RAG와 유사하게 Context 길이를 다양화 TTFT, ITL, E2E Latency, tok/s 실제 workload 성능 확인 3a10 MemKV / External KV GPU HBM 외부 KV 저장소 활용 TTFT, ITL, KV transfer, GPU Memory External KV의 효과 및 overhead 확인 이 단계의 큰 그림: "AI 모델을 실제 서비스처럼 돌렸을 때 얼마나 빠르고, 몇 명까지 동시에 버틸 수 있는가"를 확인. vLLM 은 대형 언어모델을 실제로 서비스(질문 받고 답 생성)할 수 있게 해주는 소프트웨어임. 먼저 자주 나오는 용어부터: TTFT (Time To First Token) : 질문을 보낸 후 답변의 "첫 글자"가 나오기까지 걸리는 시간. 사람이 체감하는 "얼마나 빨리 반응하나"에 가장 직결됨. ITL (Inter-Token Latency) : 답변이 시작된 후 한 글자(토큰)씩 나오는 간격. 이게 짧을수록 답변이 술술 나오는 느낌임. 동시성(Concurrency) : 동시에 몇 명이 질문을 던지고 있는가. 양자화(Quantization, BF16/FP8) : 위 Stage1의 8번과 같은 개념 — 계산 정밀도를 낮춰서 속도를 올리는 기법. BF16이 기본(더 정밀), FP8이 더 빠른 대신 살짝 부정확해질 수 있는 방식임. 기본 5개 시나리오 1. Dense70B, BF16, 짧은/긴 질문 70B(700억 개 파라미터, 모델의 "크기") 모델을 기본 정밀도로 돌려서 기준점을 잡음. 짧은 질문(1024토큰 입력)과 긴 질문(32768토큰, 예: 긴 문서 첨부)을 나눠서 보는 이유는, 짧은 질문은 "답변 생성 속도"가 중요하고 긴 질문은 "질문을 읽어들이는 속도(프리필)"가 병목이라 성격이 다르기 때문임. 2. Dense70B, FP8 같은 모델을 양자화(FP8)해서 돌려보고 1번과 비교. "정밀도를 낮췄더니 실제로 얼마나 빨라지나"를 실측. 3. Prefix Caching(같은 앞부분 재사용) 검증 챗봇은 보통 같은 시스템 설명(프롬프트)을 매번 반복해서 받음. Prefix Caching은 "어차피 같은 내용이니 처음 계산한 걸 저장해뒀다가 재사용"하는 기능임. 이걸 켜면 반복되는 긴 문서를 다시 계산 안 해도 되니 훨씬 빨라지는지 확인. 4. DeepSeek-R1 (초대형 모델) 70B보다 훨씬 큰(6710억 개 파라미터) 모델을 실제로 올려서 서빙되는지 확인. B300 GPU 8장이 가진 큰 메모리 용량을 제대로 활용할 수 있는지 증명하는 시나리오임. 5. 소형 8B 모델 아주 작은 모델(80억 개 파라미터)을 돌려서, "메모리가 병목이 아닐 때 이 GPU가 낼 수 있는 최고 속도"를 확인. 큰 모델 테스트만으로는 GPU의 순수 최고 속도를 알기 어렵기 때문임. 심화 검증(3a2~3a10) 기본 5개로는 "일단 돌아간다"까지만 확인되고, 실제로 서비스에 올리기 전에 더 봐야 할 것들로 구성. 단계 무엇을 보는가 왜 필요한가 3a2 서버 설정값(배치 크기 등)을 이것저것 바꿔보고, GPU 8장을 하나로 쓸지(TP8) 4장씩 두 묶음으로 쓸지(TP4x2) 비교 기본값이 최선이 아닐 수 있어서 최적 설정을 찾음 3a3 짧은 질문과 긴 질문이 동시에 섞여 들어올 때 서로 방해하는지 실제 운영에선 항상 섞여서 들어옴 3a4 몇 시간 계속 서비스를 돌려도 점점 느려지지 않는지(Soak, 장시간 부하테스트) 짧은 테스트로는 서서히 나빠지는 문제를 못 잡음 3a5 양자화(FP8)했을 때 답변의 "정확도"가 떨어지는지 지금까지는 속도만 봤지 정답 품질은 한 번도 확인 안 했음 3a6 동시 사용자를 계속 늘렸을 때 어디서 무너지는지(포화점) "몇 명까지 버티는지" 용량 산정에 필수 3a7 GPU 1/2/4/8장으로 나눠 쓸 때 성능이 어떻게 변하는지 GPU를 여러 장 묶을 때 생기는 통신 손실을 정량화 3a8 Prefix Caching을 켰을 때 vs 껐을 때 정확히 비교 3번 시나리오는 "켰을 때"만 봤어서, 진짜 효과를 보려면 꼭 비교 필요 3a9 RAG처럼 길이가 들쭉날쭉한 질문 지금까지 테스트는 길이가 항상 고정값이라 현실과 다름 3a10 내부(NVMe)와 외부 저장장치(MemKV)를 GPU 메모리의 보조 저장공간처럼 쓸 때 이득이 있는지 GPU 메모리가 꽉 찼을 때 다시 계산 안 하고 불러오기만 할 수 있는지 확인 Stage 3a 주요 성능 지표 지표 의미 중요도 TTFT 요청 후 첫 Token까지 걸리는 시간 ★★★★★ ITL Token 간 생성 간격 ★★★★★ Input tok/s Prompt/Prefill 처리 속도 ★★★★☆ Output tok/s Decode 처리 속도 ★★★★★ Total tok/s 전체 Token 처리량 ★★★★★ tok/s/GPU GPU 1장당 처리량 ★★★★★ Concurrency 동시 처리 요청 수 ★★★★☆ Queue Time 요청이 실제 처리되기 전 대기 시간 ★★★★★ KV Cache Utilization KV Cache 사용률 ★★★★★ GPU Memory HBM 사용량 ★★★★★ GPU Utilization GPU 연산 사용률 ★★★★☆ HBM Bandwidth HBM 메모리 대역폭 사용량 ★★★★☆ Power GPU 전력 사용량 ★★★☆☆ Temperature GPU 온도 ★★★☆☆ Error / OOM CUDA/XID/OOM 등 오류 ★★★★★
OpenAI가 DevDay에서 개발자와 사용자를 위한 대규모 신규 기능과 요금제를 대거 공개했습니다. 앱 개발자가 사용자의 구독 자원을 직접 활용할 수 있는 새로운 인증 방식부터 초고속 추론 모델, 상시 작동형 에이전트 서비스가 도입되었으며, 이에 맞서 로컬 환경에서 비용 효율적으로 구동 가능한 오픈소스 모델 생태계도 함께 조명받고 있습니다. The one OpenAI announcement that can actually make you money... OpenAI 신규 서비스 및 요금제 공개 OpenAI는 이번 발표를 통해 개발자의 비즈니스 모델을 바꾸고 모델 라인업을 다변화하는 여러 핵심 기능과 요금제를 제시했습니다. Sign in with ChatGPT : 이번 발표에서 가장 주목받는 기능으로, 개발자가 사용자에게 API 비용을 직접 청구하거나 부담하지 않고도 앱을 빌드할 수 있게 해주는 새로운 인증 방식입니다. 외부 앱에서 이 인증을 사용하면 사용자가 보유한 ChatGPT 의 기존 토큰을 앱 서비스 소비에 직접 소진할 수 있어, 개발자에게 강력한 수익화 도구로 작용합니다. Dots : GPT-6 Astra 모델을 기반으로 동작하는 개인용 AI 에이전트 서비스입니다. 단발성 응답을 넘어 사용자를 위해 24시간 상시 작동하며 복잡한 연속 업무를 수행합니다. Pro 500 플랜 & UltraFast : 월 $500 기반의 엔터프라이즈급 Pro 500 플랜이 신설되었습니다. 이 플랜과 함께 공개된 UltraFast 추론 모드는 초당 300토큰이라는 압도적인 처리 속도를 제공하여 실시간 응답이 필수적인 애플리케이션에 최적화되어 있습니다. GPT-6.1 Sol & Decisions API : GPT-6.1 Sol 은 고성능을 유지하면서도 구동 비용을 대폭 낮춘 효율적 모델입니다. 함께 공개된 Decisions API 는 정형 데이터 분류 및 의도 파악 업무를 전용으로 처리하도록 설계된 특화 API입니다. Fastino Labs의 GLiNER 2.5 / GLiDE (오픈소스 대안) OpenAI가 비공개 상용 모델 기반으로 의도 분류 및 데이터 처리 API 시장을 확장함에 따라, 이에 대응하는 로컬 오픈웨이트 모델 생태계도 빠르게 발전하고 있습니다. Fastino Labs 는 OpenAI의 독점적 API 시스템에 종속되지 않고 자체 서버나 로컬 환경에서 구동할 수 있는 GLiNER 2.5 및 GLiDE 모델을 선보였습니다. 이 모델들은 클라우드 API 호출 비용을 지불하지 않고도 정형 데이터 추출, 엔티티 인식, 의도 분류 작업에서 매우 높은 정확도와 빠른 추론 속도를 발휘합니다. 외부 API 의존도를 낮추고 데이터 보안과 비용 절감을 동시에 달성하려는 개발자들에게 강력한 오픈소스 대안이 됩니다. 정리 OpenAI는 Sign in with ChatGPT 를 통해 개발자가 API 비용 부담 없이 사용자 구독 자원으로 수익을 창출할 수 있는 생태계를 구축하고, UltraFast 및 Dots 로 고성능 에이전트 시장을 선점하려 합니다. 반면 비공개 클라우드 API의 비용과 데이터 종속성에 대응하여, GLiNER 2.5 와 같은 로컬 오픈소스 모델이 정밀 데이터 처리 영역에서 실질적인 대안으로 떠오르며 상용 플랫폼과 오픈소스 생태계 간의 양강 구도가 더욱 뚜렷해지고 있습니다. 다룬 영상 The one OpenAI announcement that can actually make you money... — Fireship · 5분
구글이 약 7개월간의 경량화 모델 집중 기간을 거쳐 최상위 지능을 지향하는 신규 프론티어 모델 Gemini 4 Argon 을 공개했습니다. 이번 발표는 단순한 지능 지수의 상승을 넘어 단일 응답에서 최대 100만 토큰을 출력할 수 있는 압도적인 컨텍스트 창과 15% 수준의 극도로 낮춘 환각률을 특징으로 합니다. Artificial Analysis의 인텔리전스 및 비용 지표를 바탕으로 Argon의 기술적 구조, 비용 효율성, 에이전트 수행 능력의 실무적 트레이드오프를 상세히 분석합니다. Gemini 4 Argon Gemini 4 Argon의 지능 수준과 프론티어 재진입 구글은 Gemini 3.1 Pro 출시 이후 Gemini 3.6, 3.7, 3.8 등 Flash 라인업을 중심으로 고속·경량 추론에 집중해 왔습니다. 하지만 마침내 최상위 프론티어급 모델인 Gemini 4 Argon 을 공개하며 테스트 단계에 들어갔고, OpenAI 및 Anthropic과의 최상위 지능 경쟁에 다시 진입했습니다. Artificial Analysis Intelligence Index 평가에서 Argon은 53점을 기록했습니다. 이는 OpenAI의 GPT-6 Astra (53점)와 정확히 동률을 이루며, GPT-6.1 Sol (52점)을 오차 범위 내에서 앞서는 수치입니다. 구글의 이전 프론티어 모델이었던 Gemini 3.1 Pro Preview (30점)와 비교하면 무려 23점이 상승했습니다. 이로써 프론티어 AI 시장은 구글, OpenAI, Anthropic의 3파전 구도로 재편되었습니다. 모델 Artificial Analysis Intelligence Index 비고 Gemini 4 Argon 53 구글의 신규 프론티어 모델 GPT-6 Astra 53 Argon과 동률 GPT-6.1 Sol 52 Argon 대비 -1점 Gemini 3.1 Pro Preview 30 구글 전작 대비 +23점 상승 Argon은 단순 지능 수치 향상 외에도 긴 맥락을 한 번에 다루는 능력과 복잡한 작업을 끝까지 완수하는 지량형 에이전트 성능에 초점을 맞춰 설계되었습니다. 현재 선택된 일부 사용자들을 대상으로 필드 테스트가 진행 중이며 곧 광범위한 API 서비스로 확장될 예정입니다. 단일 응답 100만 토큰 출력의 구조적 이점 Argon이 가진 가장 강력한 스펙상 차별점은 단일 응답(Single Response) 시 최대 100만(1M) 토큰을 연속으로 생성할 수 있다는 점입니다. 경쟁 모델인 Opus 5.5 , Fable 5.1 , GPT-6 Astra 가 최대 128K 토큰 출력을 지원하고, 구글의 Gemini 3.8 Flash 가 64K 출력을 지원했던 것과 비교하면 약 8배 확장된 생성 한계입니다. [ 기존 프론티어 모델 ] ──► 최대 128K Output Cap (중간 요약/압축으로 맥락 유실 발생) [ Gemini 4 Argon ] ──► 최대 1M Output Cap (단일 패스 유지, 전체 변수 및 맥락 보존) 출력 창이 100만 토큰으로 넓어지면 대규모 작업을 처리할 때 에이전트 컨트롤 하네스(Harness)가 중간에 출력을 끊고 토큰을 요약·압축한 뒤 다시 루프를 돌리는 번거로운 과정이 사라집니다. 전체 코드베이스 재작성이나 대규모 라이브러리 마이그레이션 작업 시, 중간 단절 없이 단일 패스(One pass)로 전체 구조를 일괄 생성할 수 있어 변수명 일관성과 모듈 간 인터페이스 일치도가 대폭 상승합니다. 실제로 구글은 C/C++ 기반의 AV1 비디오 디코더 라이브러리인 libgav1 전체를 Rust 언어로 완전히 재작성하여 실행 성능을 2.7배 향상시키는 고난도 에이전트 작업을 Argon 단일 모델 기반으로 완수했습니다. 다만 이러한 초장문 출력에는 명확한 속도 측면의 트레이드오프가 따릅니다. 현재 Argon의 생성을 초당 100토큰(tok/s)으로 가정한 경우, 최대치인 100만 토큰을 모두 받아내는 데 걸리는 실제 벽시계 시간(Wall-clock time)은 약 3시간에 달합니다. 약 300 tok/s를 출력하는 Gemini 3.8 Flash나 OpenAI의 UltraFast 계열 모델처럼, 향후 대용량 생성 환경에 맞춘 디코딩 최적화 및 속도 향상 작업이 필수 과제로 남아있습니다. 작업당 토큰 효율성, 비용, 환각률 검증 Artificial Analysis의 최근 데이터에 따르면, 단순 인텔리전스 점수보다 작업당 필요한 토큰 수(Tokens per task) 가 실제 운영 지연 시간과 비용을 결정하는 더 핵심적인 지표로 다뤄지고 있습니다. Gemini 3.6 Flash를 비롯한 기존 모델들은 높은 지능에도 불구하고 단일 과제를 해결하기 위해 너무 많은 토큰을 남발하여 비용 효율성이 떨어지는 문제가 있었습니다. Argon은 작업당 토큰 생성 효율성을 최적화하여 이러한 과도한 생성을 제어했습니다. Argon의 API 출시 가격은 100만 토큰을 기준으로 입력 $2(현재 출시 기념 50% 할인 적용 상태, 정가 $4), 출력 $10(정가 $20)로 책정되었습니다. 특히 캐시된 입력(Cached Input)에 대해서는 95%의 파격적인 할인이 적용됩니다. 인텔리전스 인덱스 표준 과제 수행 시 발생하는 작업당 평균 비용을 계산하면 다음과 같습니다. [작업당 비용 비교 (Intelligence Index Task)] - GPT-6 Astra : $3.26 - Gemini 4 Argon: $1.99 (Astra의 60% 수준) - GPT-6.1 Sol : $0.72 Argon의 작업당 비용인 $1.99는 GPT-6 Astra($3.26)와 비교했을 때 60% 수준으로 경제적입니다. 하지만 이 비용 절감은 토큰 생성 개수가 획기적으로 줄어들었기 때문이라기보다는 단가 할인 프로모션의 영향이 더 큽니다. 실제로 동일한 작업을 수행할 때 생성하는 토큰의 개수만 보면 Argon이 GPT-6 Astra보다 약 1.2배 더 많은 토큰을 소비합니다. 한편 극도의 비용 효율을 자랑하는 GPT-6.1 Sol($0.72)에 비해서는 Argon의 작업당 비용이 약 2.7배 높습니다. 신뢰성과 지식 정확도 측면에서는 AA-Omniscience 환각률 평가가 주요 지표로 활용됩니다. Argon은 해당 평가에서 15%라는 매우 낮은 환각률을 보였습니다. GPT-6 Astra(51%)나 GPT-6.1 Sol(54%)이 절반 이상의 오답이나 환각을 생성한 것에 비하면 놀라운 수준입니다. Argon은 지식이 확실하지 않은 질문에 대해 억지로 사실을 지어내기보다 "모른다"고 답변하는 비중(85%)을 대폭 늘리도록 최적화 훈련을 받았습니다. 다만 이로 인해 질문에 맞혀서 얻는 정답률 자체는 다소 제한되어, 종합 지식 점수에서는 Astra와 비슷한 42~43점대에 머물렀습니다. 에이전트 환경 수행 능력은 벤치마크 성격에 따라 엇갈린 결과를 보였습니다. 자동화 및 에이전트 작업 제어 능력을 측정하는 AutomationBench-AA 에서는 77.5%로 전체 1위를 차지했습니다. 반면 실제 터미널 명령어 조작 및 개발 환경 제어 능력을 다루는 Terminal-Bench 4.0 에서는 57%를 기록하여, Sonnet 5.5 (64%)나 Opus 5.5 (60%) 같은 개발 특화 프론티어 모델들에 비해 다소 열세를 나타냈습니다. 정리 Gemini 4 Argon의 등장으로 프론티어 AI 시장은 구글, OpenAI, Anthropic의 균형 잡힌 3파전 구도로 복귀했습니다. Argon의 핵심 경쟁력은 단일 패스로 전체 작업을 완성할 수 있는 100만 토큰 단일 응답 창 과 환각 발생률을 15%까지 낮춘 고신뢰도 응답 성향 입니다. 실무 개발자 및 엔지니어링 팀은 Argon을 도입할 때 다음과 같은 방향으로 시스템을 전환하는 것이 유리합니다. 컨트롤러 구조 단순화 : 기존에는 128K 출력 제한 때문에 코딩 작업을 여러 단계로 나누어 요약하고 다시 전달하는 복잡한 에이전트 루프가 필요했으나, Argon 도입 시 대규모 마이그레이션 및 코드 생성 작업을 단일 요청 프롬프트로 통합할 수 있습니다. 환각 검증 라우터 활용 : 오답을 사실처럼 말하면 안 되는 서비스나 RAG 시스템 전면에 Argon을 배치하면, 잘못된 정보 전달을 막고 "모름"으로 응답을 격리하는 안전장치로 사용할 수 있습니다. 프롬프트 캐싱 극대화 : 캐시 입력에 적용되는 95% 할인율을 활용해 대용량 코드베이스나 문서를 캐싱해 두고, 단일 생성 시 발생하는 토큰 단가 비용 부담을 offset하는 구조를 설계해야 합니다. 다룬 영상 Gemini 4 Argon — Sam Witteveen · 10분
토큰/초만 보는 벤치마크는 오프로드 붕괴를 숨깁니다. TTFT(첫 토큰까지 시간)를 보세요. 왜 이 글을 쓰게 됐나 "16GB면 이 모델 돌아가나요?" 질문에는 정답이 두 개 있습니다. 로딩이 되느냐 — 가중치 파일 크기만 보면 답이 나옵니다. 실제로 쓸 만하느냐 — 컨텍스트 채우는 순간 VRAM을 넘기면 시스템 램으로 오프로드되면서 TTFT가 0.1초 → 25초 이상으로 무너집니다. 대부분의 벤치마크 표는 1번만 보여주고 2번은 숨깁니다. 직접 돌려서 측정했습니다. 실측 환경 Apple M5 Max 128GB (MLX 런타임) 기준 측정 NVIDIA 카드 수치는 메모리 대역폭 기준 스케일 추정치 (방법론은 링크된 페이지에 명시) 2026년 10월 데이터 측정 결과 (일부) 모델 tok/s VRAM TTFT qwen3-coder:30b 146.2 18GB 4.3s gpt-oss:20b 102.9 13GB 14.2s llama3.1:8b 100.8 4.9GB 0.1s qwen3.6:35b-a3b 95.1 23GB 25.5s gemma4:26b MLX 88.1 52GB 0.1s qwen3.5:9b 73.0 6.6GB 0.1s 표에서 놓치면 안 되는 줄: gpt-oss:20b는 13GB라고 적혀 있지만 TTFT가 14.2초 입니다. 8K 컨텍스트 채우면 그 아래로 더 떨어집니다. "지원"과 "사용 가능"은 다른 말이에요. 카드별 판정표 내 카드에 그 모델이 fits / tight / offload인지 즉답 주는 페이지를 만들었습니다: VRAM Fit Matrix — 카드 선택, 모델 선택, 판정 확인. 함께 만든 실측표: 16GB에 도는 로컬 LLM 전부 실측 (EN) 10월 GPU 시세 + VRAM 1GB당 가격 순위 결론 16GB 카드면 컨텍스트 8K 전제에서 13GB 이하 모델, TTFT 1초 미만인 것들을 고르세요. 24GB(3090/4090 중고)가 체감상 가장 경제적입니다 — GB당 가격표 참고. 벤치마크 표 볼 때 tok/s 말고 TTFT/VRAM을 같이 달라고 하세요. 질문/교정 환영합니다. 측정 스크립트와 원시 데이터는 링크된 페이지에 있습니다.