Как перенести и клонировать сервер с помощью снапшота - Академия Selectel

Как перенести и клонировать сервер с помощью снапшота

Иногда сервер нужно быстро перенести или клонировать — без остановки сервисов и создания образов. Показываем простой способ, как это сделать с помощью снапшота и прямой передачи данных по сети.

Даже если вы не планируете в ближайшее время клонировать сервер или мигрировать на другую инфраструктуру, лучше разобраться в этих вопросах заранее. Причин может быть много:

  • ваша компания решила сменить провайдера;
  • нужно перенести проект с облачного сервера на выделенный;
  • хотите сделать резервную копию всей системы без простоев;
  • планируете подготовить тестовую среду, максимально приближенную к продакшену.

Рассмотрим метод переноса и клонирования сервера без создания образов и остановки сервисов. Подойдет как для облачных, так и для выделенных серверов.

В чем суть метода

Способ, о котором пойдет речь, основан на технологии CoW (Сopy-on-Write) — она позволяет записывать все изменения в копию объекта и при этом не изменять оригинал. Механизм можно реализовать как на уровне блочного устройства — например, LVM, так и на уровне файловой системы — например, btrfs и zfs. 

Далее разберем именно вариант с файловой системой (ФС) — наиболее гибкий и удобный. Рассмотрим процесс переноса.

  1. Создаем снапшот текущей ФС.
  2. Переносим ФС по сети. 
  3. Готовим загрузку на принимающей машине: пересобираем при необходимости ядро и initramfs, устанавливаем загрузчик (grub, systemd-boot или efi-stub).
  4. Проверяем, что все работает, и удаляем временные снапшоты.

Почему не образ

К этому моменту вы могли задаться логичным вопросом: зачем все это, если в облаке можно просто создать образ и развернуть новый сервер? Рассмотрим несколько ключевых преимуществ подхода.

Более универсальный. С помощью образа нельзя «просто так» переехать на выделенный сервер.

Не требует остановки сервиса. Снапшот создается на «живой» системе, а перенос идет в фоне. Приложения продолжают работать.

Быстрее. Образ — это, по сути, длинная цепочка операций между компонентами OpenStack. При этом большой локальный диск может «ехать» часами, а иногда — сутками. Передача снапшота по сети — это прямой потоковый процесс без промежуточных копий.

Схема копирования данных между компонентами OpenStack: Cinder → Glance → Cinder — для сетевых дисков и Nova → Glance → Nova — для локальных.
Пример копирования данных между компонентами OpenStack.

Что такое снапшот и зачем он нужен

Снапшот — это состояние файловой системы, зафиксированное в определенный момент времени. Он работает как точка восстановления и позволяет сохранить «слепок» всей структуры данных без копирования каждого файла.

Представим: в вашей файловой системе есть три файла — 1.txt, 2.conf и 3.png. Вы создаете снапшот, однако пока он ничего не весит, ведь сохраненное состояние идентично настоящему — пока файлы не меняются, снапшот остается «незаполненным». 

После создания снапшота вы пошли дальше и изменили 2.conf. Теперь у вас есть две версии файла: сохраненная в снапшоте и настоящая. Это произошло из-за того, что запись выполнялась в копию файла. Снапшот же теперь занимает ровно столько места, сколько весят внесенные обновления в 2.conf, так как неизмененные части файла и так есть в настоящей версии. 
Идем дальше: вы удалили 3.png, после чего он пропал из файловой системы. Однако он остался в снапшоте, который прибавил вес 3.png.

TL;DR: создав снапшот вы зафиксировали состояние ФС, но при этом не нарушили работу приложений, которые ее изменяют. Полученный снапшот можно скопировать на другой сервер, не рискуя целостностью данных.

Рассмотрим команды, которые вам пригодятся для создания снапшота.

Для btrfs:


    btrfs subvolume snapshot -r / /mnt/rootfs-transfer

Для ZFS:


    zfs snapshot zpool/rootfs@transfer

Подготовка принимающего сервера

