Odejście od VMware po Broadcom: sprawdzony plan migracji
Krótka odpowiedź (sprawdzone 2026-11-03): Od przejęcia VMware Broadcom przestał sprzedawać licencje wieczyste, zebrał ofertę w dwa pakiety subskrypcyjne, zamknął dawny program partnerów chmurowych w Europie, a tej jesieni wycofał publiczne pobieranie VDDK - biblioteki dyskowej, której większość narzędzi migracyjnych używa do odczytu dysków VMware. Dziś The Register opisuje otwartoźródłowy zamiennik tej biblioteki działający tylko w trybie odczytu. Nie każda droga wyjścia wymaga jednak VDDK: eksport OVA plus konwersja dysku nie korzysta z żadnego kodu VMware. Poniżej: zmiany z datami, realistyczne cele dla zespołu z UE, przeprowadzka krok po kroku oraz konwerter, który uruchomiliśmy na prawdziwym eksporcie w formacie VMware.
Źródło. "Architect creates open-source replacement for VM migration tool VMware has hidden" (Simon Sharwood, The Register, 2026-11-03). Pozostałe fakty mają linki w miejscu, w którym się pojawiają. Twoja umowa może różnić się od publicznych warunków, więc szczegóły licencji potwierdź u swojego resellera.
Co zmienił Broadcom, z datami
| Data | Zmiana | Źródło |
|---|---|---|
| 2023-12-11 | Koniec sprzedaży licencji wieczystych i odnowień wsparcia; oferta ograniczona do subskrypcji VMware Cloud Foundation (VCF) i vSphere Foundation (VVF) | Komunikat Broadcom, Krish Prasad |
| 2025-03-28 | Dystrybutor Arrow informuje partnerów, że od 2025-04-10 minimalne zamówienie rośnie z 16 do 72 rdzeni na linię produktów | The Register |
| 2026-01-26 | Umowy z dostawcami chmury (VCSP) w EOG nie są odnawiane; otwarte transakcje do zamknięcia do 2026-03-31; potem program tylko na zaproszenie | The Register, 2026-01-31 |
| Aug-Sep 2026 | VDDK usunięty z publicznego pobierania: "The VDDK is not a customer entitlement" (VDDK nie jest uprawnieniem klienta), dostęp przez wybranych partnerów TAP | TechTarget, 2026-09-09 |
| 2026-11-03 | OpenVDDK (autor: Andrija Panić): zamiennik drop-in dla libvixDiskLib.so.8.0.3 na licencji Apache, tylko do odczytu, który "contains no VMware code" (nie zawiera kodu VMware) | The Register, 2026-11-03 |
Dwa zastrzeżenia. Minimum 72 rdzeni podał dystrybutor, a nie strona Broadcom, którą udało nam się znaleźć: artykuł Broadcom o liczeniu rdzeni (KB 313548, sprawdzone 2026-11-03) mówi "You must license a minimum of 16 physical cores for each CPU" (minimum 16 fizycznych rdzeni na każdy CPU), a część resellerów twierdzi, że minimalne zamówienie 72 rdzeni znów zniesiono. Poproś o liczbę rdzeni w ofercie na piśmie. Strona unijna też pozostaje otwarta: branżowe stowarzyszenie chmurowe CISPE wniosło w lipcu 2025 do Sądu UE o stwierdzenie nieważności zgody Komisji na fuzję (sprawa T-503/25); orzeczenia jeszcze nie znaleźliśmy.
Dlaczego VDDK ma znaczenie i które ścieżki go omijają
Biblioteka VDDK, libvixDiskLib, pozwala narzędziu czytać dyski maszyn wirtualnych, które wciąż działają w vSphere. The Register wymienia virt-v2v, nbdkit, importery Forklift / MTV, Apache CloudStack i własne skrypty firm wśród narzędzi, które ją ładują. Rozszerzenie VM Conversion firmy Microsoft dla Windows Admin Center (z vCenter do Hyper-V, z synchronizacją na ciepło przez Change Block Tracking) nadal ma status preview w swojej dokumentacji (aktualizacja 2026-02-20), a relacje z premiery wymieniają VDDK wśród wymagań. OpenVDDK to nowa opcja dla wszystkich tych narzędzi, z ostrzeżeniem autora, że raporty testerów to "not independently audited customer endorsements" (nie są niezależnie zweryfikowanymi rekomendacjami klientów).
Ścieżki bez VDDK według dokumentacji wejścia VMware w virt-v2v (sprawdzone 2026-11-03): eksport OVA (-i ova), pliki VM na NFS lub przez SSH (-i vmx, -it ssh), NFC oraz zwykła ścieżka HTTPS przez vCenter, którą dokumentacja nazywa "the slowest method" (najwolniejszą metodą). Kreator importu z ESXi w Proxmox VE, ogłoszony 2024-03-27, czyta dyski przez API ESXi za pomocą własnego systemu plików w przestrzeni użytkownika napisanego w Rust i był testowany z ESXi od 6.5 do 8.0 (forum Proxmox).
Dokąd się przenieść: pięć realistycznych celów
| Cel | Pasuje, gdy | Na co uważać |
|---|---|---|
| Proxmox VE (KVM) na własnym sprzęcie | 3+ hosty, zespół, który chce GUI i klastrowania | Aktualizacje, storage (ZFS, Ceph) i wsparcie są po twojej stronie |
| OpenStack | Wielu najemców, wszystko przez API | Ciężki w utrzymaniu; opłaca się dopiero w dużej skali |
| Hyper-V (Windows Server) | Środowisko głównie Windows, licencje Datacenter już opłacone | Narzędzie Microsoftu wymaga najpierw sterowników Hyper-V w gościach Linux |
| Serwery w chmurze (KVM) | Od kilku do kilkudziesięciu VM, bez chęci utrzymywania hypervisora | Jeden import na VM, nowe adresy IP |
| Serwery dedykowane + własny hypervisor | Stałe obciążenie, chcesz całą maszynę | Hypervisor utrzymujesz i łatasz sam |
Przenieś VM w pięciu krokach
- Inwentaryzacja i porządki. Zapisz firmware (BIOS lub EFI), kontroler dysku, typ karty sieciowej, statyczne adresy IP i każdą licencję powiązaną z adresem MAC. Usuń lub scal snapshoty, aby wyeksportować jeden aktualny dysk.
- Przygotuj gościa, póki działa jeszcze na VMware. Windows: zainstaluj teraz sterowniki virtio (virtio-win); gość Windows przeniesiony na dysk virtio bez nich nie uruchomi się. Linux: sterowniki virtio są w jądrze, ale initramfs w trybie host-only może je pominąć; sprawdź to przez
lsinitramfslublsinitrdi w razie potrzeby przebuduj go. - Eksport lub połączenie. Wyłącz VM i wyeksportuj OVF / OVA (vSphere Client "Export OVF Template" lub ovftool) albo pozwól, by virt-v2v lub kreator Proxmox pobrał ją sam.
- Konwersja. Sam dysk przez qemu-img, cały gość przez virt-v2v (który dostosowuje też sterowniki) albo bezpośrednio do Proxmox z OVF.
- Uruchom przez konsolę, potem posprzątaj. Sprawdź nazwę karty sieciowej i IP, usuń VMware Tools lub open-vm-tools, zainstaluj qemu-guest-agent.
qemu-img convert -p -f vmdk -O qcow2 disk1.vmdk disk1.qcow2
virt-v2v -i ova VM.ova -o local -os /var/tmp -of qcow2
qm importovf 120 VM.ovf local-lvm
To udokumentowane formy wywołań z podręcznika każdego narzędzia; tych trzech nie uruchamialiśmy tutaj. Konwersja, którą faktycznie uruchomiliśmy, jest poniżej.
Przetestowane: co naprawdę robi konwersja (ova2raw.js)
Aby pokazać poszczególne elementy, napisaliśmy konwersję w około 100 liniach Bun bez zależności. Skrypt otwiera OVA (zwykły tar), sprawdza manifest SHA-256, wypisuje sprzęt, którego wymaga OVF, rozpakowuje VMDK typu streamOptimized grain po grainie do rzadkiego (sparse) obrazu raw i sprawdza GPT. Podaliśmy mu oficjalny OVA Ubuntu 24.04, prawdziwy eksport w formacie VMware: noble-server-cloudimg-amd64.ova, build z 2026-10-17, którego SHA-256 zgadzał się z SHA256SUMS Ubuntu. Zapisz jako ova2raw.js:
// ova2raw.js - check a VMware OVA export and turn its disk into a raw image for KVM
// usage: bun ova2raw.js <export.ova> <out.raw>
import { openSync, readSync, writeSync, ftruncateSync, closeSync, fstatSync } from 'node:fs'
import { inflateSync } from 'node:zlib'
import { createHash } from 'node:crypto'
const [ova, out] = process.argv.slice(2)
const fd = openSync(ova, 'r')
const read = (pos, len) => {
const b = Buffer.alloc(len)
readSync(fd, b, 0, len, pos)
return b
}
// 1. an OVA is a plain tar: list its members
const files = {}
for (let o = 0; o + 512 <= fstatSync(fd).size; ) {
const h = read(o, 512)
if (h.every(x => x === 0)) break
const name = h.toString('utf8', 0, 100).replace(/\0.*$/s, '')
const size = parseInt(h.toString('ascii', 124, 136).replace(/\0.*$/s, '').trim(), 8)
files[name] = { pos: o + 512, size }
o += 512 + Math.ceil(size / 512) * 512
}
const find = ext => Object.keys(files).find(n => n.endsWith(ext))
const text = n => read(files[n].pos, files[n].size).toString('utf8')
console.log('OVA members:', Object.entries(files).map(([n, f]) => `${n} (${f.size} B)`).join(', '))
// 2. the .mf manifest: every member's SHA-256 must match
const mf = find('.mf')
for (const line of mf ? text(mf).split('\n') : []) {
const m = line.match(/^SHA(1|256)\((.+)\)\s*=\s*([0-9a-f]+)/i)
if (!m || !files[m[2]]) continue
const h = createHash('sha' + m[1])
const f = files[m[2]]
for (let p = 0; p < f.size; p += 1 << 24) h.update(read(f.pos + p, Math.min(1 << 24, f.size - p)))
console.log(` manifest ${m[2]}: ${h.digest('hex') === m[3].toLowerCase() ? 'OK' : 'MISMATCH'}`)
}
// 3. the .ovf descriptor: what the VM expects from its hardware
const ovf = text(find('.ovf'))
const items = [...ovf.matchAll(/<(?:ovf:)?Item\b[\s\S]*?<\/(?:ovf:)?Item>/g)].map(x => x[0])
const tag = (s, t) => s.match(new RegExp(`<(?:rasd:)?${t}>([^<]*)<`))?.[1]
const kind = { 3: 'CPU', 4: 'RAM', 5: 'IDE controller', 6: 'SCSI controller', 10: 'NIC', 17: 'disk', 20: 'SATA controller' }
console.log('OVF:', ovf.match(/vmw:osType="([^"]+)"/)?.[1] ?? '?', '| firmware',
ovf.match(/key="firmware" ovf:value="([^"]+)"/)?.[1] ?? 'bios (default)')
for (const it of items) {
const k = kind[tag(it, 'ResourceType')]
if (k) console.log(` ${k.padEnd(16)} ${tag(it, 'ElementName') ?? ''} ${tag(it, 'ResourceSubType') ?? ''} ${tag(it, 'VirtualQuantity') ?? ''}`.trimEnd())
}
// 4. the streamOptimized VMDK: a header, then compressed grains and markers
const vmdk = files[find('.vmdk')]
const hd = read(vmdk.pos, 512)
if (hd.readUInt32LE(0) !== 0x564d444b) throw new Error('not a sparse VMDK (no KDMV magic)')
const capacity = Number(hd.readBigUInt64LE(12)) * 512
const desc = read(vmdk.pos + Number(hd.readBigUInt64LE(28)) * 512, Number(hd.readBigUInt64LE(36)) * 512)
console.log(`VMDK ${desc.toString().match(/createType="([^"]+)"/)?.[1]}, virtual size ${capacity} B, grain ${Number(hd.readBigUInt64LE(20)) * 512} B`)
const ofd = openSync(out, 'w')
ftruncateSync(ofd, capacity) // sparse: unwritten grains stay holes = zeros
let pos = Number(hd.readBigUInt64LE(64)) * 512
let grains = 0
let written = 0
while (pos < vmdk.size) {
const m = read(vmdk.pos + pos, 512)
const val = Number(m.readBigUInt64LE(0))
const size = m.readUInt32LE(8)
if (size) { // compressed grain: lba, size, zlib data
const raw = inflateSync(read(vmdk.pos + pos + 12, size))
writeSync(ofd, raw, 0, raw.length, val * 512)
grains++
written += raw.length
pos += Math.ceil((12 + size) / 512) * 512
} else if (m.readUInt32LE(12) === 0) break // end-of-stream marker
else pos += 512 + val * 512 // grain table / directory / footer: metadata only
}
closeSync(ofd)
console.log(`Wrote ${out}: ${grains} grains, ${written} B of data in ${capacity} B`)
// 5. check the result: a valid GPT, and the file systems where the partitions say they are
const rfd = openSync(out, 'r')
const at = (p, l) => { const b = Buffer.alloc(l); readSync(rfd, b, 0, l, p); return b }
const gpt = at(512, 92)
const crc = Buffer.from(gpt)
crc.writeUInt32LE(0, 16)
console.log(`GPT "${gpt.toString('ascii', 0, 8)}", header CRC ${(Bun.hash.crc32(crc) >>> 0) === gpt.readUInt32LE(16) ? 'OK' : 'BAD'}`)
const n = gpt.readUInt32LE(80)
const es = gpt.readUInt32LE(84)
const ents = at(Number(gpt.readBigUInt64LE(72)) * 512, n * es)
console.log(` partition array CRC ${(Bun.hash.crc32(ents) >>> 0) === gpt.readUInt32LE(88) ? 'OK' : 'BAD'}`)
for (let i = 0; i < n; i++) {
const e = ents.subarray(i * es, (i + 1) * es)
if (e.subarray(0, 16).every(x => x === 0)) continue
const first = Number(e.readBigUInt64LE(32))
const last = Number(e.readBigUInt64LE(40))
const sb = at(first * 512 + 1024, 136)
const fs = sb.readUInt16LE(56) === 0xef53 ? `ext4 label "${sb.toString('ascii', 120, 136).replace(/\0.*$/s, '')}"`
: at(first * 512, 512).toString('ascii', 82, 87) === 'FAT32' ? 'FAT32 (EFI system partition)'
: e.toString('ascii', 0, 16) === 'Hah!IdontNeedEFI' ? 'BIOS boot (GRUB for legacy BIOS)' : 'unknown'
console.log(` p${i + 1} ${String((last - first + 1) * 512 / 2 ** 20 | 0).padStart(5)} MiB ${fs}`)
}
bun ova2raw.js noble-server-cloudimg-amd64.ova noble.raw
Wynik, uruchomione na serwerze DCXV Cloud z Bun 1.4.4 dnia 2026-11-03:
OVA members: ubuntu-noble-24.04-cloudimg.ovf (11186 B), ubuntu-noble-24.04-cloudimg.mf (213 B), ubuntu-noble-24.04-cloudimg.vmdk (590749696 B)
manifest ubuntu-noble-24.04-cloudimg.vmdk: OK
manifest ubuntu-noble-24.04-cloudimg.ovf: OK
OVF: ubuntu64Guest | firmware bios (default)
CPU 2 virtual CPU(s) 2
RAM 1024MB of memory 1024
SCSI controller SCSI Controller 0 VirtualSCSI
IDE controller VirtualIDEController 1
IDE controller VirtualIDEController 0
disk Hard Disk 1
NIC Ethernet 1 VmxNet3
VMDK streamOptimized, virtual size 10737418240 B, grain 65536 B
Wrote noble.raw: 28663 grains, 1878458368 B of data in 10737418240 B
GPT "EFI PART", header CRC OK
partition array CRC OK
p1 2558 MiB ext4 label "cloudimg-rootfs"
p14 4 MiB BIOS boot (GRUB for legacy BIOS)
p15 106 MiB FAT32 (EFI system partition)
p16 913 MiB ext4 label "BOOT"
Trzy uruchomienia trwały na tym serwerze od 14.6 do 16.2 s czasu rzeczywistego. Plik raw ma rozmiar 10 GiB i zajmuje 1.8 GB na dysku.
Co mówi wynik przed przełączeniem
- VmxNet3 i VirtualSCSI to urządzenia VMware. Na KVM stają się virtio-net oraz virtio-blk lub virtio-scsi. Gość potrzebuje sterowników do obu przed pierwszym uruchomieniem, a interfejs sieciowy zwykle dostaje nową nazwę, więc statyczny IP przypisany do starej nazwy się nie podniesie. Pierwsze uruchomienie wykonaj przez konsolę.
- Firmware musi się zgadzać. Ten OVF niczego nie ustawia, więc jest to BIOS; dysk zawiera zarówno partycję BIOS boot, jak i partycję systemową EFI, więc uruchomi się w obu trybach. VM wyeksportowana z
firmware=efiwymaga UEFI (OVMF) na docelowym hoście, inaczej nie znajdzie bootloadera. - Thin zostaje thin tylko wtedy, gdy o to zadbasz. 10 GiB wirtualnie, 1.8 GB danych. Kopiuj narzędziami obsługującymi pliki rzadkie (
cp --sparse=always,rsync -S) albo używaj qcow2, inaczej przesyłasz i przechowujesz zera. - Manifest to twoja kontrola transferu. Sprawdzaj SHA-256 po każdej kopii między lokalizacjami: przerwany upload bez żadnych skarg skonwertuje się do dysku, który zawiedzie później.
Zaplanuj przestój
Przeprowadzka na zimno to eksport + transfer + konwersja + pierwsze uruchomienie. Zmierz każdy etap na jednej testowej VM, a potem pomnóż. Rachunek transferu (nasz, bez narzutu protokołu): 1.8 GB przy 1 Gbit/s to około 15 s, 1 TB około 2.2 godziny. Narzędzia na ciepło kopiują podczas pracy VM i na końcu wysyłają tylko zmienione bloki; testerzy cytowani przez The Register opisują przebiegi na zimno i na ciepło z Forklift i MTV. Dla baz danych i obciążonych aplikacji replikacja plus przełączenie DNS wygrywa z każdą kopią dysku: najpierw obniż TTL (zob. propagacja DNS i TTL) i skorzystaj z list kontrolnych w artykule jak przenieść się na serwer w chmurze.
Kiedy pasują serwery w chmurze lub dedykowane DCXV
- Kilka VM, brak zespołu od hypervisora: nasze serwery w chmurze działają na KVM z dyskami virtio. W panelu Reinstalacja -> "Własny obraz (URL)" przyjmuje plik qcow2, raw lub vmdk przez https; obraz większy niż dysk planu powiększa dysk i jest rozliczany jako rozszerzenie dysku, a ustawienia sieci są stosowane tylko wtedy, gdy obraz ma cloud-init. Plany od EUR 15.00/mies. (2 vCPU, 4 GB RAM, 40 GB; sprawdzone 2026-11-03, bez VAT).
- Dziesiątki VM lub własny klaster Proxmox: serwery dedykowane od EUR 210/mies., z wybranym przez ciebie hypervisorem.
- Goście Windows: odsprzedajemy licencje Windows Server; zapytaj przed przeprowadzką, aby licencja była gotowa od pierwszego dnia.
Oba rozwiązania działają w naszych centrach danych Tier III w Pradze i Covilha.
Zaplanuj przeprowadzkę z nami
Uruchom ova2raw.js na jednej wyeksportowanej VM i wyślij linie ze sprzętem z jej wyniku na sales@dcxv.com albo uruchom serwer w chmurze i przetestuj import samodzielnie.
