Pull to refresh

Перенос образа виртуальной машины между облачными хостерами или устанавливаем Windows Server на Digital Ocean

Reading time8 min
Views14K
К примеру, Digital Ocean не предоставляет возможности загрузить свой образ виртуалки, более того, так же нельзя подключить ISO-образ для установки (правда, есть KVM — и на том спасибо).
Поэтому придётся пойти хитрым путём и делать всё через ssh. Основная идея — на новой виртуалке отмонтировать корневую файловую систему, залить файлы/образ от старой, обновить настройки grub, обновить настройки сети и fstab, перезагрузить.
Самое неочевидное в первом шаге. Можно отмонтировать / и на живой системе, это реально, хоть и муторно. Гораздо проще добавить ssh сервер и пару утилит в initramdisk и сделать всё оттуда, т.к. на этом этапе загрузки ОС корневая система еще не примонтирована.
Собственно, вся статья — это демонстрация утилиты для включения ssh сервера в ramdisk + два разобранных примера.



Перенос сервера Debian из Xen-а c паравиртуализацией на Digital Ocean.


Важно чтобы на DigitalOcean виртуалка была с возможностью менять ядро с самой виртуалки (В панели управления DigitalOcean, во вкладке Kernel должно быть написано что меняйте ядро внутри хоста).
Источник на Xen:

Там стоит nginx с такой вот демо страницей


Шаг 1. Подготовка сервера-источника к переносу


Можно ничего не подготавливать, просто выключить сервер-источник и отдавать файлы/образ из гипервизора, но это не интересно и, к тому же, источник вполне может быть не на виртуалке, а на голом железе. Поэтому, первым шагом мы перемонтируем корневую файловую систему (и, соответственно, так же подключенные ФC, если есть) в read-only. Перемонтировать в read-only гораздо проще чем отмонтировать целиком — достаточно просто прибивать демоны, которые могут что-то писать.
/etc/init.d/nginx stop
apt-get install lsof rsync
lsof /

Смотрим что осталось. Нам интересны строчки где колонка FD заканчивается на w или u

Ага, остался rsyslog
kill 6288
mount -o remount,ro /

Если получилось — то хорошо, если нет — то дальше искать кто держит файлы открытыми на запись. Как правило, достаточно выключить все дополнительно установленные демоны + syslog.

Шаг 2. Отмонтирование корневой ФС на сервере-получателе


Получателем у нас будет Ubuntu 16.04. В теории абсолютно не важно что на получателе будет стоять до переноса, т.к. это всё перезатрётся, просто утилита для автоматизации настройки initramdisk работают с ubuntu/debian.
Итак, свежесозданная ubuntu 16.04 на DigitalOcean:
apt-get install -y git dropbear && \
git clone https://github.com/roginvs/early-ssh && \
cd early-ssh && \
./build_deb.sh && \
dpkg -i early-ssh*.deb && \
sed -ie 's/DISABLED=1/DISABLED=0/' /etc/early-ssh/early-ssh.conf  && \
update-initramfs -u && \
reboot

Пингуем IP адрес получателя и следим на TTL. Как только TLL в ответах поменяется — это значит что initramdisk загрузился и ждёт подключений. По умолчанию он долго ждать не будет, если через 15 секунд никто не подключился то загрузка пойдёт дальше.

Подключаемся по ssh и получаем shell. В моём случае авторизация у root-а по ключу, но будет работает и по паролю, даже если в sshd_config стоит 'PermitRootLogin without-password' (потому как в initramdisk стоит dropbear, а не sshd).

Собственно, один раздел.

Шаг 3. Перенос данных


Можно перенести все данные либо через dd (естественно если размер диска получателя не меньше чем источника), либо через rsync. dd будет предпочтительней если на файловой системе мало свободного места либо ну очень много мелких файлов.

Вариант с dd по ssh:

dd if=/dev/xvda2 bs=65536 | ssh root@198.199.127.149 'dd of=/dev/vda1 bs=65536'


Вариант с dd без шифрования, но с компрессией:


# На получателе
nc -q 1 -l 8000 | gzip -d | dd bs=65536 of=/dev/vda1
# На источнике
dd if=/dev/xvda2 bs=65536 | gzip | nc -q 1 198.199.127.149 8000

Из соседней консоли можно постоянно слать сигнал USR1 на dd чтобы видеть как идут дела:
while true; do kill -USR1 `pidof dd`; sleep 10; done


Вариант с rsync:


На получателе пересоздаём файловую систему и монтируем её:
mkfs.ext3 /dev/vda1
mkdir /mnt
mount /dev/vda1 /mnt

Файловая система ext3, а не ext4 только потому что на источнике ext3. Теперь с сервера-источника:
rsync -aAXv --one-file-system --progress / root@198.199.127.149:/mnt/

(Аккуратней со слешами в конце, они должны быть)

Шаг 4. Проверка ядра, проверка /etc/fstab, настройка сети, обновление настроек grub