Прежде чем приступить к переносу, важно подготовить принимающий сервер: 

  • Разметьте диск;
  • Создайте файловую систему;
  • Учтите нюансы загрузки (BIOS или UEFI);
  • Если это выделенный сервер — продумайте использование RAID (raid1, raid5 и т. д., будет ли он реализован аппаратно, средствами ФС или с помощью mdadm);
  • Загрузите сервер с livecd, например system-rescue.org.

Приведем примеры разметки. 

BIOS-загрузка:


    nvme0n1          476.9G  disk
├─nvme0n1p1          1M  part
├─nvme0n1p2          3G  part  /boot
└─nvme0n1p3      473.8G  part

UEFI-загрузка:


    nvme0n1          476.9G  disk
├─nvme0n1p1        127M  part  /boot/efi
├─nvme0n1p2          3G  part  /boot
└─nvme0n1p3      473.8G  part

В BIOS первый раздел нужен для установки GRUB — он должен обладать флагами bios_grub и boot. В UEFI это, наоборот, обычный раздел FAT32 с флагом esp.

Во втором разделе будут лежать ядро и initramfs. Строго говоря, он не нужен, если ваш загрузчик умеет читать btrfs или zfs, но я его все равно оставил — на всякий случай. 🙂 Оставшееся место отдадим под будущую ФС.


    zpool create -o ashift=12 -o autotrim=on -O normalization=formD -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O canmount=off -O mountpoint=none zpool /dev/nvme0n1p3

Btrfs:


    mkfs.btrfs /dev/nvme0n1p3
mount /dev/nvme0n1p3 /mnt

Переносим сервер

Обе ФС (btrfs, и zfs) могут дампить снапшот в stdout и принимать его через stdin. Это позволяет передавать данные напрямую — просто через pipe, используя netcat или ssh. Без создания временного образа и без лишнего копирования. Далее в примере рассмотрим самый распространенный стек: GRUB и initramfs-tools, которые используют Debian и Ubuntu. Подробности о настройке загрузки вашего дистрибутива рекомендуем уточнять в его официальной документации.

ZFS:


    zfs send zpool/rootfs@transfer | ssh root@new-server zfs receive -F zpool/rootfs

Btrfs:


    btrfs send /mnt/snapshots/rootfs-transfer | ssh root@new-server btrfs receive /mnt/rootfs-transfered

🎉 Данные перенесены! Если /boot был на отдельном разделе, его можно легко перенести с помощью rsync, либо пересобрать все вручную:


    # перенос
rsync -aPvh /boot root@new-server:/boot
# пересборка внутри chroot:
apt reinstall linux-image-generic; update-initramfs -u -k all


Теперь давайте научим перенесенную систему загружаться: установим загрузчик, укажем изменившиеся данные в fstab и конфиге загрузчика. Все эти действия выполняем уже на новом сервере.
Если используем btrfs:


    btrfs subvolume snapshot /mnt/rootfs-transfered /mnt/rootfs
umount /mnt
mount -o subvol=rootfs /dev/sdXX /mnt
arch-chroot /mnt
# смотрим новый UUID
blkid /dev/sdXX
# Здесь исправляем UUID и опцию subvolume, если выбрали имя отличное от того, что было. Если subvolume ранее не использовался, то самое время это исправить! Просто после defaults добавьте “,subvol=rootfs”
nano /etc/fstab
grub-install /dev/sdX
update-grub

Если используем ZFS:


    zfs set canmount=noauto zpool/rootfs
zfs set mountpoint=/ zpool/rootfs
mount -t zfs -o zfsutil zpool/rootfs /mnt
# если /boot находится отдельно, монтируем его тоже:
mount /dev/sdY /mnt/boot
arch-chroot /mnt
grub-install /dev/sdX
update-grub
exit
# не забудьте отмонтировать /boot, если он был отдельно:
umount /mnt/boot
umount /mnt
zfs destroy zpool/rootfs@transfer
zpool export zpool

Обычно это не нужно, но если у вас в initramfs есть процессы, обращающиеся к реальной ФС, то перегенерируйте ее:


    update-initramfs -u -k all

Теперь сервер готов к работе. Если снапшот больше не нужен — удалите его, чтобы он не продолжал постепенно занимать место (в зависимости от того, сколько изменений накопится после создания).


    btrfs subvolume delete /mnt/snapshots/rootfs-transfer
zfs destroy zpool/rootfs@transfer