Model Serving
Phục vụ mô hình — Đưa AI vào thực tế
Đội bạn vừa fine-tune xong một LLM 8B cho chatbot chăm sóc khách hàng của một ngân hàng Việt Nam. 500 nhân viên tổng đài sẽ dùng đồng thời. Bước kế tiếp để đưa model vào sản xuất là gì?
Hãy tưởng tượng một chuỗi nhà hàng phở: khách hàng là client, lễ tân là load balancer, từng đầu bếp là một replica mô hình, và bếp chính là GPU. Khi đông khách, lễ tân phân bàn đều cho các đầu bếp. Khi một đầu bếp làm nhiều tô cùng lúc cho tiết kiệm củi, đó là batching. Khi quản lý thấy hàng chờ dài, anh gọi thêm đầu bếp mới — đó là autoscale.
Trong bối cảnh AI hiện đại, mỗi request thường là một chuỗi prompt token cần sinh ra tiếp theo một chuỗi completion token. GPU xử lý nhanh nhưng kích hoạt kernel đắt, cho nên mô hình muốn được gửi batch lớn để khấu hao chi phí khởi động kernel. Đây là lý do batching là trái tim của serving LLM.
Điều chỉnh inference server, chế độ batching, số replica và QPS mục tiêu. Mỗi lần bấm Mô phỏng sẽ bơm một lô request vào hệ thống và cập nhật metrics phía dưới.
Hình minh họa
Inference server
PagedAttention + continuous batching
Batching
Continuous batching: request mới được nhét vào batch đang chạy ngay khi có slot trống. GPU luôn bận — chuẩn cho LLM.
Số replica (2)
QPS mục tiêu (12)
QPS thực đo
0.0
req/s
GPU util TB
0%
trên 2 replica
Hàng chờ
0
req chưa phục vụ
Đã hoàn thành
0
request
Phân phối latency
dựa trên 0 request
P50
0ms
P95
0ms
P99
0ms
Cột xanh — request nhanh. Cột cam — tail. Cột đỏ — outlier (những request 1% xấu nhất — chính chúng quyết định SLA P99).
vLLM
— PagedAttention + continuous batching
Mạnh ở
- Throughput cao nhất cho LLM decoder-only
- PagedAttention cho KV-cache hiệu quả
- OpenAI-compatible API sẵn
Hạn chế
- Tập trung LLM text; ít hỗ trợ vision / tabular
- Khởi động lần đầu tốn thời gian compile CUDA graph
Batching mặc định: Continuous batching (mặc định). Phù hợp nhất cho: LLM online serving quy mô lớn.
Trái tim của một hệ thống serving hiện đại không phải là kiến trúc mạng, mà là chiến lược batching. Continuous batching biến GPU từ một cỗ máy "đợi đầy mới chạy" thành một băng chuyền luôn bận: request cũ vừa xong một iteration thì request mới đã nhảy vào slot trống. Đó là lý do vLLM đạt throughput 5-10×so với serving kiểu "một request — một inference", mà vẫn giữ được P99 latency thấp.
Sản phẩm của bạn cần P99 latency ≤ 300ms với 50 QPS sustained. Bạn đang dùng batching tĩnh 16 với 2 GPU. Bước nào sẽ giúp ngay?
Một replica đang xử lý continuous batching 48 slot, GPU util 95%, P50 140ms, P99 520ms. Autoscale nên làm gì theo QPS rule?
Giải thích
Model serving là quá trình biến một checkpoint đã huấn luyện thành một dịch vụ trực tuyến có SLA rõ ràng về latency, throughput và availability. Khác với batch inference (offline, chạy 1 lần qua dataset), serving phải trả lời từng request trong hàng chục mili giây đến vài giây, 24/7.
Hai chỉ số nền tảng:
Tail latency (P95/P99) thường quan trọng hơn trung bình, vì đúng những request chậm đó mới là nơi người dùng phàn nàn, mất conversion và khiếu nại support.
Các thành phần kiến trúc chuẩn:
- API Gateway: terminate TLS, xác thực JWT / API key, rate-limit theo user/tenant, log request.
- Load Balancer (L4/L7): Envoy, NGINX, AWS ALB — phân phối request, health-check replica, blue/green deploy.
- Inference Server: vLLM, TGI, Triton, Ray Serve — đây là nơi model thực thi, batching, và KV-cache sống.
- Request Queue: đệm khi traffic spike; có thể là queue nội bộ của server hoặc hệ thống ngoài (Redis, Kafka) với fanout đến nhiều replica.
- Autoscaler: HPA/KEDA trên Kubernetes đo QPS hoặc GPU util để thêm / bớt pod.
- Observability: Prometheus + Grafana cho metric, OpenTelemetry cho trace, Loki cho log.
Ba kiểu batching:
- Static batching: chờ đủ N request rồi mới phóng. Throughput ổn định nhưng tail latency tệ vì head-of-line blocking.
- Dynamic batching: gom trong cửa sổ thời gian ngắn (ví dụ 5-20ms). Triton mặc định kiểu này.
- Continuous batching (iteration-level): request mới nhảy vào batch đang decode ngay khi có slot — vLLM là người tiên phong. Đây là chuẩn cho LLM hiện đại.
from vllm import LLM, SamplingParams
from vllm.entrypoints.openai.api_server import run_server
# 1) Khởi tạo engine
# - PagedAttention + continuous batching bật mặc định
# - tensor_parallel_size=2 chia model qua 2 GPU
llm = LLM(
model="meta-llama/Meta-Llama-3-8B-Instruct",
tensor_parallel_size=2,
max_model_len=8192,
gpu_memory_utilization=0.90,
swap_space=8, # GB host memory cho CPU offload
enable_prefix_caching=True,
)
# 2) Offline inference (test nhanh)
prompts = [
"Tóm tắt tin: 'FPT mở mảng AI...'",
"Viết email xin nghỉ phép 2 ngày, giọng văn thân thiện.",
]
sp = SamplingParams(temperature=0.7, max_tokens=256, top_p=0.95)
for out in llm.generate(prompts, sp):
print(out.prompt[:40], "=>", out.outputs[0].text[:80])
# 3) Serving qua OpenAI-compatible API:
# $ python -m vllm.entrypoints.openai.api_server \
# --model meta-llama/Meta-Llama-3-8B-Instruct \
# --host 0.0.0.0 --port 8000 \
# --tensor-parallel-size 2 \
# --max-num-seqs 256 # continuous batch size tối đa
#
# Client dùng openai SDK trỏ vào base_url http://localhost:8000/v1Autoscale dựa trên QPS: trong Kubernetes, KEDA là lựa chọn phổ biến. Cấu hình dưới đây trigger scale-up khi QPS đo qua Prometheus vượt 40 req/s mỗi pod:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: vllm-llama-scaler
namespace: ai-serving
spec:
scaleTargetRef:
name: vllm-llama
minReplicaCount: 1
maxReplicaCount: 8
pollingInterval: 15
cooldownPeriod: 120
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.observability:9090
query: |
sum(rate(vllm_requests_total[1m])) by (deployment)
threshold: "40" # req/s / pod trước khi +1 pod
metricName: vllm_qps
- type: prometheus
metadata:
serverAddress: http://prometheus.observability:9090
query: |
avg(nvidia_gpu_utilization{pod=~"vllm-llama-.*"})
threshold: "75" # % GPU util
metricName: gpu_utilTrong thực tế — stack production Việt Nam: một pattern quen thuộc là Kubernetes (GKE / EKS / FPT Cloud K8s) + vLLM + Envoy + KEDA + Prometheus. Cold-start model 70B mất 60-120 giây nên pre-provision minReplicas = 1 luôn sống, maxReplicas cover peak. Xem thêm tối ưu suy luận để biết cách giảm latency từng request, và container hoá để đóng gói runtime tái lập được.
- Serving biến checkpoint thành dịch vụ online với SLA về latency, throughput, availability — khác hẳn batch inference offline.
- Hai chỉ số nền tảng: latency (thường đo P50/P95/P99) và throughput (req/s). Tail latency quan trọng hơn trung bình.
- Continuous batching là đột phá quan trọng nhất cho LLM serving: GPU luôn bận, không còn head-of-line blocking, throughput 5-10× so với static.
- Kiến trúc chuẩn: Client → API Gateway → Load Balancer → Inference Server (vLLM/TGI/Triton/Ray Serve) → GPU, kèm Queue và Observability.
- Autoscale nên dùng leading indicator (QPS, queue depth, GPU util) chứ không đợi CPU 100%. KEDA + Prometheus là combo phổ biến trên Kubernetes.
- Chọn framework theo bài toán: vLLM cho LLM throughput tối đa; TGI nếu đã có stack Hugging Face; Triton cho đa dạng model; Ray Serve cho pipeline phức tạp.
Kiểm tra hiểu biết
Continuous batching khác static batching ở điểm cốt lõi nào khi serving LLM?