1. Cài đặt KEDA để Scaling dựa trên số lượng Request
Chúng ta cần KEDA để mở rộng pod dựa trên số lượng request đang chờ xử lý (queue length) hoặc throughput, thay vì chỉ dựa trên CPU/GPU như HPA thông thường.
Việc này đảm bảo khi có nhiều request ập vào, hệ thống sẽ tự động thêm pod mới để xử lý hàng đợi, giảm độ trễ cho người dùng.
Thư mục cấu hình: /etc/keda/
Đầu tiên, thêm repository Helm của KEDA và cài đặt phiên bản mới nhất vào namespace keda.
helm repo add keda https://kedacore.github.io/charts && helm repo update && helm install keda keda/keda --namespace keda --create-namespace --set operator.rbac.podSecurityPolicy.enabled=false
Kết quả mong đợi: Namespace keda được tạo, các pod keda-operator, keda-operator-metrics-apiserver và prometheus-adapter chuyển sang trạng thái Running.
Kiểm tra trạng thái các pod:
kubectl get pods -n keda
2. Cấu hình HPA dựa trên độ trễ GPU và CPU
KEDA xử lý các metric từ external sources (như Kafka, Redis), còn HPA (Horizontal Pod Autoscaler) vẫn cần thiết để phản ứng nhanh với tải CPU/GPU ngay lập tức.
Chúng ta sẽ cấu hình HPA để scale dựa trên tỷ lệ sử dụng GPU (thông qua NVIDIA Device Plugin) và CPU.
Đường dẫn file: /etc/k8s-configs/vllm-hpa.yaml
Tạo file cấu hình HPA với các tham số tối ưu cho workload AI inference (thường cần phản ứng nhanh hơn workload web thông thường).
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-inference-hpa
namespace: ai-inference
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-deployment
minReplicas: 2
maxReplicas: 10
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Pods
value: 2
periodSeconds: 15
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: nvidia.com/gpu
target:
type: Utilization
averageUtilization: 80
Áp dụng cấu hình vào cluster:
kubectl apply -f /etc/k8s-configs/vllm-hpa.yaml
Kết quả mong đợi: HPA được tạo, current replicas khớp với số pod hiện có, và cpu cùng gpu metric hiển thị giá trị 0% hoặc giá trị thực tế.
Verify kết quả:
kubectl get hpa vllm-inference-hpa -n ai-inference
3. Triển khai Ingress Controller (Nginx) để cân bằng tải
Để phân phối traffic đều vào các pod vLLM đã scale lên, chúng ta cần một Ingress Controller. Nginx Ingress là lựa chọn ổn định và hiệu năng cao cho các dịch vụ HTTP/HTTPS.
Nó sẽ đóng vai trò là điểm vào duy nhất (Single Entry Point) và cân bằng tải (Load Balancing) sang các backend service.
Đường dẫn file: /etc/k8s-configs/nginx-ingress.yaml
Cài đặt Nginx Ingress Controller với cấu hình tối ưu cho latency thấp (tắt buffering, tăng số connection).
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
proxy-buffering: "off"
proxy-connect-timeout: "60"
proxy-read-timeout: "3600"
proxy-send-timeout: "3600"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/component: controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/component: controller
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/component: controller
spec:
containers:
- name: controller
image: k8s.gcr.io/ingress-nginx/controller:v1.9.0
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
args:
- /nginx-ingress-controller
- --election-id=ingress-nginx-leader
- --controller-class=k8s.io/ingress-nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
resources:
requests:
cpu: 100m
memory: 90Mi
limits:
cpu: 2
memory: 512Mi
Áp dụng cấu hình:
kubectl apply -f /etc/k8s-configs/nginx-ingress.yaml
Kết quả mong đợi: Pod ingress-nginx-controller chạy trong namespace ingress-nginx và Service có địa chỉ IP External (LoadBalancer).
Verify kết quả:
kubectl get pods -n ingress-nginx && kubectl get svc -n ingress-nginx
4. Cấu hình Ingress Rule cho vLLM Service
Bây giờ cần kết nối Ingress Controller với Service vLLM của chúng ta để traffic từ bên ngoài đi vào được.
Chúng ta sẽ cấu hình rule để route domain ai-inference.local (hoặc IP) đến service vllm-service.
Đường dẫn file: /etc/k8s-configs/vllm-ingress.yaml
Tạo file Ingress resource với cấu hình cân bằng tải Round Robin mặc định của Nginx.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vllm-ingress
namespace: ai-inference
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
spec:
ingressClassName: nginx
rules:
- host: ai-inference.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vllm-service
port:
number: 8000
Áp dụng cấu hình:
kubectl apply -f /etc/k8s-configs/vllm-ingress.yaml
Kết quả mong đợi: Ingress được tạo, ADDRESS trỏ đến IP của Nginx LoadBalancer.
Verify kết quả:
kubectl get ingress -n ai-inference
5. Cấu hình Canary Deployment để cập nhật Model không downtime
Khi cập nhật model mới hoặc phiên bản vLLM, ta không thể restart toàn bộ service vì sẽ gây mất dịch vụ (downtime).
Sử dụng chiến lược Canary Deployment: tạo một Deployment mới (canary) với 1 pod, route 10% traffic vào đó, nếu ổn thì tăng dần, nếu lỗi thì rollback tự động.
Ở đây ta giả định sử dụng Argo Rollouts hoặc cơ chế thủ công qua Ingress với weight. Để đơn giản và hiệu quả ngay lập tức, ta dùng Argo Rollouts.
Đầu tiên, cài đặt Argo Rollouts:
kubectl create namespace argo-rollouts && kubectl apply -f https://github.com/argoproj/argo-rollouts/releases/download/v1.6.0/manifests/install.yaml
Đường dẫn file: /etc/k8s-configs/vllm-rollout.yaml
Tạo file Rollout thay thế cho Deployment. Cấu hình 20% traffic vào phiên bản mới (canary) ban đầu.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: vllm-rollout
namespace: ai-inference
spec:
replicas: 4
strategy:
canary:
steps:
- setWeight: 20
- pause: {duration: 5m}
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100
trafficRouting:
nginx:
stableService: vllm-service
canaryService: vllm-canary-service
stableSet: stable
canarySet: canary
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: vllm/vllm:latest-canary
ports:
- containerPort: 8000
resources:
limits:
nvidia.com/gpu: 1
requests:
nvidia.com/gpu: 1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values:
- NVIDIA-A100
- NVIDIA-H100
Để chạy canary, ta cần tạo thêm Service cho canary (vllm-canary-service) tương tự vllm-service nhưng selector trỏ vào label canarySet: canary (Argo Rollouts sẽ tự động quản lý label này khi tạo pod mới).
File service canary: /etc/k8s-configs/vllm-canary-service.yaml
apiVersion: v1
kind: Service
metadata:
name: vllm-canary-service
namespace: ai-inference
labels:
app: vllm
spec:
ports:
- port: 8000
targetPort: 8000
selector:
app: vllm
rollout.argoproj.io/canary: "true"
Áp dụng Rollout và Service:
kubectl apply -f /etc/k8s-configs/vllm-canary-service.yaml && kubectl apply -f /etc/k8s-configs/vllm-rollout.yaml
Kết quả mong đợi: Argo Rollouts tạo các pod, ban đầu 80% traffic đi vào stable, 20% đi vào canary. Sau 5 phút, canary tăng lên 50%, rồi 100%.
Verify kết quả (kiểm tra tiến trình canary):
kubectl get rollout vllm-rollout -n ai-inference -o yaml | grep -A 5 "currentWeights"
6. Xử lý trường hợp OOM (Out of Memory) khi scaling up
Khi scale lên quá nhiều pod, tổng VRAM của cluster có thể bị cạn kiệt, dẫn đến lỗi OOM và Kubernetes sẽ kill các pod liên tục (CrashLoopBackOff).
Cần cấu hình Resource Quota ở namespace và LimitRange để ngăn chặn việc request quá nhiều GPU, đồng thời thiết lập maxReplicas hợp lý trong HPA/KEDA.
Đường dẫn file: /etc/k8s-configs/ai-namespace-limits.yaml
Tạo ResourceQuota giới hạn tổng số GPU và CPU toàn namespace, và LimitRange để đặt giới hạn tối đa cho từng container.
apiVersion: v1
kind: ResourceQuota
metadata:
name: ai-inference-quota
namespace: ai-inference
spec:
hard:
requests.nvidia.com/gpu: "12"
limits.nvidia.com/gpu: "12"
requests.cpu: "40"
limits.cpu: "40"
requests.memory: "250Gi"
limits.memory: "250Gi"
pods: "20"
---
apiVersion: v1
kind: LimitRange
metadata:
name: ai-inference-limits
namespace: ai-inference
spec:
limits:
- default:
memory: 32Gi
cpu: 4
nvidia.com/gpu: 1
defaultRequest:
memory: 16Gi
cpu: 2
nvidia.com/gpu: 1
max:
memory: 64Gi
cpu: 8
nvidia.com/gpu: 1
min:
memory: 8Gi
cpu: 1
nvidia.com/gpu: 1
type: Container
Áp dụng giới hạn:
kubectl apply -f /etc/k8s-configs/ai-namespace-limits.yaml
Kết quả mong đợi: Kubernetes từ chối tạo pod nếu request vượt quá ResourceQuota hoặc LimitRange. Nếu pod bị OOM, nó sẽ bị kill và restart, nhưng không gây sập toàn bộ cluster.
Verify kết quả bằng cách cố gắng tạo pod vượt quá giới hạn (sẽ bị lỗi exceeded quota):
kubectl run test-oom --image=vllm/vllm:latest --replicas=15 --limits="nvidia.com/gpu=1" -n ai-inference
Điều hướng series:
Mục lục: Series: Series: Xây dựng nền tảng AI inference hiệu năng cao với vLLM, TensorRT-LLM và Kubernetes trên Proxmox
« Phần 4: Tối ưu hóa hiệu năng sâu với TensorRT-LLM và custom Docker image
Phần 6: Quản lý dữ liệu và caching model với NVMe và Shared Storage »