SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor

VPS có chạy được Proxmox với nested virtualization không?

Kiểm tra VPS trong 1 phút bằng kvm-ok để biết có flag vmx hoặc svm. Bài viết giải thích lỗi exact khi thiếu flag và khi nào chạy được KVM, Proxmox guest.

Câu trả lời ngắn

Nested virtualization là hypervisor chạy bên trong một máy ảo: VPS của bạn vốn đã là guest, và bạn muốn nó host các guest riêng. Cách này chỉ hoạt động khi hypervisor của nhà cung cấp chủ động expose các phần mở rộng virtualization của CPU cho instance của bạn. Kiểm tra /proc/cpuinfo để tìm flag vmx (Intel) hoặc svm (AMD). Nếu không thấy flag nào, không cấu hình nào bên trong VPS có thể khắc phục được.

Trước hết, cần xác định rõ một điểm: Docker không cần bất kỳ thứ nào trong số này. Container dùng chung kernel của VPS và không bao giờ truy cập /dev/kvm. Nếu mục tiêu thực sự là "chạy nhiều service trong container trên server", bạn đã có đủ những gì cần thiết. Nested virtualization chỉ cần thiết khi bạn muốn chạy một kernel thứ hai, dựng lab Proxmox, chạy guest Windows, Firecracker microVM, Android emulator, một Kubernetes testbed gồm các VM thật, hoặc các CI runner khởi động VM image.

Thực tế đang lồng những gì

Có 3 lớp:

  • L0, hypervisor của nhà cung cấp, chạy trên máy vật lý. Bạn không có quyền truy cập vào lớp này.
  • L1, VPS của bạn. Đối với L0, đây chỉ là một guest.
  • L2, VM bạn muốn chạy bên trong VPS.

Ảo hóa phần cứng trên Intel là VT-x (flag vmx) cùng với EPT; trên AMD là AMD-V / SVM (svm) cùng với RVI/NPT. Hypervisor dùng các instruction này để chuyển CPU vào guest mode và cho phép CPU duyệt đồng thời 2 page table.

Các cơ chế này không được thiết kế để re-entrant, nên việc lồng được mô phỏng: khi L1 thực thi một VMX instruction như VMLAUNCH hoặc VMRESUME, instruction đó trap về L0. L0 duy trì các cấu trúc shadow cho L2 thay mặt L1. Trên CPU có VMCS shadowing, các thao tác VMREAD và VMWRITE của L1 có thể được xử lý mà không cần exit. Điều này loại bỏ phần lớn traffic đó, nhưng không loại bỏ toàn bộ. KVM xử lý việc này tốt, nhưng L0 vẫn phải thực hiện thêm công việc ở mỗi exit. Vì vậy nhà cung cấp phải chủ động bật tính năng này.

Khi L0 dùng KVM, cả 2 điều kiện sau đều phải đúng để L2 được accelerated:

  1. KVM module của L0 được load với nested=1.
  2. L0 cấp cho VPS của bạn một CPU model có flag này: <cpu mode='host-passthrough'/> trong libvirt, cpu: host trong Proxmox, -cpu host trong QEMU raw. CPU model được emulated chung (qemu64, kvm64) sẽ ẩn vmx ngay cả khi nesting đã được bật globally.

Các hypervisor khác có switch riêng. Ví dụ, Hyper-V emulated các virtualization extension cho nested hypervisor bên trong VM của nó, nhưng chỉ trong những VM đã được bật nesting riêng từng VM.

Kiểm tra VPS trong một phút

# 1. Are you in a VM, and under what?
systemd-detect-virt          # kvm, vmware, xen, microsoft, or "none" on metal

# 2. Does the CPU expose the extensions to you?
grep -o -E 'vmx|svm' /proc/cpuinfo | sort -u
lscpu | grep -i -E 'virtual|hypervisor'

# 3. The definitive check
sudo apt update && sudo apt install -y cpu-checker
kvm-ok

# 4. The device node the whole stack depends on
ls -l /dev/kvm

Một instance có thể sử dụng sẽ hiển thị vmx hoặc svm, kvm-ok cho biết KVM acceleration can be used, và /dev/kvm tồn tại với mode root:kvm 660. Nếu flag có nhưng device node không tồn tại, hãy tải module thủ công rồi đọc kernel log:

sudo modprobe kvm_intel     # or kvm_amd
sudo dmesg | tail -n 20