На получателе монтируем свежескопированную ФС и делаем chroot туда:
mount /dev/vda1 /mnt
mount -o bind /dev/ /mnt/dev/
mount -o bind /proc/ /mnt/proc/
mount -o bind /sys/ /mnt/sys/
chroot /mnt
PATH=$PATH:/usr/sbin
bash


Проверяем что ядро установлено (это нужно если ОС источника была на xen с паравиртуализацией)
apt-cache search linux-image
apt-get install linux-image-3.16.0-4-amd64
dpkg-reconfigure linux-image-3.16.0-4-amd64


Обновляем настройки сети в /etc/network/interfaces
nano /etc/network/interfaces

Тут есть небольшой подводный камень: в некоторых случаях на некоторых ядрах интерфейсы могут называться по-другому. К примеру, в Ubuntu он может называться ens3, а на ядре Debian уже eth0. Лучше просто на всякий создать записи для обоих вариантов. Либо можно в /etc/default/grub дописать настройки ядра чтобы оно не использовало ens наименования (заменить/добавить строчку GRUB_CMDLINE_LINUX):
...
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"
...

+ удалить файл /etc/udev/rules.d/70-persistent-net.rules чтобы ядро начало считать интерфейсы с eth0

Сама корневая фс в /etc/fstab (Устройство называется теперь vda1 + удалить своп)
nano /etc/fstab


Можно и через blkid узнать UUID и вписать его:
UUID="66bd1995-a4e5-418f-a1f7-0ec2f81ac017" / ext4 errors=remount-ro 0 1


Ну и загрузчик:
apt-get install grub2
grub-install /dev/vda
update-grub2


Все, можно всё отмонтировать и перезагрузить:
exit # Из bash
exit # Из chroot
cd /
umount /mnt/dev
umount /mnt/proc
umount /mnt/sys
umount /mnt
reboot


После перезагрузки проверяем что веб-страница открывается с нового IP:


Установка Windows Server 2012r2 на DigitalOcean



Шаг 1. Подготовка источника


С Windows у нас дела обстоят немного сложней, поэтому я сначала делал виртуалку в VmWare, и потом её переносил из гипервизора. При установке указывал что ОС будет Linux, драйвер диска Lsi SAS.
Установленный Windows сервер нужно приготовить к переносу:

Загрузка драйверов для KVM


