Tối ưu hóa I/O với Local NVMe storage cho từng node GPU
Trong kiến trúc AI inference, tốc độ đọc model weights từ đĩa là yếu tố quyết định thời gian khởi động (cold start). NVMe local cung cấp băng thông và IOPS cao nhất, vượt trội so với mạng storage.
Bước 1: Xác định và mount NVMe local vào thư mục đích trên node GPU.
Chúng ta cần tạo điểm mount riêng biệt (ví dụ: /mnt/nvme-models) và đảm bảo quyền truy cập cho người dùng chạy container (thường là root hoặc user với gid cụ thể). Điều này giúp Kubernetes nhận diện storage là local.
Trên mỗi node GPU, thực hiện lệnh sau để tạo thư mục và mount:
sudo mkdir -p /mnt/nvme-models
sudo mount /dev/nvme0n1 /mnt/nvme-models
sudo chown -R 1000:1000 /mnt/nvme-models
Kết quả mong đợi: Thư mục /mnt/nvme-models hiện diện, có quyền truy cập bởi user id 1000 (thường dùng trong container) và dữ liệu được đọc từ NVMe trực tiếp.
Bước 2: Kiểm tra thông số I/O để đảm bảo NVMe hoạt động đúng chuẩn.
Sử dụng fio để benchmark nhanh tốc độ đọc tuần tự (sequential read), mục tiêu đạt trên 2GB/s cho NVMe single channel.
fio --name=read_test --ioengine=libaio --iodepth=64 --rw=read --bs=1M --direct=1 --size=10G --numjobs=4 --runtime=60 --group_reporting --filename=/mnt/nvme-models/test_file
Kết quả mong đợi: Đầu ra hiển thị bandwidth (bw) lớn hơn 1500 MB/s và IOPS cao, xác nhận phần cứng NVMe đã sẵn sàng cho việc tải model nặng.
Cấu hình Persistent Volume (PV) và PVC để lưu trữ model weights
Để Kubernetes quản lý storage local NVMe này một cách an toàn và có thể tái sử dụng, ta cần định nghĩa Persistent Volume (PV) loại HostPath với nodeAffinity.
Bước 1: Tạo file định nghĩa PV trỏ vào thư mục NVMe local.
File này sẽ được áp dụng trên mỗi node GPU. Lưu ý sử dụng `hostPath` với type `DirectoryOrCreate` và thêm label node selector để đảm bảo PV chỉ được bind vào node có GPU và NVMe.
cat > /etc/kubernetes/manifests/nvme-model-pv.yaml
Kết quả mong đợi: PV được tạo thành công với trạng thái "Available", gắn liền với node hiện tại và class storage `nvme-local`.
Bước 2: Tạo Persistent Volume Claim (PVC) để Pod yêu cầu tài nguyên này.
PVC này sẽ được gắn vào Deployment vLLM. Kubernetes sẽ tự động tìm PV có label `storage-type: nvme-local` và thuộc về node đang chạy Pod.
cat > /etc/kubernetes/manifests/model-pvc.yaml
Kết quả mong đợi: PVC chuyển sang trạng thái "Bound" với PV tương ứng, sẵn sàng được mount vào container.
Bước 3: Mount PVC vào container vLLM trong Deployment.
Cấu hình volumeMount trong spec của container để ánh xạ PVC vào thư mục chứa model weights.
cat > /etc/kubernetes/manifests/vllm-deployment-storage.yaml
Kết quả mong đợi: Pod vLLM khởi động, mount được thư mục /mnt/models từ NVMe local. Verify bằng lệnh: `kubectl exec -it -n vllm-system -- ls -la /mnt/models`.
Triển khai Distributed Cache (Redis) cho token cache
Để giảm tải cho GPU khi xử lý các prompt lặp lại hoặc ngữ cảnh dài, ta sử dụng Redis làm cache cho KV Cache (Key-Value Cache) ở mức cluster.
Bước 1: Triển khai Redis Cluster hoặc Single Node với RAM cao.
Sử dụng Redis với persistence type AOF để đảm bảo dữ liệu cache không mất khi restart, nhưng ưu tiên tốc độ ghi RAM.
cat > /etc/kubernetes/manifests/redis-cache.yaml
Kết quả mong đợi: Pod Redis chạy ổn định, Service `redis-cluster` có địa chỉ nội bộ (ClusterIP) để các pod vLLM kết nối.
Bước 2: Cấu hình vLLM để sử dụng Redis làm backend cho Prefix Caching.
Cần điều chỉnh tham số khởi động của vLLM (thông qua Docker args hoặc environment variables) để trỏ vào Redis. Lưu ý: vLLM cần thư viện `redis-py` và cấu hình đúng endpoint.
docker run -d --gpus all \
-e REDIS_HOST=redis-cluster.vllm-system.svc.cluster.local \
-e REDIS_PORT=6379 \
-e ENABLE_PREFIX_CACHING=true \
-v /mnt/models:/mnt/models \
vllm/vllm:latest \
--model /mnt/models/Llama-3-70B \
--tensor-parallel-size 4 \
--enable-prefix-caching \
--gpu-memory-utilization 0.9
Kết quả mong đợi: Log khởi động vLLM hiển thị thông báo "Prefix caching enabled with Redis backend" và các key cache được lưu vào Redis khi có request đầu tiên.
Cấu hình policy để tải model từ object storage (MinIO/S3) vào RAM/GPU
Để quản lý nhiều model mà không tốn NVMe, ta lưu model weights gốc trên Object Storage (MinIO/S3) và chỉ tải về RAM/NVMe khi cần thiết.
Bước 1: Tạo một Job Kubernetes để tải model từ MinIO vào thư mục NVMe local.
Job này sẽ chạy khi cluster khởi động hoặc khi có lệnh trigger, sử dụng `aws-cli` (tương thích S3) để copy model vào /mnt/nvme-models.
cat > /etc/kubernetes/manifests/model-puller-job.yaml
Kết quả mong đợi: Job chạy xong, file model weights xuất hiện trong thư mục /mnt/nvme-models trên node GPU. Verify bằng: `kubectl get jobs -n vllm-system` và kiểm tra log.
Bước 2: Cấu hình chính sách tự động (Automation) thông qua CronJob hoặc Webhook.
Để tự động tải model trước khi Pod vLLM khởi động, ta sử dụng Init Container trong Deployment vLLM để gọi lệnh tải model.
cat > /etc/kubernetes/manifests/vllm-init-container.yaml
Kết quả mong đợi: Khi Pod vLLM được tạo, container chính sẽ chờ Init Container tải xong model từ S3 vào NVMe mới bắt đầu chạy inference.
Giảm thời gian khởi động (cold start) bằng cách pre-warm model
Cold start xảy ra khi vLLM phải tải model từ đĩa vào VRAM và khởi tạo engine. Để giảm thiểu thời gian này, ta sử dụng cơ chế "Pre-warming" bằng cách giữ một Pod vLLM sẵn sàng (Warm Pool).
Bước 1: Sử dụng KEDA hoặc Vertical Pod Autoscaler để duy trì số lượng Pod tối thiểu.
Cấu hình HPA (Horizontal Pod Autoscaler) với `minReplicas: 1` để luôn có ít nhất một instance vLLM chạy sẵn với model đã load vào VRAM.
cat > /etc/kubernetes/manifests/vllm-hpa-warm.yaml
Kết quả mong đợi: Kubernetes luôn giữ 1 Pod vLLM chạy. Khi có traffic tăng, Pod mới sẽ được tạo nhanh hơn vì model đã sẵn sàng trong memory của Pod hiện tại (hoặc Init Container đã chạy sẵn).
Bước 2: Tối ưu hóa Init Container với caching logic thông minh.
Thay vì tải model mỗi lần, ta kiểm tra xem model đã tồn tại trên NVMe chưa. Nếu có, Init Container sẽ chỉ thực hiện checksum verification nhanh, bỏ qua bước download.
docker run --rm -v /mnt/nvme-models:/mnt/models amazon/aws-cli:latest bash -c '
MODEL_PATH="/mnt/models/Llama-3-70B"
if [ -d "$MODEL_PATH" ] && [ -f "$MODEL_PATH/safetensors/index.json" ]; then
echo "Model found in cache, skipping download."
# Optional: Verify integrity
# md5sum $MODEL_PATH/safetensors/index.json
else
echo "Model not found, downloading..."
aws s3 sync s3://my-ai-models/Llama-3-70B $MODEL_PATH --endpoint-url http://minio.minio-system.svc.cluster.local:9000
fi
'
Kết quả mong đợi: Thời gian khởi động Pod giảm đáng kể (từ 3-5 phút xuống còn 30-60 giây) vì bỏ qua quá trình tải dữ liệu nặng nề từ Object Storage khi model đã có sẵn trong cache NVMe.
Bước 3: Verify toàn bộ quy trình Pre-warm.
Sử dụng `kubectl rollout status` để theo dõi thời gian Pod chuyển sang trạng thái `Running`.
time kubectl rollout status deployment/vllm-inference -n vllm-system --timeout=120s
Kết quả mong đợi: Thời gian hiển thị ở đầu dòng lệnh nhỏ hơn 60 giây, xác nhận cơ chế Pre-warm và caching NVMe hoạt động hiệu quả.
Đ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 5: Cấu hình Scaling tự động và cân bằng tải cho dịch vụ AI
Phần 7: Giám sát, Logging và Alerting cho hệ thống AI inference »