Có một file thường xuyên được trích dẫn nhưng cũng thường bị hiểu sai:

cat /sys/module/kvm_intel/parameters/nested   # Y or N

Bên trong VPS, đó là thiết lập của KVM module của bạn. Nó quyết định guest L2 có thể lồng thêm một cấp L3 hay không. Thiết lập này không cho biết L0 có bật nested virtualization cho bạn hay không; /proc/cpuinfo và kvm-ok mới trả lời câu hỏi đó. Tham số nested là tùy chọn bạn thiết lập trên máy do chính bạn sở hữu:

echo 'options kvm_intel nested=1' | sudo tee /etc/modprobe.d/kvm-nested.conf
sudo modprobe -r kvm_intel && sudo modprobe kvm_intel

Không thể gỡ module khi VM đang chạy, vì vậy hãy tắt các guest trước. Nếu máy đó là Proxmox host, bật nested virtualization trong Proxmox VE 9 hướng dẫn cùng thay đổi trong modprobe.d và thêm thiết lập cpu: host cho từng VM để flag này hiển thị bên trong guest.

Vì sao hầu hết nhà cung cấp VPS tắt tính năng này

  • Di chuyển trực tiếp. Cung cấp cho bạn vmx có nghĩa là để lộ một model CPU có flag này. Guest phụ thuộc vào các tính năng CPU đó không thể di chuyển an toàn sang máy không có CPU hỗ trợ chúng. Nhà cung cấp di chuyển workload của khách hàng để rút node sẽ mất khả năng đó ngay khi bật nested virtualization.
  • Bề mặt tấn công. Các đường xử lý nested VMX/SVM thuộc nhóm code phức tạp nhất trong lớp virtualization của kernel, và lịch sử CVE cũng tương ứng.
  • L0 có thể không phải KVM. Nếu systemd-detect-virt hiển thị vmware, xen hoặc microsoft, các quy tắc nesting do stack đó quyết định, không phải KVM.

Instance của bạn không có flag? Hãy hỏi support (một số nhà cung cấp bật tính năng này theo từng VM), chọn plan có ghi rõ hỗ trợ nesting hoặc chuyển sang máy dedicated. Phần còn lại giả định bạn có root trên một máy hiển thị flag này.

Chạy guest L2 bằng libvirt

sudo apt install -y qemu-system-x86 libvirt-daemon-system virtinst ovmf
sudo systemctl enable --now libvirtd
sudo usermod -aG libvirt,kvm "$USER"   # log out and back in

virt-install \
  --name guest1 \
  --memory 2048 \
  --vcpus 2 \
  --cpu host-passthrough \
  --disk path=/var/lib/libvirt/images/guest1.qcow2,size=20,format=qcow2,bus=virtio \
  --network network=default,model=virtio \
  --os-variant debian13 \
  --location https://deb.debian.org/debian/dists/trixie/main/installer-amd64/ \
  --graphics none \
  --console pty,target_type=serial \
  --extra-args 'console=ttyS0,115200n8'

Không cần phiên đồ họa. Quá trình cài đặt qua serial sẽ chạy một lúc, vì vậy hãy bắt đầu trong một shell persistent: cùng quy trình tmux giúp duy trì các phiên Claude Code trên VPS sẽ giữ console virt-install kết nối ngay cả khi kết nối SSH bị ngắt. Nếu --os-variant debian13 bị từ chối, osinfo-db của bạn cũ hơn release đó; hãy chạy osinfo-query os và chọn một tên hiện có. --cpu host-passthrough chuyển tiếp vmx xuống L2, chỉ cần khi L2 cũng phải thực hiện virtualization. Đảm bảo guest có thể boot an toàn bằng virsh autostart guest1.

Bus virtio trên disk và NIC không phải để trang trí: các thiết bị IDE và e1000 được emulation sẽ trap vào hypervisor thường xuyên hơn nhiều so với các virtio queue. Khi nesting, một exit mà L0 không thể tự xử lý sẽ được chuyển lên L1. Việc xử lý tại đó có thể tiếp tục tạo ra các exit khác.

Mạng: phần mà tutorial thường bỏ qua

VPS của bạn có một IP public và nằm sau một hệ thống lọc các địa chỉ MAC lạ. Có 2 hệ quả.