Скачиваем fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso (https://pve.proxmox.com/wiki/Windows_VirtIO_Drivers), распаковываем, устанавливаем Tools и для каждого inf файла для нашей версии выполняем
PnPUtil -i -a <InfName>.inf


(Или сразу для всех
forfiles /S /M *.inf /C "cmd /c pnputil -i -a @Path"
)

Немного костыльной магии


Костыли это потому что по-хорошему это нужно делать через unattended.xml. Создаем файл c:\firstrun.bat, и пишем туда следующее, попутно заменяя mac/ip/маску/шлюз/пароль тем, что у нас есть на виртуалке приёмнике:
@Echo Off
:: Mac is in Windows format (dashes)!
SET MAC=00-50-56-9E-B8-57
SET IP=192.168.2.85
SET MASK=255.255.255.0
SET GW=192.168.2.1
SET ADMINPW=QWEasd123

echo Started >> c:\firstrun.log
date /T >> c:\firstrun.log
time /T >> c:\firstrun.log

ipconfig /all >> c:\firstrun.log

echo "Start to search network interface" >> c:\firstrun.log
:start_loop
  For /f "delims=, tokens=1,3 skip=1" %%a In ('getmac /V /FO CSV') Do ( 
    IF /I %%b == "%MAC%" (
      echo "Found card" >> firstrun.log
      echo %%a >> firstrun.log
      netsh interface ip set address %%a static %IP% %MASK% %GW% 10
      netsh interface ip add dns %%a 8.8.8.8
      goto :end_loop
    )
  )
  ping 127.0.0.1  
goto :start_loop
:end_loop

net user Administrator %ADMINPW%
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
NetSh Advfirewall set allprofiles state off

reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State" /v ImageState /f
reg add "HKEY_LOCAL_MACHINE\SYSTEM\Setup" /v OOBEInProgress /t REG_DWORD /d 0 /f
reg add "HKEY_LOCAL_MACHINE\SYSTEM\Setup" /v RestartSetup /t REG_DWORD /d 0 /f
reg add "HKEY_LOCAL_MACHINE\SYSTEM\Setup" /v SetupPhase /t REG_DWORD /d 0 /f
reg add "HKEY_LOCAL_MACHINE\SYSTEM\Setup" /v SetupType /t REG_DWORD /d 0 /f

reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v DigitalOceanHack /f
echo "Ok, reboot" >> c:\firstrun.log
shutdown -r -t 20


Далее в консоли добавляем этот скрипт на автозапуск и делаем sysprep в audit:
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v DigitalOceanHack /t REG_SZ /d "C:\firstrun.bat" /f
%WINDIR%\system32\sysprep\sysprep /generalize /audit /shutdown


Образ готов, при следующей загрузке он начнёт себя конфигурировать.
Можно обойтись и без sysprep
Переносил Windows 10 из ESXi в KVM, перед финальным выключением сделал: Device Manager -> IDE ATA/ATAPI controller -> на каждом правой Update Driver Software -> Browse my computer… -> Let me pick… -> Выбрать «Standart ...» (естественно галку «Show compatible hardware» не снимать).
Завелось нормально.


Шаг 2. Отмонтирование корневой ФС в источнике, переразметка партиций


Загружаемся в ramdisk как в предыдушем примере, двигаем корневую ФС, создаём новые разделы в начале диска, обновляем настроки grub:
e2fsck -f /dev/vda1
resize2fs /dev/vda1 25G
fdisk /dev/vda
 Command: d
 Command: n
 Select (default p): p
 Partition number: 1
 First sector: 2048
 Last sector: +716799       ### Такой же как в образе Windows = 716800 секторов минус 1
 Command: n
 Select (default p): p
 Partition number: 2
 First sector: 718848
 Last sector: +32833535   ### Точно так же значение из образа минус 1
 Command: n
 Select (default p): p
 Partition number: 3
 First sector: 73400320 ### Хочу взять 25Гб = 52428800 секторов с конца диска объемом 125829120 секторов. 
 Last sector: +52428799   # 25Гб в секторах - 1
 Command: t
 Partition number: 1
 Partition type: 7
 Command: t
 Partition number: 2
 Partition type: 7
 Command: a
 Partition number: 1
 Command: a
 Partition number: 3
 Command: w


Первый раздел — загрузочный для Windows, 350Мб, второй — сама Windows, третий, соответственно, Linux. Откуда эти числа взялись — будет понятно на следующем шаге, пока что можно сначала сделать два раздела случайного размера (но чтобы последний сектор был не меньше размера образа Windows) и потом уже их пересоздать из Ubuntu когда будет скопирован образ на файловую систему Ubuntu. В конечном итоге важно чтобы разделы для Windows были на тех же местах и такого же размера, что и в образе-источнике.
Я оставил так много на корневую ФС потому как я буду временно туда класть vmdk файл. В теории можно сразу писать его через pipe на раздел.
Теперь копируем Ubuntu (она-то до сих пор лежит в начале диска), 25Гб с /dev/vda начиная с 2048 сектора в /dev/vda3:
dd if=/dev/vda bs=4096 skip=256 count=6553600 of=/dev/vda3

(skip и count в 8 раз уменьшенные потому как блок не 512, а 4096)
Обязательно обновляем настройки grub и правим /etc/fstab если нужно (/dev/vda1 заменить на /dev/vda3):
mkdir /mnt
mount /dev/vda3 /mnt
mount -o bind /dev/ /mnt/dev/
mount -o bind /proc/ /mnt/proc/
mount -o bind /sys/ /mnt/sys/
chroot /mnt
PATH=$PATH:/usr/sbin
grub-install /dev/vda
update-grub2
nano /etc/fstab
exit
cd /
umount /mnt/dev
umount /mnt/proc
umount /mnt/sys
umount /mnt
reboot


Шаг 3. Любым образом копируем vmdk файл на опять загрузившуюся убунту


Хорошо, если vmdk файл сразу представляет собой образ диска (если нет — то нужно сконвертировать в raw через «qemu-img convert -p -f vmdk original.vmdk -O raw converted.raw»)
fdisk Windows2012r2.vmdk


Вот, собственно, откуда берутся значения для /dev/vda1 и /dev/vda2 для предыдущего шага.

Копируем Windows с образа в разделы:
dd bs=4096 skip=256 count=89600 if=Windows2012r2.vmdk of=/dev/vda1
dd bs=4096 skip=89856 count=4104192 if=Windows2012r2.vmdk of=/dev/vda2

(skip и count так же умешьненные в 8 раз)
Можно попытаться примонтировать /dev/vda1 и /dev/vda2 для того, чтобы проверить что всё ок, и, если нужно, поправить настройки в костыльном скрипте.

Обновляем grub, смотрим что он увидел Windows:
update-grub2

Теперь включаем Windows на следующую перезагрузку, это удобно если что-то пойдёт не так, то вернуться можно выключив и включив виртуалку:
grub-reboot 2
reboot

Т.к. образ подготовленный через sysprep + костыльный скрипт, то Windows захочет 2 раза перезагрузится с интервалом в несколько минут. Т.е. еще 2 раза придётся залогинится в ubuntu и запустить последние 2 команды.

Шаг 4. Загрузка Windows по-умолчанию


Вместо того, чтобы включать windows в дефаулт в grub лучше просто добавить в /etc/rc.local:
sleep 60 && grub-reboot 2 && reboot &

— если Windows перестанет загружаться, то можно успеть залогинится в Ubuntu и сделать «killall sleep».

После перезагрузок можно зайти по RDP:


Ссылки:
Устанавливаем любой Linux дистрибутив на Digital Ocean
Tags:
Hubs:
Total votes 11: ↑11 and ↓0+11
Comments4

Articles