Cách hệ thống Proxmox của Great Lotus cắt một chiếc GeForce GTX 1660 Ti thành nhiều card ảo độc lập — vì sao nó hoạt động được dù NVIDIA không cho phép, những con số nào tin được, và cách dựng lại toàn bộ từ đầu.
Một máy chủ ảo hoá có thể chia CPU và RAM cho hàng chục máy ảo rất dễ dàng. Nhưng card đồ hoạ thì không: theo mặc định, một card chỉ gán được cho đúng một máy ảo. Máy thứ hai muốn dùng thì máy thứ nhất phải tắt.
Với công việc cần GPU — dựng hình, chạy trình duyệt có tăng tốc phần cứng, xử lý ảnh — điều đó nghĩa là mỗi máy ảo phải có một card riêng. Rất tốn kém.
vGPU (virtual GPU) là lời giải: chia một card vật lý thành nhiều card ảo, mỗi máy ảo nhận một phần VRAM riêng và chia sẻ nhân xử lý theo lượt.
| Thành phần | Là gì | Ở đâu |
|---|---|---|
| pve-wp2-02 | Máy chủ ảo hoá Proxmox, Dual Xeon E5-2673 v3 (48 luồng), 94 GB RAM, GTX 1660 Ti | 172.16.20.102 |
| pve-wp2-01 | Máy chủ Proxmox thứ hai, laptop, chỉ có đồ hoạ tích hợp Intel | 172.16.20.101 |
| B43 Lotus Cloud | Bảng điều khiển web kiểu Digital Ocean — tạo máy, gán vGPU, sao lưu | :20430 |
| B44 FastAPI-DLS | Máy chủ cấp phép — thay thế máy chủ license của NVIDIA | :20440 |
| Chia vGPU | Passthrough nguyên card | |
|---|---|---|
| Số máy dùng được | Nhiều (tới 6) | Đúng 1 |
| Hiệu năng mỗi máy | Chia sẻ theo lượt | Trọn vẹn 100% |
| Driver trên máy chủ | Cần driver vgpu-kvm đã vá | Không cần gì |
| Phụ thuộc phiên bản kernel | Rất chặt | Không |
| Cần giấy phép | Có (đã có B44) | Không |
Trang Cấu hình máy chủ của B43 có công tắc chuyển giữa hai chế độ này. Đổi chế độ đòi khởi động lại máy chủ.
NVIDIA cố tình khoá vGPU trên card GeForce để bán card datacenter đắt hơn. Toàn bộ phần cứng cần thiết đã nằm sẵn trong con chip — thứ chặn lại chỉ là một danh sách mã card trong phần mềm. Đó là lý do vá được.
| Cách | Nguyên lý | Đánh giá |
|---|---|---|
| API remoting | Máy ảo gửi lệnh đồ hoạ về máy chủ vẽ hộ | Chậm, hỏng nhiều phần mềm |
| Passthrough | Giao thẳng cả card cho một máy ảo | Nhanh nhất, nhưng chỉ một máy |
| Mediated passthrough (mdev) | Kernel tạo nhiều "card con" ảo từ một card thật | Cái ta dùng |
mdev (mediated device) là cơ chế của nhân Linux cho phép một thiết bị vật lý tự khai báo ra nhiều thiết bị con. Mỗi thiết bị con được gán cho một máy ảo qua VFIO, và máy ảo nhìn thấy nó như một card PCI thật.
Driver nvidia-vgpu-kvm mang theo tệp vgpuConfig.xml liệt kê
đúng 27 mã card được phép — toàn bộ là card datacenter (Tesla, A100, L40…).
Khi khởi động, tiến trình nvidia-vgpud đọc mã card thật rồi đối chiếu:
# Nhật ký khi chưa vá — mã 0x2182 là GTX 1660 Ti
nvidia-vgpud: GPU not supported by vGPU at PCI Id: 0:81:0:0 DevID: 0x10de / 0x2182
Thêm tay mã 0x2182 vào vgpuConfig.xml không giải quyết
được gì — thông báo từ chối y hệt. Phép kiểm nằm trong mã nhị phân của driver,
không đọc từ tệp XML. Đó chính là lý do phải vá nhị phân.
nv-kernel.o_binary để bỏ phép kiểm mã card.LD_PRELOAD, chặn lời gọi ở tầng người dùng.0x2182 (TU116) → 0x1e30 (Quadro RTX 6000).0x1e30 có trong danh sách ⇒ sinh ra 18 hồ sơ vGPU.Vì sao chọn Quadro RTX 6000 làm đích giả trang? Vì nó dùng cùng thế hệ chip Turing.
Bảng ánh xạ nằm trong vgpu_unlock_hooks.c:
/* Turing */
case 0x1e02 ... 0x1ff9:
case 0x2182 ... 0x21d1: /* TU116 — GTX 1660 Ti nằm ở đây */
return 0x1e30; /* Quadro RTX 6000 */
Sau khi vá nhị phân, nvidia-vgpud vẫn báo GPU not supported
(chỉ khác một chi tiết: subsystem đổi từ 0x8841 sang 0x0000).
Phải có cả vgpu_unlock-rs nạp qua LD_PRELOAD,
và khởi động lại máy chủ thì mdev_supported_types mới xuất hiện.
Tên hồ sơ có dạng GRID RTX6000-2Q: số là dung lượng VRAM tính bằng GB,
chữ cái là mục đích sử dụng.
| Dòng | Dùng cho | Đặc điểm |
|---|---|---|
| Q — Máy trạm đồ hoạ | Thiết kế, CAD, dựng phim, CUDA, AI | Đa dụng nhất. Có CUDA, tới 7680×4320 |
| B — Máy để bàn ảo | Văn phòng, duyệt web | Nhiều màn hình, tối đa 5120×2880 |
| A — Phát ứng dụng | Máy chủ ứng dụng dùng chung | Khoá ở 1280×1024, không CUDA |
Nếu phân vân, chọn dòng Q. Nó bao trùm mọi nhu cầu của hai dòng kia.
Driver báo Homogeneous vGPUs: 1 — mọi vGPU trên cùng một card phải có
cùng cỡ khung đệm (framebuffer). Không thể vừa có máy 1 GB vừa có máy 2 GB; đã thử và bị từ chối.
Nhưng ràng buộc chỉ nằm ở cỡ, không ở mã hồ sơ. Đọc sysfs khi một máy đang giữ một lát 2 GB thấy rõ điều đó — ba dòng Q / B / A cùng 2 GB vẫn mở:
/sys/bus/pci/devices/0000:81:00.0/mdev_supported_types/*/available_instances
nvidia-256 (1Q) 0 ← khác cỡ ⇒ khoá
nvidia-257 (2Q) 11 ← cùng cỡ ⇒ còn chỗ
nvidia-436 (2B) 11 ← cùng 2 GB, KHÁC dòng ⇒ vẫn mở
nvidia-438 (2A) 11 ← nt
nvidia-259 (4Q) 0 ← khác cỡ ⇒ khoá
Hệ quả thực tế: phải quyết định trước sẽ chia thành mấy phần, vì đổi cỡ đòi tắt hết máy đang dùng card. Còn đổi dòng (2Q sang 2B) thì không cần.
available_instances bị thổi phồng y hệt max_instance — cả hai cùng
tính theo card 24 GB mà driver tưởng mình đang là. Nhưng vì lệch cùng một lượng,
hiệu của hai số thì đúng:
số máy đang giữ = max_instance(driver) − available(driver) = 12 − 11 = 1
số chỗ còn lại = VRAM thật ÷ framebuffer − số máy đang giữ = 3 − 1 = 2
Nhờ vậy đọc được trạng thái card mà không cần hỏi từng file cấu hình máy ảo.
Vì card đang giả trang thành Quadro RTX 6000 (24 GB), driver lấy luôn bảng thông số của card đó. Kết quả: mọi con số liên quan tới dung lượng đều sai.
| Thông số | Tin được? | Vì sao |
|---|---|---|
framebuffer (VRAM mỗi máy) | ĐÚNG | Máy ảo thật sự nhận đúng chừng đó. Đã kiểm: hồ sơ 2Q → máy khách báo 2048 MiB |
max_resolution, num_heads | ĐÚNG | Driver thật sự áp giới hạn này lên máy khách |
frl_config (giới hạn fps) | ĐÚNG | Bộ giới hạn khung hình thật sự hoạt động |
max_instance | SAI | Tính theo VRAM 24 GB của RTX 6000 |
available_instances | SAI | Cùng lý do — nhưng hiệu max_instance − available thì đúng, vì hai số lệch cùng một lượng. Đó là cách đếm số máy đang giữ chỗ |
Tên GRID RTX6000-* | Gây hiểu nhầm | Chỉ là mặt nạ; phần cứng vẫn là TU116 |
Hồ sơ 2 GB: driver báo max_instance = 12. Thực tế chỉ chạy được 3 máy. Ai tin con số 12 mà lên kế hoạch sẽ phát hiện ra sai lầm ở máy thứ tư — lúc đó nó đơn giản là không khởi động.
Đơn giản hơn tưởng tượng — chia trọn vẹn, không trừ phần dự trữ nào:
số máy tối đa = VRAM thật ÷ framebuffer của hồ sơ
= 6144 MiB ÷ 2048 MiB = 3 máy
Ban đầu người viết đoán phải trừ ~64 MiB cho driver máy chủ (vì nvidia-smi báo
56 MiB lúc rảnh). Thí nghiệm bác bỏ giả thuyết đó: nếu có phần dự trữ thì hồ sơ
2 GB chỉ được 2 máy, nhưng thực tế chạy được 3. Công thức đúng là phép chia trọn vẹn.
| Hồ sơ | VRAM mỗi máy | Driver nói | Thực tế | Kiểm chứng |
|---|---|---|---|---|
| 1Q / 1B / 1A | 1 GB | 24 | 6 | đã đo — máy thứ 7 bị từ chối |
| 2Q / 2B / 2A | 2 GB | 12 | 3 | đã đo — máy thứ 4 bị từ chối |
| 3Q / 3A | 3 GB | 8 | 2 | theo công thức |
| 4Q / 4A | 4 GB | 6 | 1 | theo công thức |
| 6Q / 6A | 6 GB | 4 | 1 | dùng hết sạch VRAM |
| 8Q · 12Q · 24Q | 8–24 GB | 1–3 | 0 | lớn hơn card, không bao giờ chạy |
Bảng điều khiển B43 đã tính lại các con số này (mã: app/vgpu_facts.py)
và ẩn hẳn 6 hồ sơ không dùng được, nên giao diện chỉ hiện những lựa chọn có thật.
Hỏng an toàn: máy ảo thứ tư đơn giản là không khởi động, báo QEMU exited with code 1.
Máy chủ và các máy đang chạy không hề bị ảnh hưởng.
Máy pve-wp2-01 dùng đồ hoạ tích hợp Intel với công nghệ GVT-g,
hoàn toàn khác. Điểm quan trọng: với GVT-g, available_instances đọc thẳng từ sysfs
của kernel nên đáng tin tuyệt đối — ngược hẳn với NVIDIA.
Không phải số nào cũng chia được. Ràng buộc thật chỉ có hai, và cả hai đều đến từ bảng hồ sơ của driver chứ không phải từ số học:
| Card | Các cách chia có thật | Số lựa chọn |
|---|---|---|
| 6 GB (GTX 1660 Ti — đang dùng) | 1×6 · 2×3 · 3×2 · 6×1 | 4 |
| 8 GB | 1×8 · 2×4 · 4×2 · 8×1 | 4 |
| 12 GB | 1×12 · 2×6 · 3×4 · 4×3 · 6×2 · 12×1 | 6 |
| 16 GB | 1×16 · 2×8 · 4×4 · 8×2 · 16×1 | 5 |
Bảng điều khiển sinh danh sách này tại chỗ (vgpu_profile.splits_for_vram())
thay cho hằng số viết cứng cho card 6 GB của bản đầu — cắm card khác vào là bốn lựa chọn cũ
đều sai mà không có gì báo.
Đây là chỗ trực giác đánh lừa mạnh nhất. Người ta mặc định "mỗi card cấu hình riêng được". Không phải. Tệp ghi đè hồ sơ khoá theo mã hồ sơ, phạm vi toàn máy chủ:
# /etc/vgpu_unlock/profile_override.toml
[profile.nvidia-256] # ← không có chỗ nào ghi "card nào"
framebuffer = 0x3A000000
Hai card CÙNG ĐỜI trên một máy chủ buộc phải chia giống hệt nhau — chúng dùng chung bộ mã mdev, nên một dòng ghi đè áp cho cả hai.
Muốn hai cách chia khác nhau thì phải là hai đời card khác nhau (ví dụ một card 8 GB và một card 12 GB): mỗi đời có bộ mã mdev riêng nên ghi đè độc lập được.
nvidia-smi in miền PCI thành 8 chữ số, còn sysfs và Proxmox dùng
4. Không chuẩn hoá thì phép ghép card ↔ hồ sơ không bao giờ khớp, và phần mềm
lặng lẽ kết luận card "không có hồ sơ nào":
nvidia-smi : 00000000:81:00.0
sysfs : 0000:81:00.0 ← dạng chuẩn
| Yêu cầu | Vì sao |
|---|---|
| Card NVIDIA Maxwell trở lên | vgpu_unlock chỉ hỗ trợ Maxwell, Pascal, Turing, Ampere |
| IOMMU bật trong BIOS (VT-d / AMD-Vi) | Không có thì VFIO không hoạt động |
| Kernel phù hợp với driver | Đây là rào cản hay gặp nhất — xem bước 1 |
| Bộ driver vgpu-kvm có bản vá | Phải tải từ NVIDIA Licensing Portal |
| Máy chủ cấp phép | Máy khách hết hạn giấy phép sẽ bị giảm hiệu năng |
Đây là chỗ dễ mất nhiều giờ nhất. Driver NVIDIA biên dịch một module vào nhân Linux, và nhân thay đổi API liên tục. Kiểm tra bằng cách biên dịch thử, không cài gì cả:
sh NVIDIA-Linux-x86_64-<ver>-vgpu-kvm.run --extract-only --target /root/nv
cd /root/nv/kernel && make -j$(nproc) SYSSRC=/lib/modules/$(uname -r)/build modules
Thất bại thì cài kernel cũ hơn và ghim lại. Trên hệ thống này: kernel 7.0 hỏng, kernel 6.14 chạy.
# Bật IOMMU trong tham số khởi động
intel_iommu=on iommu=pt # hoặc amd_iommu=on
# Chặn driver nguồn mở, nếu không nó chiếm mất card
echo -e "blacklist nouveau\nblacklist nvidiafb\noptions nouveau modeset=0" \
> /etc/modprobe.d/blacklist-nouveau.conf
update-initramfs -u && reboot
Lấy từ NVIDIA Licensing Portal (đăng ký dùng thử miễn phí; nên dùng email tên miền công ty, email gmail hay bị xét duyệt tay).
sh <file>.run --check # PHẢI ra "check sums and md5 sums are ok"
Kích thước tệp là bội số chẵn của 256 KiB. Đã gặp thật: cả 4 tệp của một bộ driver đều tải dở, và tất cả đều có kích thước tròn trịa như vậy.
Bản vá là diff nhị phân. patch phiên bản 2.8 (Debian 13) từ chối dữ liệu
chứa byte NUL. Phải lấy bản 2.7.6:
curl -fL http://deb.debian.org/debian/pool/main/p/patch/patch_2.7.6-7_amd64.deb -o p.deb
dpkg-deb -x p.deb /root/p276 && mkdir -p /root/bin
cp /root/p276/usr/bin/patch /root/bin/patch # KHÔNG cài đè hệ thống
PATH=/root/bin:$PATH ./driver.run --apply-patch <ver>.patch
# → sinh ra driver-custom.run
Bản vá lấy ở polloloco/vgpu-proxmox. Mỗi bản vá chỉ dùng cho đúng một phiên bản driver — sai phiên bản sẽ báo hunks FAILED.
./driver-custom.run --dkms -m=kernel
--dkms để driver tự biên dịch lại khi kernel cập nhật.
cd /opt && git clone https://github.com/mbilker/vgpu_unlock-rs.git
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal
source $HOME/.cargo/env && cd vgpu_unlock-rs && cargo build --release
mkdir -p /etc/vgpu_unlock /etc/systemd/system/nvidia-vgpud.service.d \
/etc/systemd/system/nvidia-vgpu-mgr.service.d
touch /etc/vgpu_unlock/profile_override.toml
for s in nvidia-vgpud nvidia-vgpu-mgr; do
printf '[Service]\nEnvironment=LD_PRELOAD=/opt/vgpu_unlock-rs/target/release/libvgpu_unlock_rs.so\n' \
> /etc/systemd/system/$s.service.d/vgpu_unlock.conf
done
systemctl daemon-reload && reboot # reboot là BẮT BUỘC
ls /sys/bus/pci/devices/0000:<slot>/mdev_supported_types/ # phải ra danh sách
qm set <VMID> --hostpci0 0000:<slot>,mdev=nvidia-257 # 257 = hồ sơ 2 GB
Trong máy khách: cài driver -grid.run cùng bộ, rồi trỏ giấy phép về máy chủ DLS.
Từ 2026-08-29, bảng điều khiển Lotus Cloud (B43) làm hộ toàn bộ phần "trong máy khách" bằng một nút: tab Cài đặt của máy ảo → Cài driver vGPU vào máy khách. Nó cài mã nguồn nhân, tải đúng phiên bản driver khách khớp nhánh với máy chủ, cài, lấy token cấp phép rồi kiểm tra lại — mất 3–8 phút, có hiện tiến độ.
Lệnh đi qua QEMU Guest Agent chứ không phải SSH, nên không cần mật khẩu hay khoá của
máy khách. Điều kiện: máy đang chạy, đã gắn vGPU, và đang chạy dịch vụ
qemu-guest-agent bên trong.
Ba chỗ vấp khi tự làm tay, nay đã xử lý sẵn:
linux-headers mà gói .deb của NVIDIA
khai Depends. Header đúng phiên bản vẫn cài rồi nhưng apt không nối được ⇒
không sửa được bằng cài thêm gói, phải lùi sang bản .run..run chết bằng SIGBUS vì giải nén vào /tmp,
mà ảnh cloud để /tmp là tmpfs 2 GB trong RAM ⇒ --tmpdir=/var/tmp.qemu-guest-agent; bật --agent enabled=1
mới chỉ tạo cổng phía máy chủ, chưa có gì ở đầu bên kia.Máy khách không có giấy phép sẽ bị NVIDIA giảm hiệu năng sau khoảng 20 phút. FastAPI-DLS là bản thay thế mã nguồn mở cho máy chủ cấp phép.
# Trên máy khách
mkdir -p /etc/nvidia/ClientConfigToken
curl -sk https://<dls>:<port>/-/client-token \
-o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date +%d-%m-%Y).tok
sed -i 's/^FeatureType=.*/FeatureType=1/' /etc/nvidia/gridd.conf
systemctl restart nvidia-gridd
nvidia-smi -q | grep -i "License Status" # mong đợi: Licensed
Địa chỉ khai trong biến DLS_URL được nhúng thẳng vào token.
Khai IP nội bộ của Docker thì máy ảo không bao giờ với tới được. Phải khai địa chỉ mà
máy ảo gọi được, và chứng chỉ phải liệt kê địa chỉ đó trong SAN.
Phần này viết cho tác nhân tự động. Thứ tự dưới đây được sắp xếp để thất bại sớm và rẻ — các phép kiểm tốn ít thời gian nhất nhưng loại bỏ nhiều khả năng sai nhất được đặt lên trước.
Đo trước khi kết luận. Trong quá trình dựng hệ thống này, hai giả thuyết hợp lý đã bị thực nghiệm bác bỏ: (1) phải trừ VRAM dự trữ cho máy chủ — sai, không trừ gì; (2) bản vá lỗi vì sai phiên bản driver — sai, lỗi vì sai phiên bản công cụ patch. Cả hai đều sẽ dẫn tới kết luận sai nếu chỉ suy luận mà không chạy thử.
lspci -nn | grep -i -E "vga|3d" # lấy mã card, vd 10de:2182
uname -r; pveversion # phiên bản kernel
cat /proc/cmdline | grep -o "iommu[^ ]*" # IOMMU đã bật chưa
dpkg -l | grep -E "proxmox-headers" # có header để biên dịch không
qm list # máy nào đang chạy (cần trước khi reboot)
pvecm status 2>/dev/null || echo standalone # có cluster không
vgpuConfig.xml. Không có ⇒ bắt buộc phải vá..patch trong repo. Không có ⇒ dừng lại và đổi phiên bản driver,
đừng cố vá bằng bản vá của phiên bản khác.make modules như ở
tab trước. Hỏng ⇒ hạ kernel trước, mọi việc khác vô nghĩa.Trước khi khởi động lại, phải xác minh driver mạng của kernel mới có tồn tại. Mất driver mạng là mất máy, phải tới tận nơi.
apt-get install -y proxmox-kernel-6.14
# BẮT BUỘC kiểm: driver mạng hiện tại có trong kernel mới không
for i in $(ls /sys/class/net | grep -v lo); do
drv=$(basename $(readlink /sys/class/net/$i/device/driver))
find /lib/modules/<kernel-moi> -name "$drv.ko*" | head -1
done
# cũng kiểm: zfs.ko nếu dùng ZFS, và dkms status
Ghim kernel bằng GRUB_DEFAULT tường minh rồi xác minh trong
grub.cfg trước khi reboot:
SUB=$(grep -m1 -o "gnulinux-advanced-[^']*" /boot/grub/grub.cfg)
ENT=$(grep -m1 -o "gnulinux-<ver>-advanced-[^']*" /boot/grub/grub.cfg)
sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\"$SUB>$ENT\"|" /etc/default/grub
update-grub
grep -n "set default" /boot/grub/grub.cfg # dòng THỨ HAI mới là thật
GRUB không ghi được vào grubenv trên LVM, nên cờ "khởi động một lần"
không tự xoá — nó lặng lẽ trở thành mặc định vĩnh viễn. Dùng
GRUB_DEFAULT tường minh, kiểm chứng được.
| Sau bước | Lệnh kiểm | Mong đợi |
|---|---|---|
| Cài driver | nvidia-smi | hiện tên card thật |
| Cài unlock + reboot | ls .../mdev_supported_types/ | wc -l | > 0 |
| Gán vGPU | nvidia-smi vgpu | liệt kê máy ảo |
| Driver máy khách | nvidia-smi trong máy ảo | tên hồ sơ + đúng VRAM |
| Cấp phép | nvidia-smi -q | grep License | Licensed |
| Bền vững | reboot rồi kiểm lại tất cả | mọi thứ tự lên lại |
Bước cuối không được bỏ qua: một thiết lập không sống sót qua khởi động lại thì chưa xong.
| Triệu chứng | Nguyên nhân thật |
|---|---|
patch line N contains NUL byte | Sai phiên bản công cụ patch, không phải sai driver |
N out of N hunks FAILED | Bản vá không khớp phiên bản driver |
GPU not supported by vGPU sau khi đã vá | Thiếu vgpu_unlock-rs, hoặc chưa reboot |
Biên dịch lỗi in_irq, __vm_flags | Kernel quá mới so với driver |
Trình cài driver chết vì SIGBUS | /tmp là tmpfs bị đầy — thêm --tmpdir |
| Máy ảo thứ N không bật được | Hết VRAM — đếm lại theo công thức thật |
| Máy khách bị giảm hiệu năng sau ~20 phút | Chưa cấp phép |
vGPU của NVIDIA không khoá theo kiểu bật/tắt. Máy khách thiếu giấy phép vẫn khởi động, vẫn thấy card, vẫn dựng hình — trong khoảng 20 phút ân hạn. Hết hạn đó, driver tự hạ hiệu năng xuống mức gần như không dùng nổi và tắt CUDA. Đây là lý do một máy "chạy tốt lúc thử" lại chậm như bò vào hôm sau.
Trạng thái nvidia-smi -q | Nghĩa là gì | Hiệu năng |
|---|---|---|
Licensed (Expiry: …) | Đã xin được giấy phép, còn hạn | đầy đủ |
Unlicensed (Unrestricted) | Chưa có giấy phép nhưng vẫn trong 20 phút ân hạn | đầy đủ, tạm thời |
Unlicensed (Restricted) | Hết ân hạn. NVIDIA đã bóp hiệu năng | bị hạ cấp |
Restricted và Unrestricted trông gần giống nhau và rất dễ đọc lướt,
nhưng đó là ranh giới giữa "chưa sao" và "đã hỏng". Nhìn thấy
Restricted nghĩa là phải đi sửa ngay, không phải chờ thêm.
Máy khách nói chuyện với máy chủ cấp phép tự dựng (FastAPI-DLS, container
lotus_dls) theo đúng bốn bước. Biết chuỗi này thì nhìn nhật ký là biết hỏng ở đâu:
registration_pending: true = máy khách tự từ chối, không phải máy chủ chặnMáy khách Windows xin giấy phép mãi không xong, dừng ngay chặng 1. Mạng thông, DLS chạy, một máy khách khác trên cùng máy chủ DLS lại xin được bình thường.
Giờ hiển thị trên màn hình vẫn ĐÚNG. Đồng hồ góc phải chỉ đúng giờ Việt Nam, lịch đúng, không có gì bất thường để mà nghi. Chỉ có UTC — thứ không ai nhìn — là sai. Không một thông báo lỗi nào nhắc tới thời gian.
# So GIỜ UTC, không so giờ hiển thị — giờ hiển thị luôn đúng
[System.DateTime]::UtcNow
tzutil /g
# Chữa: đặt lại múi giờ rồi ép đồng bộ
tzutil /s "SE Asia Standard Time"
w32tm /resync /force
Có hai máy khách trên cùng một máy chủ, một chạy một không — thì nguyên nhân gần như chắc chắn nằm bên trong máy hỏng, không nằm ở máy chủ hay ở mạng. So hai máy theo từng thuộc tính cho tới khi tìm ra thứ khác nhau.
Trên card đã vgpu_unlock, driver khách 553.74 treo cứng Windows
ngay lúc nạp — không màn hình xanh, không nhật ký, máy đơn giản là đứng. Cứu bằng cách
tắt cứng rồi qm set <vmid> --delete hostpci0, khởi động lại
là vào được để gỡ driver.
Chụp snapshot hoặc sao lưu trước. Một bản driver không hợp với card đã mở khoá sẽ khoá bạn ra khỏi chính máy đó, và đường quay lại duy nhất là gỡ card ra.
Máy khách gắn vGPU dựng hình bằng driver NVIDIA GRID, nên bộ khung hình giả lập của QEMU không còn được vẽ vào nữa. Hệ quả:
screendump) ra một khung đen tuyền.Đường vào đúng là Remote Desktop. Bảng điều khiển B43 phát hiện khung hình đồng màu và hiện thẳng lời giải thích kèm địa chỉ RDP, thay vì để người dùng nhìn một ô đen và tưởng máy đã chết.
Chụp màn hình từ bên trong máy khách bằng GDI (CopyFromScreen) cũng không
cứu được: chạy ở phiên 0 thì báo The handle is invalid; chạy đúng phiên người dùng
qua schtasks /it thì ra một ảnh trắng đều. Muốn có ảnh thật phải
dùng DXGI Desktop Duplication — một tác nhân riêng chạy trong máy khách.
qm template là
một chiều — máy nguồn vĩnh viễn không chạy lại được. Quy trình đúng: nhân bản đầy đủ → gỡ
vGPU khỏi bản sao → mới qm template lên bản sao đó.hostpci0. Card chỉ có ngần ấy chỗ mdev;
bản mẫu mang sẵn vGPU thì máy nhân bản vượt số chỗ sẽ không khởi động nổi. Gắn vGPU sau lúc
nào cũng được — driver đã nằm sẵn trong kho driver của Windows.Mỗi mục dưới đây là một lỗi thật đã gặp trong quá trình dựng hệ thống này, kèm cách nhận ra nó.
Bốn tệp driver đều tải thiếu, nhưng chỉ lộ ra khi giải nén thất bại. Dấu hiệu: kích thước là bội số chẵn của 256 KiB. Tệp nguyên vẹn hầu như luôn có kích thước lẻ.
Phòng tránh: luôn chạy --check ngay sau khi tải, trước khi làm bất cứ việc gì khác.
Cả hai phiên bản driver đều không vá được, thông báo giống nhau. Kết luận tự nhiên là
"bản vá không khớp". Thực ra patch 2.8 của Debian 13 từ chối
byte NUL — kể cả với bản vá hoàn toàn đúng phiên bản.
Cách phân biệt: contains NUL byte = lỗi công cụ.
hunks FAILED = lỗi phiên bản.
Proxmox 9 chạy kernel 7.0. Driver NVIDIA mới nhất vẫn không biên dịch nổi trên đó —
in_irq() đã bị xoá khỏi nhân, vm_area_struct.__vm_flags đổi tên.
Đây là lỗi API, không vá bằng cờ biên dịch được.
Sau khi vá, thông báo từ chối gần như y hệt — chỉ khác một trường phụ đổi từ
0x8841 sang 0x0000. Chi tiết nhỏ đó là bằng chứng bản vá đã
có tác dụng; thứ còn thiếu là lớp vgpu_unlock-rs ở tầng người dùng, và một lần reboot.
Ảnh đĩa đám mây đặt /tmp làm tmpfs 2 GB. Trình cài driver giải nén ~1,5 GB vào đó
rồi chết vì SIGBUS — dấu hiệu kinh điển của ghi tràn tmpfs, không phải lỗi driver.
Cách chữa: --tmpdir=/var/tmp/nv.
Khai DLS_URL bằng IP nội bộ của Docker thì token tải về mang theo địa chỉ đó,
và máy ảo ở mạng khác không bao giờ với tới. Sửa cấu hình xong phải tải lại token —
token cũ vẫn giữ địa chỉ cũ.
Sau khi mở khoá, driver nói gì cũng theo bảng thông số của card mà nó tưởng mình đang là. Bảng điều khiển hiển thị lại nguyên văn sẽ dẫn người dùng lên kế hoạch sai.
Nguyên tắc: con số nào suy ra từ dung lượng card thì phải tính lại; con số nào driver thật sự áp đặt lên máy khách (VRAM mỗi máy, độ phân giải, giới hạn fps) thì tin được.
grub-reboot ghi cờ vào grubenv. Khi /boot nằm trên LVM,
GRUB không ghi lại được lúc khởi động nên cờ đó không bao giờ tự xoá —
nó lặng lẽ trở thành mặc định vĩnh viễn. GRUB có cảnh báo, nhưng rất dễ lướt qua.
hostpci0 thiếu chữ mdev=Gán vGPU bằng chuỗi "0000:81:00.0,nvidia-256" bị Proxmox trả
400 Parameter verification failed — mà câu đó không nói sai ở đâu.
Dạng đúng là "0000:81:00.0,mdev=nvidia-256".
Nơi giấu manh mối: tên khoá hỏng nằm ở trường errors của phản
hồi, không nằm ở message. Đọc mỗi message là vứt đi đúng phần có ích.
Máy nhân bản từ bản mẫu kế thừa machine: pc-q35-9.0+pve1. Node khác không có
phiên bản +pveN đó sẽ từ chối bật máy.
Nơi giấu manh mối: lời gọi API trả về "thành công" — lỗi chỉ nằm trong nhật ký tác vụ của Proxmox. Ai chỉ nhìn mã trả về sẽ thấy một máy "bật thành công" mà không bao giờ chạy.
agent/exec-status của Proxmox trả out-data đã giải mã sẵn,
không còn lớp base64 của QEMU Guest Agent. Giải thêm lần nữa thì
base64.b64decode của Python bỏ qua ký tự không hợp lệ và lặng lẽ trả về
rác nhị phân — trông y như lỗi mã hoá ký tự, dẫn người sửa đi lạc rất xa.
Gần như trường hợp nào thông báo lỗi cũng trỏ sai hướng — và ba cái tệ nhất
(đồng hồ UTC, ghim +pveN, giải mã hai lần) thì không có thông báo lỗi nào cả.
Cách thoát ra luôn giống nhau: dựng một phép thử nhỏ, cô lập, cho kết quả dứt khoát — rồi tin
kết quả đó hơn tin thông báo lỗi.