ข้ามไปที่เนื้อหา

การสำรอง VM

BombVault สำรอง VM แบบ KVM/libvirt โดยไม่เมานต์พาธ libvirt ใด ๆ มันรัน virsh บนโฮสต์ผ่าน SSH (qemu+ssh://) อ่านและเขียนดิสก์ VM ผ่าน mount ของ Host Data (/mnt บนโฮสต์คือ /host/user ในคอนเทนเนอร์) และคัดลอก UEFI NVRAM ผ่าน SSH เพราะไม่มีอะไรของ libvirt ถูก bind-mount BombVault จึงไม่มีทางไปขวาง VM Manager ของ Unraid

คู่มือนี้ครอบคลุมการตั้งค่า วิธีการสำรอง VM ที่มีสแนปช็อต Unraid และ TrueNAS SCALE การสำรองคอนเทนเนอร์ไม่ต้องใช้สิ่งเหล่านี้เลย

การสำรองชุดข้อมูล ZFS ใช้ลิงก์ SSH เดียวกัน ด้วยคีย์ โฮสต์ และผู้ใช้เดียวกัน เมื่อการสำรอง VM เชื่อมต่อได้ รายการชุดข้อมูล ZFS ก็เชื่อมต่อได้ด้วย ดู ชุดข้อมูล ZFS

สิ่งที่คุณตั้งค่า

การตั้งค่า ที่ไหน ค่าเริ่มต้น ความหมาย
Host SSH: Address (LIBVIRT_HOST) ตัวแปรเทมเพลต host.docker.internal โฮสต์ Unraid ที่เข้าถึงผ่าน SSH
Host SSH: Port (LIBVIRT_SSH_PORT) ตัวแปรเทมเพลต 22 พอร์ต SSH ของ Unraid
Host SSH: User (LIBVIRT_SSH_USER) ตัวแปรเทมเพลต root ผู้ใช้ SSH บนโฮสต์
กุญแจสาธารณะ การตั้งค่า, การเชื่อมต่อ, SSH ของโฮสต์ สร้างตอนเริ่มครั้งแรก อนุญาตมันบนโฮสต์

BombVault สร้างคู่คีย์ SSH ของมันตอนเริ่มครั้งแรกที่ /config/ssh/id_ed25519 เก็บใน appdata คีย์ของโฮสต์ถูกปักหมุดใน /config/ssh/known_hosts ตอนเชื่อมต่อครั้งแรก

เทมเพลตกรอก Host SSH: Address ด้วยตัวยึด 192.168.x.x BombVault ถือว่านั่นคือไม่ได้ตั้ง และลอง host.docker.internal ซึ่งใช้ได้บนเครือข่าย bridge เริ่มต้น บนเครือข่ายกำหนดเอง ให้แทนที่ด้วย IP LAN ของโฮสต์ (ขั้นที่ 3 B)

ขั้นที่ 1: เปิด SSH บนโฮสต์ Unraid

  1. ใน Unraid เปิด Settings, Management Access และตั้ง Use SSH เป็น Yes
  2. ถ้าคุณเปลี่ยนพอร์ต SSH ให้จดไว้ Host SSH: Port ต้องตรงกัน

ขั้นที่ 2: อนุญาตกุญแจสาธารณะของ BombVault

/root ของ Unraid อยู่ในหน่วยความจำและถูกล้างทุกครั้งที่บูต คีย์จึงต้องอยู่บนแฟลชไดรฟ์ด้วย รันในเทอร์มินัล Unraid

K=$(docker exec BombVault cat /config/ssh/id_ed25519.pub)
mkdir -p /root/.ssh /boot/config/ssh && chmod 700 /root/.ssh
grep -qxF "$K" /root/.ssh/authorized_keys 2>/dev/null  || echo "$K" >> /root/.ssh/authorized_keys
grep -qxF "$K" /boot/config/ssh/root.pubkeys 2>/dev/null || echo "$K" >> /boot/config/ssh/root.pubkeys
chmod 600 /root/.ssh/authorized_keys

Unraid 6.9 ขึ้นไปคัดลอก /boot/config/ssh/root.pubkeys เข้า authorized_keys ของ root ทุกครั้งที่บูต วิธีนี้จึงอยู่รอดหลังรีบูต และใช้ได้กับการเข้าสู่ระบบ root ใด ๆ ไม่ว่า BombVault ใช้ชื่อโฮสต์หรือพอร์ตอะไร

ขั้นที่ 3: เครือข่าย

BombVault ต้องเปิดการเชื่อมต่อ TCP จากคอนเทนเนอร์ไปยังพอร์ต SSH ของโฮสต์ เลือกกรณีที่ตรงกับการตั้งค่าของคุณ

A. BombVault บนเครือข่าย bridge เริ่มต้น

  • คง Host SSH: Address ไว้ที่ host.docker.internal หรือตัวยึดของเทมเพลต เทมเพลตเพิ่ม --add-host=host.docker.internal:host-gateway ซึ่งชี้ไปที่เกตเวย์ docker0 ซึ่งก็คือโฮสต์
  • ตั้ง Host SSH: Port เป็นพอร์ต SSH ของคุณ

B. BombVault บนเครือข่ายกำหนดเอง

คอนเทนเนอร์บน br0.x macvlan หรือ ipvlan เข้าถึงโฮสต์ผ่าน host.docker.internal ไม่ได้ เพราะ 172.17.0.1 คือ docker0 และเข้าถึงจาก br0.x ไม่ได้ ให้ทำดังนี้แทน

  1. ใน Unraid ตั้ง Settings, Docker, Host access to custom networks เป็น Enabled
  2. ตั้ง Host SSH: Address เป็น IP LAN ของโฮสต์ Unraid คือที่อยู่ที่คุณใช้เปิดหน้าเว็บ
  3. ถ้าเครือข่ายของคอนเทนเนอร์กับโฮสต์อยู่คนละ VLAN ให้อนุญาตเส้นทางที่เราเตอร์หรือไฟร์วอลล์ของคุณ: จาก VLAN ของคอนเทนเนอร์ไปยัง IP ของโฮสต์ที่พอร์ต SSH ผ่าน TCP
  4. ถ้ายังเข้าถึง IP LAN ของโฮสต์ไม่ได้ ให้ใช้ IP shim ของโฮสต์บนซับเน็ตของคอนเทนเนอร์ หาบนโฮสต์ด้วย ip -4 addr show | grep -B2 <container-subnet>

ตรวจว่าโฮสต์ตอบ

รันในเทอร์มินัล Unraid พร้อม IP และพอร์ตของคุณ

docker exec BombVault timeout 6 bash -c 'echo > /dev/tcp/192.168.1.10/22' && echo OPEN || echo UNREACHABLE

OPEN หมายความว่าเส้นทางใช้ได้ UNREACHABLE หมายความว่าต้องแก้เครือข่ายในขั้นที่ 3 ก่อนอย่างอื่น

ขั้นที่ 4: ตั้งตัวแปรและทดสอบ

  1. ใน Unraid เปิด Docker คลิก BombVault เลือก Edit ตั้ง Host SSH: Address (และ Host SSH: Port ถ้าไม่ใช่ 22) แล้วกด Apply ถ้าไม่มีตัวแปร ให้นำเข้าเทมเพลตอีกครั้ง เพราะ Unraid เก็บการกำหนดค่าที่บันทึกไว้ของคอนเทนเนอร์ที่มีอยู่
  2. ใน BombVault เปิด การตั้งค่า, การเชื่อมต่อ, SSH ของโฮสต์ แล้วกด ทดสอบการเชื่อมต่อ ควรเปลี่ยนเป็นสีเขียว

การเรียกเดียวกันจากเทอร์มินัลลิสต์ VM ของคุณ

docker exec BombVault virsh -c "qemu+ssh://root@192.168.1.10:22/system?keyfile=/config/ssh/id_ed25519&known_hosts=/config/ssh/known_hosts&known_hosts_verify=auto" list --all

ขั้นที่ 5: วิธีการต่อ VM

แต่ละ VM มี วิธีการ ในการ์ด VM บนแท็บการตั้งค่าของมัน (เปิด VM ใต้ การสำรองข้อมูล)

  • แบบราบรื่น (ปิดเครื่อง) เป็นค่าเริ่มต้น มันปิด VM สำรองดิสก์ และเริ่มใหม่ การสำรองจึงสอดคล้องกันเสมอ มันรอให้เกสต์ปิดนานถึงสิบนาทีและไม่บังคับปิดเลย เกสต์ที่ยังรันอยู่หลังจากนั้นจะถูกปล่อยไว้ และการสำรองนั้นล้มเหลวพร้อมเหตุผล ถ้าเกสต์ปิดลงภายในหนึ่งชั่วโมงถัดมา BombVault จะเริ่มมันอีกครั้ง ถ้าการเริ่มนั้นล้มเหลว การรันที่ล้มเหลวจะบอกไว้
  • สแนปช็อตขณะทำงาน สำรอง VM ที่กำลังรันผ่านสแนปช็อตภายนอกและ blockcommit ต้องให้ดิสก์อยู่บน /mnt/cache หรือ /mnt/diskX ไม่ใช่ /mnt/user ถ้ามี qemu guest agent ใน VM สแนปช็อตจะหยุดการเขียนชั่วคราว และถ้าไม่มี สแนปช็อตจะอยู่ในสภาพเดียวกับตอนเครื่องดับกะทันหัน (crash-consistent) บน VM ที่ปิดอยู่ แบบขณะทำงานถอยไปเป็นแบบราบรื่นเอง เมื่อทำสแนปช็อตไม่ได้ การสำรองนั้นจะล้มเหลวและ VM รันต่อไป เพราะ VM ที่คุณเลือกให้สำรองขณะทำงานจะไม่ถูกปิดเพื่อการสำรองเลย

สิ่งที่การสำรองเว้นไว้

การสำรองอาจสำเร็จแต่ยังไม่มี VM ครบทั้งหมด การรันจะมีหมายเหตุในโทนคำเตือนที่ระบุสิ่งที่ขาดไป:

  • ดิสก์ที่ BombVault ไม่อ่าน ดิสก์แบบอ่านอย่างเดียว ดิสก์ lun และดิสก์ network หรือ volume จะถูกเว้นไว้ และหมายเหตุจะระบุแต่ละตัวพร้อมอุปกรณ์เป้าหมายและชนิดของมัน คำจำกัดความในข้อมูลสำรองยังลิสต์ดิสก์เหล่านี้ไว้ cdrom และ floppy ไม่ถูกระบุในหมายเหตุ อุปกรณ์บล็อกธรรมดาที่ไม่ใช่ zvol ของ ZFS จะทำให้การสำรองล้มเหลวแทน
  • สถานะของ TPM แบบจำลอง มันไม่มีพาธใน XML ของ domain VM Manager ของ Unraid ตั้ง TPM แบบนี้ให้ VM ที่ใช้ BIOS OVMF TPM หมายเหตุนี้จึงปรากฏบน Unraid ด้วย เกสต์ที่เก็บความลับไว้ใน TPM อย่างที่ BitLocker ทำได้ อาจขอรหัสกู้คืนหลังการกู้คืนที่สถานะนั้นหายไป จึงควรเก็บรหัสนั้นไว้ใกล้มือ

ดิสก์แบบ thin

การกู้คืนเก็บดิสก์แบบ thin ไว้เป็นแบบ thin ไม่ว่าจะแทนที่ดิสก์หรือเขียนดิสก์ใหม่ restic เขียนช่วงที่เป็นศูนย์เป็นรูในไฟล์ใหม่ และ BombVault คืนบล็อกศูนย์ของไฟล์ที่ถูกแทนที่ด้วย fallocate --dig-holes เมื่อเขียนเสร็จ ระหว่างที่ restic แทนที่ไฟล์ ไฟล์นั้นจะใช้พื้นที่เต็มขนาดของมัน หาก pool มีพื้นที่พอ ระบบไฟล์ที่เจาะรูไม่ได้จะเก็บดิสก์ไว้ที่ขนาดเต็ม และบันทึกจะบอกไว้ การตรวจพื้นที่ยังนับดิสก์ใหม่ที่ขนาดเต็มเหมือนเดิม

การกู้คืนที่ล้มเหลว

การกู้คืนทับที่เดิมจะปิด VM เขียนดิสก์ และแทนที่คำจำกัดความกับ NVRAM ต่อเมื่อเขียนดิสก์เสร็จแล้วเท่านั้น การกู้คืนที่ล้มเหลวจะปล่อยให้ VM ยังมีคำจำกัดความอยู่พร้อม NVRAM ของมัน ถ้าดิสก์ไม่ได้เปลี่ยนและ VM รันอยู่ก่อน มันจะรันอีกครั้ง ถ้าดิสก์อาจเปลี่ยนไปแล้ว มันจะคงปิดอยู่ เพราะการบูตดิสก์ที่เขียนไปครึ่งเดียวจะเขียนทับมันอีก และการรันจะบอกไว้ การกู้คืนข้อมูลสำรองเดิมอีกครั้งจะทำงานนี้ให้เสร็จ

สแนปช็อตที่บันทึกไว้ใน libvirt ซึ่งไม่ใช่วิธีที่ VM Manager ของ Unraid สร้าง จะหยุดการกู้คืนทับที่เดิมก่อนแตะ VM เพราะ libvirt ลบ VM ที่มีสแนปช็อตแบบนี้ไม่ได้ ให้ลบสแนปช็อตเหล่านั้นก่อน

เฉพาะบล็อกที่เปลี่ยน

VM ที่มีดิสก์ qcow2 สำรองได้โดยอ่านเฉพาะสิ่งที่เปลี่ยน เปิด เฉพาะบล็อกที่เปลี่ยน ในการ์ดเดียวกัน แล้ว BombVault จะใช้ API สำรองของ libvirt แทนวิธีข้างบน

  • มันเริ่มงานสำรองแบบ pull (virsh backup-begin) ที่สร้าง checkpoint ชื่อ bombvault-<time> ในขณะเดียวกัน และอ่านดิสก์ผ่านลิงก์ SSH ผ่าน Unix socket ที่ส่งต่อ เมื่อ guest agent ตอบ ระบบไฟล์ของเกสต์จะถูกแช่แข็งเฉพาะตอนที่งานเริ่ม การ thaw ที่ล้มเหลวจะลองใหม่ราวสิบห้าวินาที และถ้าเกสต์ยัง thaw ไม่ได้ BombVault ส่งการแจ้งเตือน
  • การรันครั้งแรกอ่านทุกบล็อกที่จัดสรรไว้ การรันครั้งต่อไปอ่านเฉพาะบล็อกที่บิตแมปของ checkpoint ทำเครื่องหมายว่าถูกเขียนนับจากการสำรองครั้งก่อน
  • แต่ละดิสก์ถูกเก็บเป็นโฟลเดอร์ของเซกเมนต์ขนาดคงที่ที่เก็บสิ่งที่เกสต์เห็น เซกเมนต์ที่ไม่เปลี่ยนถูกรับมาจากสแนปช็อตก่อนหน้าโดยไม่ต้องอ่าน ทุกสแนปช็อตจึงสมบูรณ์และกู้คืนได้ในตัวเอง
  • การกู้คืนสร้างดิสก์ใหม่จากเซกเมนต์ของมันและแปลงกลับเป็น qcow2 ด้วย qemu-img ดิสก์มีเนื้อหาเหมือนตอนสำรอง แต่ไฟล์เป็นไฟล์ qcow2 ใหม่ และห่วงโซ่ backing กลับมาแบบแบนราบ
  • การตรวจก่อนการกู้คืนแบบนี้ลิสต์ทุกดิสก์ที่มันสร้างใหม่และนับที่ขนาดเต็มของเกสต์ สองเท่าสำหรับ qcow2 เพราะอิมเมจ raw กับสำเนาที่แปลงอยู่ข้างกันจนกว่าไฟล์เก่าจะถูกแทนที่ นั่นคือมากที่สุดที่ดิสก์ใช้ได้ ดังนั้นพื้นที่ไม่พอตามการนับนี้จะทำให้บรรทัดพื้นที่เป็นสีเทาแทนที่จะบล็อก การตรวจกู้คืนหลังการสำรองครั้งแรก และ ตรวจการกู้คืน ยังตรวจให้แน่ใจว่าสแนปช็อตมีทุกเซกเมนต์ของทุกดิสก์ในขนาดที่ควรเป็น
  • ระหว่างการรัน VM ไม่มี checkpoint ที่บันทึกไว้ใน libvirt BombVault เก็บคำจำกัดความของ checkpoint bombvault- ล่าสุดไว้ใต้ /config/vm-checkpoints และลบเฉพาะบันทึกของ libvirt เกี่ยวกับมัน (virsh checkpoint-delete --metadata) บิตแมปของมันอยู่ในไฟล์ qcow2 และนับการเขียนต่อไป และการรันครั้งถัดไปวาง checkpoint กลับ (virsh checkpoint-create --redefine) ก่อนเริ่ม libvirt ปฏิเสธการ undefine VM ที่มี checkpoint นี่จึงทำให้ Remove VM ของ Unraid และการกู้คืนทำงานตามปกติ
  • งานสำรองที่ BombVault ทิ้งไว้ขณะรันเพราะหยุดกลางการสำรอง จะถูกจบเมื่อ BombVault เริ่มอีกครั้ง พร้อม checkpoint ที่การรันนั้นทิ้งไว้ในบันทึก
  • การปิดสวิตช์ การเอา VM ออกจากตารางเวลา หรือการลบข้อมูลสำรองของมัน ก็จบงานแบบนั้นด้วย และลบบิตแมปด้วยขณะที่ VM รันอยู่ บน VM ที่ปิดอยู่ libvirt ลบไม่ได้ และมันค้างอยู่ในอิมเมจโดยไม่ได้ใช้
  • checkpoint ของเครื่องมืออื่นไม่ถูกแตะ VM ที่มีบางอันกู้คืนไม่ได้จนกว่าจะลบมัน และการกู้คืนหยุดก่อนแตะ VM

สวิตช์ถอยกลับเอง checkpoint ที่ขาดหรือเสีย ดิสก์ที่เปลี่ยนขนาด หรือสแนปช็อตใหม่สุดที่ไม่ใช่แบบเฉพาะบล็อกที่เปลี่ยน หมายความว่าดิสก์ทั้งก้อนถูกอ่านอีกครั้ง VM ที่ปิดอยู่ ดิสก์ raw หรือ zvol ไม่มีลิงก์ SSH ของโฮสต์ libvirt ที่ไม่มี API สำรอง หรือพื้นที่ว่างใต้ /config น้อยเกินไป หมายถึงการสำรองตามปกติด้วยวิธีข้างบน การ์ดบอกว่าการรันครั้งล่าสุดใช้อันไหนและเพราะอะไร

ข้อกำหนด: libvirt ที่มี API สำรอง (ทดสอบกับ libvirt 12.2 และ QEMU 10.2 บน Unraid 7.3) sshd ที่มี AllowStreamLocalForwarding yes (ค่าเริ่มต้น รวมถึงบน Unraid ที่ปิดการส่งต่อ TCP) และดิสก์ qcow2 libvirt ปฏิเสธ checkpoint บนดิสก์ raw และ Unraid สร้าง vdisk แบบ raw โดยค่าเริ่มต้น VM รันต่อระหว่างการสำรอง เหมือนแบบขณะทำงาน และเซกเมนต์ที่รอ restic ใช้ที่ได้ถึง 4 GiB ใต้ /config

VM ที่มีสแนปช็อต Unraid

สแนปช็อตที่ VM Manager ของ Unraid สร้างเปลี่ยนดิสก์ qcow2 เป็นห่วงโซ่: อิมเมจเดิมอยู่ล่างสุด และข้างบนมีหนึ่งไฟล์ต่อสแนปช็อต แต่ละไฟล์เก็บเฉพาะสิ่งที่เปลี่ยนนับจากอันข้างล่าง VM เขียนลงไฟล์ใหม่สุด ดิสก์ที่มีสองสแนปช็อตคือสามไฟล์

BombVault สำรอง VM แบบนั้นครบ

  • ทุกไฟล์ของห่วงโซ่ มันตามห่วงโซ่ backing ของแต่ละดิสก์จนสุดและสำรองทุกไฟล์ในนั้น พร้อมรายการสแนปช็อตของ Unraid (/etc/libvirt/qemu/snapshotdb/<vm>/snapshots.db อ่านผ่าน SSH) ห่วงโซ่ที่ตามไปจนสุดไม่ได้ เช่นเพราะไฟล์ตรงกลางหาย ทำให้การสำรองล้มเหลวและระบุไฟล์
  • การสำรองแบบสดเก็บสแนปช็อตไว้ สแนปช็อตขณะทำงาน วางโอเวอร์เลย์ชั่วคราวไว้ด้านบนขณะอ่านดิสก์ แล้วรวมกลับทีหลัง เฉพาะเข้าอิมเมจที่อยู่ใต้มันทันที ห่วงโซ่จึงเหมือนเดิมหลังการสำรองเหมือนก่อน VM ที่ไม่มีสแนปช็อตทำงานเหมือนที่เคย
  • การกู้คืนนำห่วงโซ่กลับมา การกู้คืนทับที่เดิมเขียนทุกไฟล์กลับที่เดิมและวางรายการสแนปช็อตของ Unraid กลับ VM Manager จึงลิสต์สแนปช็อตอีกครั้ง การกู้คืนข้างต้นฉบับเขียนไฟล์ลงโฟลเดอร์ของสำเนาและชี้ดิสก์ที่กู้คืนไปยังไฟล์ที่กู้คืน สำเนาจึงไม่เคยอ่านจากอิมเมจของต้นฉบับ รายการสแนปช็อตของ Unraid กลับมาเฉพาะกับการกู้คืนทับที่เดิม เพราะมันระบุไฟล์ตามที่ที่สำรอง ถ้าไม่มี สำเนาก็บูตได้เหมือนกัน เพียงแต่ VM Manager ไม่ลิสต์สแนปช็อตให้มัน
  • เฉพาะบล็อกที่เปลี่ยน อ่านสิ่งที่เกสต์เห็น การกู้คืนของมันจึงกลับมาเป็นไฟล์ qcow2 แบบแบนราบไฟล์เดียวโดยไม่มีสแนปช็อต
  • การกู้คืนนำดิสก์ของข้อมูลสำรองที่คุณเลือกกลับมา สแนปช็อตที่ถ่ายหรือรวมหลังข้อมูลสำรองนั้นไม่เปลี่ยนเรื่องนี้ ข้อมูลสำรองจากก่อนสแนปช็อตจะ define VM บนไฟล์ที่มันเก็บไว้ และข้อมูลสำรองจากก่อนการรวมจะนำโอเวอร์เลย์ของมันกลับมาและรันบนนั้น ไฟล์ที่ใหม่กว่าจะอยู่ที่เดิม ไม่ถูกใช้และไม่ถูกเปลี่ยน ข้อมูลสำรองที่มีโอเวอร์เลย์แต่ไม่มีไฟล์ข้างใต้จะถูกปฏิเสธก่อนแตะ VM หลังการกู้คืน คำจำกัดความของ VM ไม่ลิสต์ห่วงโซ่ไว้ libvirt จึงอ่านห่วงโซ่จากไฟล์ที่กู้คืน

ตัวอย่าง: VM ที่มีสองสแนปช็อต

VM homeassistant มีหนึ่งดิสก์ vdisk1.qcow2 และสองสแนปช็อตที่คุณทำใน VM Manager ก่อนอัปเดตสองครั้ง ตอนนี้ดิสก์ของมันเป็นห่วงโซ่สามไฟล์ มันรันตลอดเวลา จึงใช้ สแนปช็อตขณะทำงาน

  1. เปิด การสำรองข้อมูล แล้ว homeassistant แล้วแท็บ การตั้งค่า ของมัน ในการ์ด VM ตรวจว่า วิธีการ เป็น สแนปช็อตขณะทำงาน และ เฉพาะบล็อกที่เปลี่ยน ปิดอยู่
  2. กด สำรองข้อมูลเดี๋ยวนี้ ที่หัวหน้า
  3. เมื่อการรันเสร็จ บรรทัดของมันใน การทำงานล่าสุด บนแท็บ ยืนยัน เป็นสีเขียว VM รันต่อตลอดเวลา และข้อมูลสำรองมีครบทั้งสามไฟล์ของดิสก์และรายการสแนปช็อตของ Unraid
  4. ใน VM Manager สองสแนปช็อตยังอยู่ และดิสก์ยังอ่านผ่านสามไฟล์เดียวกัน

หนึ่งเดือนต่อมา การอัปเดตผิดพลาดและคุณอยากได้สถานะเมื่อวานกลับมาข้างอันที่พัง เพื่อเปรียบเทียบ

  1. เปิด การกู้คืน เลือก VMs แล้วคลิก homeassistant
  2. คงข้อมูลสำรองที่แนะนำจากเมื่อคืนไว้ และเลือก ข้างต้นฉบับ ใต้ ไปที่ไหน
  3. กด กู้คืน ทั้งสามไฟล์ไปอยู่ในโฟลเดอร์ที่ตั้งชื่อตาม VM พร้อม -restored และดิสก์ที่กู้คืนชี้ไปยังไฟล์ที่กู้คืน VM ที่กำลังรันไม่เปลี่ยนอะไร

ถ้าจะย้อน VM ทั้งตัวแทน ให้เลือก แทนที่ต้นฉบับ พร้อมเปิด สำรองสถานะปัจจุบันก่อน BombVault สำรองสถานะที่พัง ปิด VM เขียนสามไฟล์และรายการสแนปช็อตของ Unraid กลับ แล้วเริ่มมันอีกครั้ง

TrueNAS SCALE

ขั้นตอนข้างบนเขียนสำหรับ Unraid แต่ลิงก์ qemu+ssh:// เดียวกันใช้ได้กับ libvirtd ใด ๆ ที่คอนเทนเนอร์เข้าถึงได้ รวมถึง TrueNAS SCALE ซึ่งมี VM บน libvirt อีกครั้งตั้งแต่ 25.04.2 บน TrueNAS สตริงการเชื่อมต่อต้องมีการแทนที่ และคีย์ไปอยู่ที่ root ในการตั้งค่าผู้ใช้ของ TrueNAS เอง อีกสามอย่างเปลี่ยนสิ่งที่การสำรองเก็บ: ดิสก์บน zvol สถานะ vTPM และชื่อที่ TrueNAS ตั้งให้ VM

1. socket ของ libvirt

libvirtd ของ TrueNAS ไม่ฟังที่ที่สตริงการเชื่อมต่อซึ่งสร้างจากฟิลด์ SSH ของโฮสต์ คาดไว้ ให้ตั้ง LIBVIRT_URI แทน พร้อมพารามิเตอร์ ?socket= เพิ่ม

LIBVIRT_URI=qemu+ssh://<user>@<truenas-host>/system?socket=/run/truenas_libvirt/libvirt-sock

BombVault ใช้ LIBVIRT_URI ตามที่เขียนทุกตัวอักษร และไม่ต้องใช้ LIBVIRT_HOST, LIBVIRT_SSH_USER และ LIBVIRT_SSH_PORT สำหรับแต่ละตัวที่ไม่ได้ตั้ง BombVault เอาโฮสต์ ผู้ใช้ และพอร์ตจาก URI สำหรับคำสั่ง SSH ของตัวเองด้วย การคัดลอก NVRAM และการสำรองชุดข้อมูล ZFS จึงไปถึงเครื่องเดียวกับ virsh ตัวแปรที่คุณตั้งเองชนะเสมอ และบันทึกการบูตบอกเป้าหมายที่เอามาจาก URI บน TrueNAS ผู้ใช้ที่ไม่ใช่ root ต้องมี zfs allow <user> snapshot,destroy,mount <dataset> สำหรับการสำรองชุดข้อมูล ZFS ด้วย ดู ชุดข้อมูล ZFS

ใส่ที่อยู่ LAN ของ TrueNAS ลงใน URI ถ้าไม่ตั้งทั้ง LIBVIRT_URI และ LIBVIRT_HOST BombVault ใช้เกตเวย์เริ่มต้นของแอปเป็นโฮสต์ เพราะ host.docker.internal ไม่ resolve ในแอป TrueNAS และบันทึกการบูตบอกที่อยู่ที่เลือก นั่นพอสำหรับการสำรองชุดข้อมูล ZFS การสำรอง VM ยังต้องใช้ URI สำหรับ socket

แอปต้องมีความสามารถ SETPCAP สำหรับการสำรอง VM ควบคู่กับ CHOWN, DAC_OVERRIDE, FOWNER, SETGID และ SETUID ถ้าไม่มี libvirt จะหยุดด้วย "cannot apply process capabilities" YAML ใต้ TrueNAS SCALE ตั้งทั้งหมดนี้ พร้อม SYS_CHROOT ที่สแนปช็อตแทนการหยุดต้องใช้

บน TrueNAS SCALE 25.10.0 พาธ socket ถูกตรวจบนฮาร์ดแวร์จริง: qemu:///system ปกติล้มเหลวด้วย Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory และการเรียกเดียวกันด้วย ?socket=/run/truenas_libvirt/libvirt-sock ลิสต์ domain ได้

2. คีย์ของ BombVault บน TrueNAS

ขั้นที่ 2 ข้างบนเขียนลงแฟลชไดรฟ์ของ Unraid และใช้ที่นี่ไม่ได้ บน TrueNAS

  1. เปิดบริการ SSH ที่ System, Services
  2. ใต้ Credentials, Users แก้ไข root และวางกุญแจสาธารณะของ BombVault จาก การตั้งค่า, การเชื่อมต่อ, SSH ของโฮสต์ เป็นกุญแจสาธารณะ SSH ของมัน TrueNAS เก็บไว้ข้ามการรีบูตและการอัปเดต

การติดตั้งใหม่ของ TrueNAS 24.10 ขึ้นไปไม่ให้ root มีรหัสผ่าน และการเข้าสู่ระบบด้วยคีย์ไม่ต้องใช้ ถ้าไม่อยากใช้ root ให้อนุญาตคีย์สำหรับบัญชีผู้ดูแลที่มี sudo และใส่ชื่อของมันแทน root ใน LIBVIRT_URI การสำรองชุดข้อมูล ZFS จะต้องใช้ zfs allow ตามที่อธิบายข้างบน

เรพลิกา ZFS ไปยังเซิร์ฟเวอร์ตัวที่สองลงชื่อเข้าใช้ด้วยคีย์ของตัวเองที่กล่องเรพลิกาแสดง คีย์ SSH ของโฮสต์ไม่ได้อนุญาตเรพลิกา และคีย์เรพลิกาไม่ได้อนุญาตการสำรอง VM

TrueNAS มองไม่เห็นการเปลี่ยนแปลงที่ทำด้วย virsh

middleware ของ TrueNAS ซึ่งเป็นชั้นเบื้องหลังหน้าจอ VM ของมัน ถูกออกแบบมาให้เป็นทางเดียวที่เปลี่ยน VM ของมัน คำสั่ง virsh ผ่านลิงก์ SSH นี้ ซึ่งเป็นสิ่งที่การสำรองและกู้คืน VM ของ BombVault รัน อ้อมมันไป และ TrueNAS อาจ reconcile หรือเขียนทับสถานะที่มันไม่ได้สร้างภายหลัง ข้อนี้มาจากพฤติกรรมที่ TrueNAS บันทึกไว้ และยังไม่เคยเห็นบนกล่องจริง เป็นความเสี่ยงชนิดเดียวกับที่ VM Manager ของ Unraid reconcile การเปลี่ยนแปลงที่ทำนอกมัน เพียงแต่แหลมกว่าบน TrueNAS

3. ดิสก์บน zvol

ดิสก์ VM ของ TrueNAS มักเป็น zvol ซึ่งเป็นอุปกรณ์บล็อก ZFS ที่ /dev/zvol/<pool>/<dataset> ไม่ใช่ไฟล์ qcow2 restic สำรองอุปกรณ์บล็อกด้วยพาธของมันแบบที่สำรองไฟล์ไม่ได้ BombVault จึงรู้จักดิสก์ zvol ใน XML ของ domain (<source dev="..."> แทน <source file="...">) และจัดการผ่านลิงก์ SSH เดียวกัน คอนเทนเนอร์ BombVault ไม่ต้องมีเครื่องมือ ZFS

การสำรอง zvol

  1. zfs snapshot <dataset>@bombvault-vm-<time> ตรึงจุดเวลาหนึ่ง แม้ขณะที่ VM รันอยู่
  2. zfs clone -o volmode=dev -o readonly=on ทำให้สแนปช็อตนั้นอ่านได้เป็นอุปกรณ์ volmode=dev ทำให้โฮสต์ไม่สแกนพาร์ทิชันของเกสต์
  3. dd อ่านอุปกรณ์ และอิมเมจ raw เข้า restic เป็นไฟล์เดียว /vm-disks/<dataset>.img ติดแท็ก vm:<name>:zvol:<dev> restic ตัดเป็นชิ้นตามเนื้อหา บล็อกที่ไม่เปลี่ยนนับจากการสำรองครั้งก่อนจึงไม่ถูกเก็บซ้ำ
  4. clone และสแนปช็อตถูกทำลายทีหลัง ไม่ว่าการสำรองจะสำเร็จหรือไม่ ถ้า BombVault หยุดกลางการสำรอง การสำรอง zvol นั้นครั้งถัดไปจะลบสแนปช็อต bombvault-vm-<time> และ clone <dataset>-bombvault-vm-<time> ของมันก่อน มันปล่อยสแนปช็อตอื่นทุกอันไว้ และเก็บอันที่มีอย่างอื่น clone จากมัน บันทึกบอกสิ่งที่ลบหรือเก็บ

BombVault ไม่ใช้ zfs send สำหรับเรื่องนี้ ทุกเรคอร์ดของสตรีม send มี GUID ของสแนปช็อตและ checksum ที่ไหลต่อเนื่อง restic จึงไม่พบอะไรของการสำรองครั้งก่อนที่จะนำกลับมาใช้ และเก็บทั้งดิสก์ใหม่ทุกครั้ง วัดบน TrueNAS SCALE 25.10 กับ zvol 512 MiB ที่มีข้อมูล 200 MiB: การสำรองครั้งแรกเพิ่ม 211 MB ครั้งที่สองที่ไม่มีอะไรเปลี่ยน 52 KB และหลังจากเปลี่ยน 1 MiB เพิ่ม 3.6 MB zvol เดียวกันผ่าน zfs send เพิ่ม 214 MB ทุกรอบ

ทุกรอบยังเขียนสแนปช็อตเล็ก ๆ ที่ติดแท็ก vm:<name> พร้อม XML ของ domain หลังจากทุกดิสก์เข้าแล้ว VM ที่ดิสก์ทั้งหมดเป็น zvol ไม่มีสแนปช็อตไฟล์ และอันนี้คืออันที่ลิสต์การรันไว้ใต้ VM และทำให้กู้คืนได้

การกู้คืนเขียนแต่ละ zvol กลับที่เดิม เหมือนที่ดิสก์ไฟล์ถูกเขียนกลับลงไฟล์ของมัน VM ที่กู้คืนจึงบูตจากข้อมูลที่กู้คืน

  1. ก่อนแตะอะไร BombVault ตรวจว่าการรันมีอิมเมจของทุกดิสก์ zvol การรันที่ขาดหนึ่งอันจะถูกปฏิเสธและ VM ถูกปล่อยไว้ตามเดิม
  2. zvol ที่มีอยู่ได้สแนปช็อตก่อน <dataset>@bombvault-before-restore-<time> โดยเวลาเป็น UTC เหมือนชื่อสแนปช็อตทุกอันที่ BombVault ทำ จึงอาจห่างจากนาฬิกาท้องถิ่นหลายชั่วโมง บันทึกบอกชื่อมัน และ zfs rollback ไปยังมันย้อนการกู้คืนได้ขณะที่มันเป็นสแนปช็อตใหม่สุดของ zvol เมื่อมีอันใหม่กว่า เช่นของการกู้คืนครั้งถัดไป zfs rollback -r ทำได้และทำลายสแนปช็อตที่ใหม่กว่า มันอยู่จนกว่าคุณลบด้วย zfs destroy เมื่อ VM ที่กู้คืนใช้ได้
  3. zvol ที่ไม่มีอยู่แล้วถูกสร้างที่ขนาดที่มีตอนสำรอง และอันที่มีอยู่แต่ขนาดอื่นถูกตั้งกลับเป็นขนาดนั้น
  4. dd เขียนอิมเมจลง /dev/zvol/<dataset> เมื่อเปิดการบีบอัด ซึ่งเป็นค่าเริ่มต้นของ TrueNAS ส่วนว่างของอิมเมจกลับมาเป็นช่องว่างและไม่ใช้พื้นที่

การกู้คืนไปยังอินสแตนซ์อื่นต้องมีพูลปลายทาง มันย้ายดิสก์ไฟล์ไปยังโฟลเดอร์ปลายทาง แต่โฟลเดอร์ไม่ระบุพูล ZFS ดังนั้นถ้าไม่มี BombVault จะปฏิเสธการกู้คืนก่อนเริ่ม ส่งพูลเป็นพารามิเตอร์ zvolPool ของ POST /api/foreign/restore: แต่ละ zvol เก็บพาธของมันใต้พูล (tank/vms/win10/disk0 ไปยัง flashpool กลายเป็น flashpool/vms/win10/disk0) และ XML ของ domain ชี้ไปที่มัน หน้าต่าง จากโฟลเดอร์สำรองข้อมูลเก่า บนหน้าการกู้คืนไม่มีฟิลด์สำหรับเรื่องนี้ จึงทำได้ผ่านการเรียก API โดยตรงเท่านั้น การกู้คืนบนอินสแตนซ์เดียวกันเขียนแต่ละ zvol กลับลงตัวมันเอง

การส่งออกเป็นไฟล์เว้น zvol ไว้ VM ที่ดิสก์ทั้งหมดเป็น zvol ถูกปฏิเสธด้วยเหตุผลนั้น ให้กู้คืนจากข้อมูลสำรองแทน สำหรับ VM ที่มีดิสก์ไฟล์ด้วย การส่งออกมีดิสก์ไฟล์และหมายเหตุที่ระบุ zvol ที่เว้นไว้

สิ่งนี้ลองบน TrueNAS SCALE 25.10 กับ ZFS 2.3.4 แล้ว: การสำรองสามครั้งของ zvol ทดลอง การกู้คืนทับที่เดิมหลังจาก zvol ถูกเขียนทับและขยาย และการกู้คืนลง zvol ที่ถูกลบ การกู้คืนแต่ละครั้งอ่านกลับได้ SHA-256 เดียวกับต้นทาง zvol กลับมามีขนาดเดิม และไม่มีสแนปช็อตหรือ clone หลงเหลือ VM ทั้งตัวที่ดิสก์เดียวคือ zvol ถูกสำรองขณะรันทั้งด้วยแบบราบรื่นและแบบขณะทำงาน และขณะปิด และการกู้คืน VM ทั้งตัว (undefine เขียน zvol define บูต) ก็รันที่นั่นด้วย: zvol อ่านกลับได้ checksum ของต้นทางและ VM เริ่มทำงาน

ดิสก์ไฟล์แบบที่ Unraid ใช้ ไม่ได้รับผลจากเรื่องเหล่านี้เลย

4. สถานะ vTPM

VM มี TPM เสมือนได้ ซึ่ง Secure Boot และเกสต์ Windows 11 ต้องใช้ สถานะของมันอยู่นอกดิสก์และ NVRAM การสำรองเฉพาะสองอย่างนั้นจึงนำเกสต์กลับมาพร้อม TPM ใหม่ที่ว่างเปล่า

  • TPM แบบ passthrough ชิปจริงที่ส่งให้เกสต์ ระบุพาธไว้ใน XML ของ domain BombVault บันทึกสถานะของมันข้างๆ NVRAM ผ่าน SSH และเขียนกลับก่อน virsh define แบบ best effort เหมือน NVRAM: การอ่านหรือเขียนที่ล้มเหลวถูกบันทึกและไม่ทำให้การสำรองหรือกู้คืนล้มเหลว
  • vTPM แบบจำลอง ชนิดที่ TrueNAS ตั้งให้เกสต์ Windows 11 และ Secure Boot ไม่เผยแพร่พาธสถานะใน XML ของ domain สถานะของมันจึงไม่ถูกสำรอง BombVault ไม่เดาพาธ และการสำรองจะเสร็จพร้อมหมายเหตุว่าสถานะ TPM ของ VM ไม่อยู่ในนั้น เก็บรหัสกู้คืนของเกสต์นั้น เช่นคีย์ BitLocker ไว้ใกล้มือก่อนกู้คืน
  • คุณต่อ TPM เข้ากับ VM ของ TrueNAS เองไม่ได้ API อุปกรณ์ VM ของ TrueNAS SCALE 25.10 รับเฉพาะ CDROM, DISPLAY, NIC, PCI, RAW, DISK และ USB และปฏิเสธ TPM ว่าเป็นชนิดที่ไม่รู้จัก

การจัดการ TPM ผ่านการทดสอบหน่วยเท่านั้น และยังไม่เคยรันกับเกสต์ TrueNAS จริงที่มี vTPM VM ที่ไม่มี TPM สำรองและกู้คืนเหมือนเดิมทุกอย่าง

NVRAM ถูกจับตามปกติ TrueNAS เก็บต่อ VM ที่ /var/db/system/vm/nvram/<id>_<name>_VARS.fd และ XML ของ domain ระบุพาธนั้น

5. ชื่อ VM บน TrueNAS

บน Unraid ชื่อ libvirt ของ VM คือชื่อที่คุณเลือก TrueNAS สร้างชื่อ libvirt เอง และเปลี่ยนวิธีมาแล้วสองครั้ง

เวอร์ชัน TrueNAS ชื่อ libvirt ชื่อของคุณอยู่ที่ไหน
25.10 "Goldeye" {id}_{name} เช่น 1_debian ในชื่อ libvirt หลัง {id}_
26 UUID ของ VM เช่น 550e8400-e29b-41d4-a716-446655440000 เฉพาะใน <title> ของ XML domain

บนโฮสต์ที่ BombVault ตรวจพบว่าเป็น TrueNAS หรือที่ PLATFORM=truenas ระบุ รายการการสำรองข้อมูลแสดงชื่อของคุณ: debian แทน 1_debian บน 25.10 และ <title> แทน UUID บน 26 หรือ UUID เมื่อไม่มีชื่อ นี่เป็นเพียงสิ่งที่รายการแสดง ทุกการเรียก virsh และการตั้งค่าต่อ VM ยังใช้ชื่อ libvirt การสำรองและกู้คืนจึงทำกับ VM ที่ถูกต้องเสมอ บน TrueNAS 26 นั่นหมายความว่า VM ที่ TrueNAS สร้างใหม่ด้วย UUID ใหม่จะปรากฏเป็นรายการใหม่โดยไม่มีการตั้งค่าของมัน แม้ชื่อดูเหมือนเดิม บน Unraid ไม่มีอะไรเปลี่ยน รวมถึง VM ที่คุณตั้งชื่อ 10_Windows

รูปแบบ 25.10 เห็นบนกล่อง TrueNAS SCALE 25.10 จริง รูปแบบ TrueNAS 26 มาจากเอกสารของ TrueNAS และผ่านการทดสอบหน่วยเท่านั้น

การแก้ปัญหา

อาการ สาเหตุและวิธีแก้
ทดสอบการเชื่อมต่อ ค้างแล้วล้มเหลว เข้าถึงโฮสต์ไม่ได้ ตรวจขั้นที่ 3 (เครือข่าย VLAN ไฟร์วอลล์) และ Host SSH: Address กับ Port
Permission denied (publickey) ทั้งที่คีย์อยู่ใน authorized_keys StrictModes ของ sshd ปฏิเสธไฟล์คีย์เพราะโฟลเดอร์แม่มีเจ้าของหรือโหมดผิด พบบ่อยเมื่อ /root/.ssh เป็นลิงก์ไปยังแฟลชไดรฟ์ FAT (/boot/config/ssh/...) ซึ่งเขียนได้สำหรับทุกคน บันทึกของโฮสต์แสดง Authentication refused: bad ownership or modes for directory ... เพิ่ม StrictModes no ลงในการกำหนดค่า sshd ของโฮสต์ในส่วนส่วนกลาง ก่อนบล็อก Match ใด ๆ แล้วรัน /etc/rc.d/rc.sshd restart Unraid เขียน /etc/ssh/sshd_config ใหม่ทุกครั้งที่บูต จึงต้องเก็บถาวรตามที่อธิบายด้านล่าง
Permission denied (publickey) และคีย์ขาดจริง ๆ ทำขั้นที่ 2 ซ้ำ และตรวจว่า SSH เปิดอยู่
Host key verification failed รัน docker exec BombVault rm -f /config/ssh/known_hosts แล้วทดสอบอีกครั้ง
การตรวจในขั้นที่ 3 บอก UNREACHABLE คอนเทนเนอร์เข้าถึงพอร์ต SSH ของโฮสต์ไม่ได้ ดูขั้นที่ 3: host access to custom networks การกำหนดเส้นทาง VLAN หรือ IP shim หรือ LAN
ไม่มีตัวแปรใต้ Edit นำเข้าเทมเพลตอีกครั้ง คอนเทนเนอร์ที่มีอยู่เก็บการกำหนดค่าเก่าที่บันทึกไว้
VM ที่มีสแนปช็อต Unraid ล้มเหลวและระบุไฟล์ ห่วงโซ่ backing ของดิสก์ตามไปจนสุดไม่ได้ ตรวจใน VM Manager ว่าทุกสแนปช็อตของดิสก์นั้นยังอยู่ และไฟล์ที่ระบุมีอยู่ใต้ /mnt

อาการ VM เพิ่มเติมอยู่ใน การแก้ปัญหา

ให้อยู่รอดหลังรีบูต

Unraid เขียน /etc/ssh/sshd_config ใหม่ทุกครั้งที่บูต การเปลี่ยนแปลงฝั่งโฮสต์จึงต้องทำใหม่ตอนบูต

  • พอร์ต SSH เก็บใน /boot/config/ident.cfg ตั้งผ่านหน้าเว็บ Unraid
  • StrictModes no ถ้าคุณต้องใช้ข้างบน จะไม่ถูกเก็บ ใช้ตอนบูตจาก /boot/config/go
# /boot/config/go: keep SSH key auth working with flash-based authorized_keys
( for i in $(seq 1 30); do [ -f /etc/ssh/sshd_config ] && break; sleep 2; done
  grep -q '^StrictModes no' /etc/ssh/sshd_config || \
    sed -i '0,/^Match /s/^Match /StrictModes no\n&/' /etc/ssh/sshd_config
  # SIGHUP reloads sshd's config in place: it keeps the listener + open sessions
  # and avoids the "killing listener process / Restarting SSH server daemon"
  # warning that `rc.sshd restart` prints at boot. Only reload if sshd is already
  # up; if it starts after this runs, it reads the edited config on its own.
  pidof sshd >/dev/null && killall -HUP sshd ) &
  • คีย์ SSH ของ BombVault เองและ ~/.ssh/config อยู่ใน /config (appdata) และถูกสร้างใหม่ทุกครั้งที่เริ่ม จึงอยู่รอดหลังรีสตาร์ทคอนเทนเนอร์และโฮสต์

ความปลอดภัย

คีย์ที่ได้รับอนุญาตให้สิทธิ์ root บนโฮสต์ ซึ่งเป็นระดับความไว้วางใจเดียวกับ Docker socket ที่ BombVault ใช้อยู่แล้ว รัน BombVault บนเครือข่ายที่เชื่อถือได้ คีย์เป็น ed25519 เก็บด้วยโหมด 0600 ใน appdata และคีย์ของโฮสต์ถูกปักหมุด ทุกคำสั่งบนโฮสต์ส่งเป็นอาร์กิวเมนต์แยกกัน ไม่เคยผ่านเชลล์ระยะไกล และการเชื่อมต่อ SSH ใช้ BatchMode และเวลาหมดการเชื่อมต่อ