Đưa guest L2 vào mạng public bằng bridging thường sẽ không hoạt động. Đặt br0 trên NIC public, gán cho guest một MAC riêng, rồi bạn sẽ thấy ARP gửi đi nhưng không có phản hồi nào quay lại. Switch của nhà cung cấp đã drop frame từ MAC mà họ chưa cấp cho bạn. Nếu gặp triệu chứng này, hãy dừng debug bridge. Đây là cơ chế gây ra lỗi.

Hãy dùng mạng NAT. libvirt cung cấp sẵn default: virbr0, 192.168.122.0/24, lease từ dnsmasq và kết nối outbound hoạt động ngay. Với inbound, hãy terminate TLS trên L1 rồi proxy vào trong. Các đường dẫn certificate bên dưới lấy từ cấp certificate Let's Encrypt bằng Certbot trên Nginx:

server {
    listen 443 ssl;
    server_name lab.example.com;

    ssl_certificate     /etc/letsencrypt/live/lab.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/lab.example.com/privkey.pem;

    location / {
        proxy_pass http://192.168.122.50:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Trước tiên, hãy cấp static lease cho guest (virsh net-edit default) để địa chỉ trong proxy_pass không thay đổi.

Không để interface quản trị trên Internet: VNC tại 5900 và web UI của Proxmox tại 8006 nên bind vào loopback, rồi truy cập qua SSH tunnel (ssh -N -L 8006:127.0.0.1:8006 you@your-vps) hoặc qua VPN WireGuard tự host vào VPS. Cách này đặt toàn bộ dải guest 192.168.122.0/24 sau một private hop. Giữ firewall ở mức tối thiểu, sudo ufw allow 22,80,443/tcp, không thêm gì khác. Nếu guest mất kết nối outbound ngay sau khi bạn enable ufw, thủ phạm thường là DEFAULT_FORWARD_POLICY="DROP" trong /etc/default/ufw. Đặt nó thành ACCEPT rồi reload ufw.

Proxmox trên VPS

Proxmox VE 9 sử dụng Debian 13 làm nền tảng, vì vậy có thể cài trên VPS Debian bằng cách thêm repository pve-no-subscription và package proxmox-ve. Hãy lấy các dòng repository và keyring từ tài liệu hiện tại của Proxmox. URL sao chép từ một bài blog cũ có thể làm quá trình cài đặt thất bại. Trước khi dành cả buổi tối xử lý phần networking bên dưới, bạn nên xác định Proxmox có phù hợp để chạy trên hardware thuê hay không. Phần so sánh chi phí và khả năng giữa một máy Proxmox tại nhà và VPS thuê đã tính sẵn phần điện năng và hardware cho câu hỏi này.

Các package không phải phần khó. Proxmox yêu cầu vmbr0 được bridge với một NIC vật lý, dẫn thẳng đến bế tắc do cơ chế lọc MAC nêu trên. Mô hình hoạt động được trên VPS là dùng vmbr0 dạng NAT hoặc routed, không gắn với port vật lý, đặt các guest trong một dải mạng private, rồi dùng rule DNAT hoặc reverse proxy trên host cho các dịch vụ public. Nếu các dịch vụ public chạy dưới dạng container thay vì VM, Traefik đứng trước nhiều app từ một file Docker Compose có thể đảm nhiệm cùng công việc định tuyến và tự động cấp certificate. Trước tiên hãy tạo snapshot /etc/network/interfaces: cấu hình bridge sai có thể khóa quyền truy cập vào một máy mà bạn có thể không có console để truy cập.

Hiệu năng, nói đúng thực tế

Nested chậm hơn ảo hóa một cấp. Cơ chế gây chậm cũng rất cụ thể, không phải xảy ra ở mọi nơi: chi phí nằm ở các lần exit và quá trình dịch địa chỉ, không nằm ở các instruction thông thường. Khi có EPT/NPT, L0 gộp page table của L1 dành cho L2 với page table của chính nó vào một shadow table. Vì vậy, khi TLB hit, tốc độ vẫn do phần cứng xử lý. TLB miss tốn nhiều chi phí hơn: phần cứng phải đồng thời walk page table của guest và shadow table. Mỗi thay đổi mà L1 thực hiện trên page table của chính nó cũng buộc phải tạo exit để L0 đồng bộ lại shadow table. Những thao tác gây tốn kém là mọi thao tác rời guest mode, như I/O, timer interrupt, MMIO và inter-processor interrupt, vì L0 xử lý exit của L2 và có thể chuyển tiếp exit đó ngược qua L1. Tác vụ chỉ dùng CPU với working set nhỏ thường có hiệu năng gần native. Working set lớn trong memory phải trả chi phí cho TLB miss. Các tác vụ chủ yếu dùng syscall, packet và disk I/O sẽ chịu ảnh hưởng rõ của các lớp ảo hóa.

Vì vậy, hãy dùng virtio cho mọi thiết bị. Ngoài ra, file qcow2 của bạn nằm trên một disk mà provider đã ảo hóa từ trước. Hai lớp thin provisioning được xếp chồng lên nhau. Khi cache=none trên guest disk, các block giống nhau sẽ không còn nằm đồng thời trong hai page cache. Không đưa ra benchmark number ở đây. Hãy đo workload của chính bạn trên instance của chính bạn.

Các tình huống lỗi và chuỗi bạn sẽ thấy

INFO: /dev/kvm does not exist / KVM acceleration can NOT be used từ kvm-ok. Module chưa được nạp hoặc flag chưa được expose. Trước tiên hãy kiểm tra /proc/cpuinfo.

kvm: disabled by bios trong dmesg. Trên bare metal, bật tùy chọn VT-x/SVM trong firmware. Trong VPS, điều này có nghĩa L0 không cung cấp các extension cho bạn. Không có lệnh nào chạy trong guest có thể thay đổi việc đó.

modprobe: ERROR: could not insert 'kvm_intel': Operation not supported. CPU mà kernel nhìn thấy không có vmx. Đây cũng là quyết định của L0.

Could not access KVM kernel module: Permission denied. Đây là vấn đề quyền, không phải phần cứng. ls -l /dev/kvm phải hiển thị group kvm và mode 660; hãy thêm tài khoản của bạn vào group đó rồi mở một login shell mới, vì thay đổi group không áp dụng cho session đang chạy.

kvm: Device or resource busy khi QEMU khởi động. Một module hypervisor khác đang giữ CPU: chạy lsmod, tìm vboxdrv hoặc các module VMware bên cạnh kvm_intel, rồi gỡ module bạn không muốn dùng.

/var/run/libvirt/libvirt-sock: No such file or directory từ virsh. Daemon đang dừng: sudo systemctl enable --now libvirtd.

Proxmox: KVM virtualisation configured, but not available. Guest đã bật KVM acceleration trên một host không thể cung cấp tính năng này. Hãy sửa cấu hình nesting, hoặc bỏ chọn tùy chọn đó và chấp nhận emulation.

Android emulator: x86_64 emulation currently requires hardware acceleration! Hãy xử lý /dev/kvm như trên, thường là vấn đề group.

Không có lỗi nào, nhưng mọi thứ chạy cực kỳ chậm. QEMU không có flag accelerator sẽ fallback sang TCG, tức software emulator của nó. Cách này vẫn hoạt động đúng nhưng rất chậm: quá trình boot có thể mất vài phút thay vì vài giây. Truyền rõ -accel kvm để QEMU dừng và báo lỗi thay vì âm thầm emulation.

Guest biến mất giữa lúc đang chạy. Hãy xem dmesg để tìm Out of memory: Killed process ... qemu-system-x86_64. Guest L2 là một process trên L1, nên OOM killer xử lý nó như mọi process khác. RAM của L2 lấy từ allocation cố định của L1, không thể mượn thêm từ host.

Vận hành: backup, nâng cấp và giới hạn

Backup. Sao chép file qcow2 của một guest đang chạy có thể tạo ra image không nhất quán, vì guest vẫn tiếp tục ghi dữ liệu trong lúc bạn đọc file. Bạn có thể chạy virsh shutdown guest1, chờ đến khi virsh domstate guest1 báo shut off (lệnh shutdown có thể trả về trước khi guest dừng hẳn), rồi mới sao chép; hoặc tạo snapshot bên ngoài (virsh snapshot-create-as guest1 snap1 --disk-only --atomic) để chuyển các thao tác ghi sang overlay trong lúc bạn sao chép base đã cố định, sau đó gộp thay đổi trở lại bằng virsh blockcommit guest1 vda --active --pivot. Hãy đưa các bản sao ra ngoài VPS. Snapshot trên cùng một disk không bảo vệ dữ liệu trước sự cố của disk đó.

Nâng cấp. apt full-upgrade cài các module kvm_intel/kvm_amd mới, nhưng kernel đang chạy vẫn dùng các module cũ cho đến khi bạn reboot. Hãy giữ lại kernel trước đó và chạy lại kvm-ok sau mỗi lần thay đổi kernel. Nếu host khởi động lại mà không có vmx, bạn vẫn chỉ cần chọn một boot entry khác để đưa hệ thống hoạt động trở lại.

Giới hạn khi quy mô tăng. Một public IP có nghĩa là mọi service ở L2 đều truy cập thế giới thông qua proxy hoặc rule DNAT trên L1. Live migration không khả dụng. Khi các VM tranh chấp CPU, nested exit path là thành phần chịu ảnh hưởng đầu tiên. Hypervisor chạy nhiều guest cũng là một máy đã phân bổ hết RAM; nested VM không thể dùng overcommit để vượt qua allocation cố định. Khi lab vượt quá giới hạn này, giải pháp không phải là dựng thêm một tầng nested stack cao hơn. Hãy dùng một máy riêng, nơi bạn là L0 và không giới hạn nào trong số này còn áp dụng.

FAQ

Tôi có cần nested virtualization để chạy Docker trên VPS không?

Không. Container dùng chung kernel của VPS và không bao giờ mở /dev/kvm, vì vậy một instance thông thường không có flag vmx hoặc svm vẫn chạy Docker và Docker Compose bình thường. Nested virtualization chỉ cần khi bạn muốn chạy một kernel thứ hai: lab Proxmox, guest Windows, microVM Firecracker, Android emulator hoặc CI runner khởi động VM image.

Làm thế nào để kiểm tra VPS có hỗ trợ nested virtualization hay không?

Chạy grep -o -E 'vmx|svm' /proc/cpuinfo | sort -u, sau đó chạy kvm-ok từ package cpu-checker. Instance có thể sử dụng sẽ in vmx (Intel) hoặc svm (AMD), kvm-ok sẽ báo KVM acceleration can be used, còn /dev/kvm sẽ tồn tại với group kvm và mode 660. Bỏ qua /sys/module/kvm_intel/parameters/nested trong trường hợp này. File đó mô tả KVM module của chính bạn, không phải những gì hypervisor của nhà cung cấp đã expose cho bạn.

Vì sao hầu hết nhà cung cấp VPS tắt nested virtualization?

Expose vmx có nghĩa là cung cấp cho guest một CPU model có flag này. Guest phụ thuộc vào các CPU feature đó không thể live-migrate sang máy có CPU không hỗ trợ chúng. Nhà cung cấp thường drain node bằng cách chuyển VM của khách hàng giữa các máy, nên họ phải từ bỏ khả năng đó. Các code path VMX/SVM lồng nhau cũng có lịch sử CVE dài. Một số host vẫn bật tính năng này cho từng VM theo yêu cầu. Một số khác ghi rõ nested virtualization là một tính năng của gói dịch vụ.

Nested VM của tôi không có network trên public bridge. Vấn đề là gì?

Switch của nhà cung cấp sẽ drop frame từ MAC address mà họ chưa lease cho bạn. Vì vậy, L2 guest được bridge vào public NIC sẽ gửi ARP nhưng không nhận được phản hồi. Dừng việc debug br0. Hãy dùng network NAT default của libvirt (virbr0, 192.168.122.0/24), cấp static lease cho guest, rồi publish dịch vụ public thông qua reverse proxy hoặc DNAT rule ngay trên VPS.

Nested VM chậm hơn bao nhiêu?

Chi phí phát sinh ở VM exit và address translation. Khi EPT/NPT đang hoạt động, instruction và TLB hit bên trong L2 chạy ở tốc độ phần cứng. Tuy nhiên, TLB miss phải duyệt thêm các page table, còn I/O, timer interrupt, MMIO và IPI do L0 xử lý và có thể bị chuyển ngược qua L1. Workload thiên về CPU với working set nhỏ có hiệu năng gần native. Working set lớn nằm trong memory và workload nặng về syscall, packet hoặc disk sẽ chịu ảnh hưởng của mọi lớp. Hãy dùng virtio device ở mọi nơi và cache=none trên guest disk, sau đó đo trên workload thực tế của bạn.