Уход с VMware после Broadcom: проверенный план миграции
Коротко (проверено 2026-11-03): после покупки VMware компания Broadcom прекратила продажу бессрочных лицензий, свела линейку продуктов к двум пакетам по подписке, закрыла старую программу облачных партнёров в Европе, а этой осенью убрала из публичного доступа VDDK - библиотеку для работы с дисками, через которую большинство инструментов миграции читает диски VMware. Сегодня The Register сообщает об открытой замене этой библиотеки, работающей только на чтение. Впрочем, не каждый путь ухода требует VDDK: экспорт в OVA плюс конвертация диска вообще не используют код VMware. Ниже: изменения с датами, реалистичные целевые платформы для команды в ЕС, пошаговый переезд и конвертер, который мы запустили на настоящем экспорте в формате VMware.
Источник. "Architect creates open-source replacement for VM migration tool VMware has hidden" (Simon Sharwood, The Register, 2026-11-03). На остальные факты даны ссылки там, где они упоминаются. Ваш собственный договор может отличаться от публичных условий, поэтому уточняйте детали лицензирования у своего реселлера.
Что изменила Broadcom: хронология
| Дата | Изменение | Источник |
|---|---|---|
| 2023-12-11 | Конец продаж бессрочных лицензий и продления поддержки; предложение сокращено до подписок VMware Cloud Foundation (VCF) и vSphere Foundation (VVF) | Новости Broadcom, Krish Prasad |
| 2025-03-28 | Дистрибьютор Arrow сообщает партнёрам, что с 2025-04-10 минимальный заказ вырастает с 16 до 72 ядер на продуктовую линейку | The Register |
| 2026-01-26 | Договоры облачных провайдеров (VCSP) в ЕЭЗ не продлеваются; открытые сделки нужно закрыть до 2026-03-31; дальше программа только по приглашениям | The Register, 2026-01-31 |
| Aug-Sep 2026 | VDDK убран из публичной загрузки: "The VDDK is not a customer entitlement" (VDDK не входит в права клиента), доступ через отдельных партнёров TAP | TechTarget, 2026-09-09 |
| 2026-11-03 | OpenVDDK от Andrija Panić: совместимая замена libvixDiskLib.so.8.0.3 только для чтения под лицензией Apache, которая "contains no VMware code" (не содержит кода VMware) | The Register, 2026-11-03 |
Две оговорки. О минимуме в 72 ядра сообщил дистрибьютор, а не страница Broadcom, которую нам удалось найти: статья Broadcom о подсчёте ядер (KB 313548, проверено 2026-11-03) гласит "You must license a minimum of 16 physical cores for each CPU" (нужно лицензировать минимум 16 физических ядер на каждый CPU), а некоторые реселлеры утверждают, что минимальный заказ в 72 ядра снова отменили. Просите письменно указать количество ядер в коммерческом предложении. Кроме того, европейская история ещё не закончена: в июле 2025 года отраслевое объединение облачных провайдеров CISPE обратилось в Общий суд ЕС с требованием отменить одобрение сделки Еврокомиссией (дело T-503/25); решения мы пока не нашли.
Почему VDDK важен и какие пути обходятся без него
Библиотека VDDK, libvixDiskLib, позволяет инструменту читать диски ВМ, которые ещё работают в vSphere. The Register называет среди инструментов, которые её загружают, virt-v2v, nbdkit, импортеры Forklift / MTV, Apache CloudStack и собственные скрипты компаний. Расширение Microsoft VM Conversion для Windows Admin Center (из vCenter в Hyper-V, с тёплой синхронизацией через Change Block Tracking) до сих пор помечено как preview в его документации (обновлено 2026-02-20), а материалы о запуске называют VDDK среди предварительных требований. OpenVDDK - новый вариант для всех них, хотя автор сам предупреждает, что отчёты тестировщиков "not independently audited customer endorsements" (не являются независимо проверенными отзывами клиентов).
Пути вообще без VDDK, согласно документации virt-v2v по вводу из VMware (проверено 2026-11-03): экспорт OVA (-i ova), файлы ВМ на NFS или через SSH (-i vmx, -it ssh), NFC и обычный путь через vCenter HTTPS, который документация называет "the slowest method" (самый медленный способ). Мастер импорта ESXi в Proxmox VE, анонсированный 2024-03-27, читает диски через ESXi API с помощью собственной файловой системы пользовательского пространства на Rust и был протестирован с ESXi от 6.5 до 8.0 (форум Proxmox).
Куда переезжать: пять реалистичных вариантов
| Целевая платформа | Подходит, когда | На что обратить внимание |
|---|---|---|
| Proxmox VE (KVM) на вашем оборудовании | 3+ хоста, команда хочет GUI и кластеризацию | Обновления, хранилище (ZFS, Ceph) и поддержка на вас |
| OpenStack | Много арендаторов, всё через API | Тяжёл в эксплуатации; окупается только на масштабе |
| Hyper-V (Windows Server) | Преимущественно Windows, лицензии Datacenter уже оплачены | Инструменту Microsoft сначала нужны драйверы Hyper-V в гостевых Linux |
| Облачные серверы (KVM) | От нескольких до нескольких десятков ВМ, нет желания обслуживать гипервизор | Один импорт на каждую ВМ, новые IP-адреса |
| Выделенные серверы + собственный гипервизор | Стабильная нагрузка, нужна вся машина | Гипервизор вы эксплуатируете и обновляете сами |
Перенос ВМ за пять шагов
- Инвентаризация и уборка. Зафиксируйте прошивку (BIOS или EFI), контроллер диска, тип сетевой карты, статические IP и любую лицензию, привязанную к MAC-адресу. Удалите или консолидируйте снимки, чтобы экспортировать один актуальный диск.
- Подготовьте гостевую систему, пока она ещё работает на VMware. Windows: установите драйверы virtio (virtio-win) сейчас; гостевая Windows, перенесённая на диск virtio без них, не загрузится. Linux: драйверы virtio есть в ядре, но initramfs в режиме host-only может их не содержать; проверьте через
lsinitramfsилиlsinitrdи при необходимости пересоберите его. - Экспорт или подключение. Выключите ВМ и экспортируйте OVF / OVA (в vSphere Client "Export OVF Template" или через ovftool), либо позвольте virt-v2v или мастеру Proxmox забрать её самостоятельно.
- Конвертация. Только диск - через qemu-img, всю гостевую систему - через virt-v2v (он также настраивает драйверы), или напрямую в Proxmox из OVF.
- Загрузка через консоль, затем уборка. Проверьте имя сетевого интерфейса и IP, удалите VMware Tools или open-vm-tools, установите 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
Это задокументированные формы из руководства каждого инструмента; эти три команды мы здесь не запускали. Конвертация, которую мы действительно запустили, приведена ниже.
Проверено: что на самом деле делает конвертация (ova2raw.js)
Чтобы показать, из чего состоит процесс, мы написали конвертацию примерно в 100 строках на Bun без зависимостей. Скрипт открывает OVA (обычный tar), проверяет манифест SHA-256, выводит оборудование, которое запрашивает OVF, распаковывает streamOptimized VMDK зерно за зерном в разреженный raw-образ и проверяет GPT. Мы подали ему официальный OVA Ubuntu 24.04, настоящий экспорт в формате VMware: noble-server-cloudimg-amd64.ova, сборка от 2026-10-17, SHA-256 которой совпал с SHA256SUMS от Ubuntu. Сохраните как 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
Результат, запущено на облачном сервере DCXV с Bun 1.4.4 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"
Три запуска заняли от 14.6 до 16.2 s реального времени на этом сервере. Raw-файл имеет размер 10 GiB и занимает 1.8 GB на диске.
Что вывод подсказывает ещё до переключения
- VmxNet3 и VirtualSCSI - это устройства VMware. На KVM они становятся virtio-net и virtio-blk или virtio-scsi. Гостевой системе нужны драйверы для обоих ещё до первой загрузки, а сетевой интерфейс обычно получает новое имя, поэтому статический IP, привязанный к старому имени, не поднимется. Первую загрузку делайте через консоль.
- Прошивка должна совпадать. Этот OVF ничего не задаёт, значит, это BIOS; на диске есть и раздел BIOS boot, и системный раздел EFI, поэтому он загрузится в любом случае. ВМ, экспортированной с
firmware=efi, на целевой платформе нужен UEFI (OVMF), иначе она не найдёт загрузчик. - Тонкий диск остаётся тонким, только если вы за этим следите. 10 GiB виртуально, 1.8 GB данных. Копируйте инструментами, учитывающими разреженность (
cp --sparse=always,rsync -S), или используйте qcow2, иначе вы будете передавать и хранить нули. - Манифест - ваша проверка передачи. Сверяйте SHA-256 после каждого копирования между площадками: оборванная загрузка без всяких жалоб конвертируется в диск, который сбоит позже.
Планируйте простой
Холодный переезд складывается из экспорта + передачи + конвертации + первой загрузки. Замерьте каждый этап на одной тестовой ВМ, затем умножьте. Арифметика передачи (наша, без накладных расходов протокола): 1.8 GB на 1 Gbit/s - это около 15 s, 1 TB - около 2.2 часа. Тёплые инструменты копируют, пока ВМ работает, и в конце отправляют только изменённые блоки; тестировщики The Register сообщают о холодных и тёплых прогонах с Forklift и MTV. Для баз данных и нагруженных приложений репликация плюс переключение DNS лучше любого копирования дисков: сначала уменьшите TTL (см. распространение DNS и TTL) и пройдите чек-листы из инструкции по миграции на облачный сервер.
Когда подходят облачные или выделенные серверы DCXV
- Несколько ВМ, нет команды для гипервизора: наши облачные серверы работают на KVM с дисками virtio. В панели Переустановка -> "Свой образ (URL)" принимает файл qcow2, raw или vmdk по https; образ больше диска тарифа увеличивает диск и тарифицируется как расширение диска, а сетевые настройки применяются, только если в образе есть cloud-init. Тарифы от EUR 15.00/мес (2 vCPU, 4 GB RAM, 40 GB; проверено 2026-11-03, без НДС).
- Десятки ВМ или собственный кластер Proxmox: выделенные серверы от EUR 210/мес с гипервизором на ваш выбор.
- Гостевые Windows: мы перепродаём лицензии Windows Server; обратитесь до переезда, чтобы лицензия была готова с первого дня.
Оба варианта работают в наших дата-центрах Tier III в Праге и Ковилье.
Спланируйте переезд вместе с нами
Запустите ova2raw.js на одной экспортированной ВМ и отправьте строки с оборудованием из его вывода на sales@dcxv.com, либо запустите облачный сервер и проверьте импорт самостоятельно.
