Jiří Meitner | IT služby Linux | Kladno | Česko

Širokoúhlá ilustrace zobrazující technického profesionála u počítačového pracoviště, zabývajícího se procesem klonování disku

Jak rychle a efektivně naklonovat disk na menší

Určitě jste slyšeli poučky, že pro klonování disků musíte vždy použít větší disk, než je ten původní. V tomto článku si ukážeme, že to není vždy tak docela pravda. Cest, jak zálohovat disk na menší je vícero, můžeme použít rsync, nebo třeba BTRFS.

BTRFS je úžasný souborový systém, díky kterému je zálohování rychlé a efektivní. Zálohování funguje prostřednictvím snapshotů, které můžeme odeslat na jiný disk nebo přes internet zabezpečeně skrze SSH.

Jak a co zálohovat

Z jaké strany to ale uchopit? Záleží na účelu. Pro domácí počítače jsou důležité fotky, dokumenty, rodinná videa, systém. Firmy a banky zajímají dost logy, ale zpravidla je nenechávají válet na serverech, ale zpracovávají jinde. V dnešním článku si ukážeme, jak zazálohovat systém, v tomto konkrétním příkladu je systém myšlen systém bez domovské složky uživatele, ale s oddělenými /var/log, /var/cache a pár dalšími. Proč ne /home? Domovskou složku si samozřejmě snapshotuji také, ale jiným scriptem, s jinou frekvencí a jinam. Pořídil jsem si nicméně nové disky a řešil jsem, jak naklonovat systém Linux Ubuntu na nový disk.

Proč zálohovat na menší disk? Nikdo si nekupuje menší disky!

Ano, zpravidla si všichni pořizujeme větší a větší disky, ale občas potřebujeme filesystémy přeorganizovat, na chvilku uklidit sem, abychom mohli přesunout něco jiného. Podstatné je, že s btrfs to je možné. Reálně přenesete jenom data a metadata.

Jdeme trošku hlouběji

V Btrfs je snapshot považován za speciální druh subvolume. Technicky vzato je snapshot v Btrfs read-write nebo read-only subvolume, které je úplnou kopií jiného subvolume v určitém čase. Snímky jsou výhodné, protože jsou vytvořeny okamžitě a počátečně nezabírají žádné další místo na disku. Další místo na disku je zapotřebí až s ukládáním změn vzhledem k původnímu stavu subvolume, od kterého byl snapshot vytvořen.

Při použití snapshotů pro zálohování můžeme vytvořit úvodní snapshot (úplný obraz subvolume) a poté použít incrementální zálohování, což znamená, že každá další záloha obsahuje pouze změny, které nastaly od posledního snapshotu. To šetří čas i místo na disku. To ale dělat nebudeme, jde nám o to přece naklonovat systémový disk na menší v prvé řadě. Nejsme lamy a umíme systém reinstalovat, když je třeba. Důležitá data máme v domovském adresáři, který si ukážeme, jak zálohovat, někdy příště.

Než vytvoříme snímek

Než vytvoříme snapshot, nejprve si určíme, kde budeme systémové snímky uchovávat v rámci btrfs odílu /. Zvolil jsem /.backup/. Nebudeme ale vytvářet /.backup/ jako složku, ale jako subvolume.

btrfs subvolume create /.backup/

Nyní máme místo, kam můžeme snapshotovat. Když budeme dělat snímek systému, obsah subvolumů se nezahrne do snímků. Proto mám /var/cache a /var/log jako samostatné subvolumy, protože nepotřebuji uchovávat jejich data tak pečlivě, jako zbytek systému. Logy se neustále mění a znamenalo by to zbytečné pro mne plýtvání místem. Nicméně při migraci na jiný disk je budeme snapshotovat taky.

Vytváříme první snímek systému

btrfs subvolume snapshot / /.backup/zaloha_systemuCode language: JavaScript (javascript)

Takovíto snapshot by nebyl pro zálohování na jiný disk dostatečný, protože by nebyl read-only. Pro přenášení snapshotů na jiné disky, oddíly či počítače se používá btrfs send a btrfs receive. Ty vyžadují, aby snapshot byl při přenosu jen pro čtení.

Další věc, co je důležité mít na paměti, je časové razítko. Pokud budete mít více snapshotů, budete chtít je spravovat, třeba mazat po určité době starší snapshoty než 30 dní, tak potřebujete vědět, kdy byl snímek vytvořen. Pokud si myslíte, že když vytvoříte snapshot, tak že získá automaticky časovou značku ze dne vytvoření snímku, tak jste na omylu. Časovou značku dědí. Ale my využijeme toho, že máme snímek zatím i pro zápis, a časovou značku mu nastavíme:

touch /.backup/zaloha_systemu

Zamknutí snímku, aby byl jen pro čtení

Nyní je na čase nastavit snímek jen pro čtení, abychom mohli jeho data zazálohovat.

btrfs property set -ts /.backup/zaloha_systemu ro trueCode language: JavaScript (javascript)

Nyní zjistíme, kolik dat zhruba potřebujeme uložení snapshotu na novém disku:

df -h /
Souborový systém Velikost Užito Volno Uži% Připojeno do
/dev/nvme1n1p2       140G   32G  108G  23% /Code language: JavaScript (javascript)

Vidíme, že v mém případě potřebuji minimálně 32G na systém, postačí nám tedy připojit třeba 60G SSD a pohodlně se vejdeme. Připojíme prázdný disk a přes gnome disky nebo přes command line nástroje vytvoříme GPT tabulku disku. Dejme tomu, že se jedná o disk /dev/sde.

Než začneme vytvářet btrfs oddíl, neměli bychom zapomenout na důležitou věc, a to je EFI oddíl. Vytvoříme prázdný oddíl o velikosti, která je o něco málo větší, než původní EFI oddíl. Chceme totiž, aby náš disk byl skutečný klon, který je schopen nabootovat. Nebudeme ho formátovat, ale v gnome diskách uděláme obraz původního EFI, který obnovíme do nového oddílu. V tuto chvíli ale velikost souborového systému fat32 nekoresponduje s o trošku větším oddílem, proto zapneme gparted, pravým tlačítkem vybereme EFI oddíl na novém disku a vybereme zkontrolovat. Gparted automaticky zarovná filesystém na velikost partišny.

Nyní pomocí svého oblíbeného nástroje vytvoříme za EFI oddílem btrfs partici, která bude mít velikost, jakou potřebujeme, alespoň 40G, jestli se nám tam mají vlézt i logy a pod.

Nyní můžeme připojit btrfs oddíl:

mkdir /mnt/backup; mount /dev/sde2 /mnt/backup -v

Připojeno. Nyní můžeme poslat snímek.

btrfs send /.backup/zaloha_systemu|btrfs receive /mnt/backup

Abychom subvolume správně přejmenovali, tak použijeme zase přepnutí na read only a další snapshot. Ten ale již nezabírá místo, jako /mnt/backup/zaloha-systemu.

btrfs property set -ts /mnt/backup/zaloha-systemu ro trueCode language: JavaScript (javascript)
btrfs subvolume snapshot /mnt/backup/zaloha-systemu /mnt/backup/@
btrfs subvolume delete /mnt/backup/zaloha-systemuCode language: JavaScript (javascript)

Nyní máte naklonovaný disk, který obsahuje systém a efi a měl by být schopen bootovat. Pro bootování budete muset aktualizovat buď UUID systému v /mnt/backup/@/etc/fstab a ujistit se, že je tam nastaveno subvolume=@ a aktuální UUID. Nebo můžete UUID nastavit dle původního disku:

Nyní nástřel kompletního skriptu, který jsem strukturoval pro lepší přehlednost a snadnější hledání chyb do funkcí a tasků.

#!/bin/bash
if [[ "$(id -u)" = 0 ]]; then
######################################################
DATE="$(date +%y%m%d%H%M)"
snapshots="/.backup/"
dest=/mnt/backup/
declare -A subvolumes=(
  ["machines"]="${dest}/@/var/lib"
  ["portables"]="${dest}/@/var/lib"
  ["log"]="${dest}/@/var"
  ["cache"]="${dest}/@/var"
)
######################################################
created_snapshots=()

NC='\033[0m'
# REGULAR COLORS
RED='\033[0;31m'          # RED
GREEN='\033[0;32m'        # GREEN
YELLOW='\033[0;33m'       # YELLOW
BLUE='\033[0;34m'         # BLUE

STDERR()
{
echo -en "${RED}"
cat - 1>&2
echo -en "${NC}"
}

OK()
{
echo -en "${GREEN}"
cat - 1>&2
echo -en "${NC}"
}

WARN()
{
echo -en "${YELLOW}"
cat - 1>&2
echo -en "${NC}"
}

BLUE()
{
echo -en "${BLUE}"
cat - 1>&2
echo -en "${NC}"
}

OLD_TIMESTAMP()
{
btrfs subvolume show "${1}"|grep 'Creation time' \
  |awk '{print $3, $4}'|tr -d ': -'|cut -b1-12
}

01_check_if_snapshots_subvolume_exist()
{
snapshots_subvolume=${snapshots%/}
if btrfs subvolume list "${snapshots_subvolume}"|grep "${snapshots_subvolume}" --color; then
   echo "snapshot subvolume existuje"|OK; else
   if btrfs subvolume create "${snapshots_subvolume}"; then
      echo "${snapshots_subvolume} vytvořeno"|OK; else
      echo "${snapshots_subvolume} se nepodařilo vytvořit"|STDERR
      return 1
   fi
fi
}

02_check_if_dest_filesystem_is_mounted()
{
if df -h "${dest}"|grep "${dest%/}" --color; then
   echo "${dest} je připojeno"|OK; else
   echo "${dest} není připojeno"|WARN
   if (mount "${dest}" -v); then
       echo "${dest} se podařilo připojit"|OK; else
       echo "${dest} se nepodařilo připojit"|STDERR
       return 2
   fi
fi
}

03_create_root_snapshot()
{
INPUT=/
OUTPUT="${snapshots}/${DATE}"
if btrfs subvolume snapshot "${INPUT}" "${OUTPUT}"; then
   echo 'root snapshot vytvořen'|OK; else
   echo 'root snapshot se nepodařilo vytvořit'|STDERR
   return 3
fi
if ! touch "${OUTPUT}"; then
     echo "touch ${OUTPUT} selhal"|STDERR
     return 3
fi
if ! btrfs property set -ts "${OUTPUT}" ro true; then
     echo "btrfs property set -ts ${OUTPUT} ro true selhal"|STDERR
     return 3
fi
}

04_create_others_snaphots()
{
for subvolume in "${!subvolumes[@]}"; do
    full_path_name="${subvolumes[$subvolume]}/${subvolume}" #plná cesta včetně /@/
    INPUT=$(echo "${full_path_name}"|cut -f2 -d@)
    OUTPUT="${snapshots}/${DATE}$(echo "${INPUT}"|tr -s '/_' '_'|sed 's/_$//')"
    if btrfs subvolume snapshot "${INPUT}" "${OUTPUT}"; then
       echo "${OUTPUT} snapshot vytvořen"|OK; else
       echo "${OUTPUT} snapshot se nepodařilo vytvořit"|STDERR
       return 4
    fi
    if ! touch "${OUTPUT}"; then
         echo "touch ${OUTPUT} selhal"|STDERR
         return 4
    fi
    if ! btrfs property set -ts "${OUTPUT}" ro true; then
         echo "btrfs property set -ts ${OUTPUT} ro true selhal"|STDERR
         return 4
    fi
done
}

05_delete_old_snapshots_in_snapshots_subvolume()
{
find "${snapshots}" -mindepth 1 -maxdepth 1 -type d -mtime +30 -exec btrfs subvolume delete {} \;
}

06_fill_array_created_snapshots_and_root_snapshot_variable()
{
if ! mapfile -t created_snapshots < <(find "${snapshots}" -mindepth 1 -maxdepth 1 -type d -name "${DATE}*" \! -name "${DATE}" -exec basename {} \;); then
     echo "pole created_snapshots se nepodařilo naplnit"|STDERR
     return 5
fi
if ! root_snapshot=$(find "${snapshots}" -mindepth 1 -maxdepth 1 -type d -name "${DATE}" -exec basename {} \;); then
     echo "proměnnou root_snapshot se nepodařilo naplnit"|STDERR
     return 5
fi
}

07_migrate_root_snapshot()
{
if btrfs send "${snapshots}/${root_snapshot}"|btrfs receive "${dest}"; then
   echo "${snapshots}/${root_snapshot} root_snapshot se podařilo odeslat a přijmout"|OK; else
   echo "${snapshots}/${root_snapshot} root_snapshot se nepodařilo odeslat a přijmout"|STDERR; return 6
fi
if ! btrfs property set -ts "${dest}/${DATE}" ro true; then echo "btrfs property set -ts ${dest}/${DATE} ro true selhal"|STDERR; return 6; fi
if btrfs  subvolume show "${dest}/@"; then
   echo "${dest}/@ již existuje"|WARN;
   old_timestamp=$(OLD_TIMESTAMP "${dest}/@")
   echo "budu přesunovat ${dest}/@ do ${dest}/@old_${old_timestamp}"|WARN
   if btrfs property set -ts "${dest}/@" ro true && \
      btrfs snapshot create "${dest}/@" "${dest}/@old_${old_timestamp}" && \
      btrfs subvolume delete "${dest}/@"; then
      echo "přesun se podařil"|OK; else
      echo "přesun se nezdařil"|STDERR
      return 6
   fi; else
   echo "${dest}/@ ještě neexistuje, budu ho vytvářet"|OK
fi
if ! btrfs subvolume snapshot "${dest}/${DATE}" "${dest}/@"; then echo "btrfs subvolume snapshot ${dest}/${DATE} ${dest}/@ selhal"|STDERR; return 6; fi
if ! btrfs subvolume delete "${dest}/${DATE}"; then echo "btrfs subvolume delete ${dest}/${DATE} selhal"|STDERR; return 6; fi
}

08_migrate_other_snapshots()
{
for snapshot in "${created_snapshots[@]}"; do
    if btrfs send "${snapshots}/${snapshot}"|btrfs receive "${dest}/@/"; then
       echo "${snapshots}/${snapshot} se podařilo odeslat a přijmout"|OK; else
       echo "${snapshots}/${snapshot} se nepodařilo odeslat a přijmout"|STDERR
       return 7
    fi
    if ! btrfs property set -ts "${dest}/@/${snapshot}" ro true; then
       echo "btrfs property set -ts ${dest}/@/${snapshot} ro true selhal"|STDERR
       return 7
    fi
    new_name=$(echo "${snapshot}"|sed "s/^${DATE}/@/"|tr -s '_' '/')
    echo "new_name: ${new_name}"|WARN
    if ! btrfs subvolume snapshot "${dest}/@/${snapshot}" "${dest}/${new_name}"; then
         echo "btrfs subvolume snapshot ${dest}/@/${snapshot} ${dest}/${new_name} selhal"|STDERR
         return 7
    fi
    if ! btrfs subvolume delete "${dest}/@/${snapshot}"; then
         echo "btrfs subvolume delete ${dest}/@/${snapshot} selhal"|STDERR
         return 7
    fi
done
}

09_create_backup_subvolume()
{
if ! btrfs subvolume list "${dest}/@/${snapshots}"; then
   if ! btrfs subvolume create "${dest}/@/${snapshots}"; then
      echo "btrfs subvolume create ${dest}/@/${snapshots} selhal"|STDERR
      return 8
   fi
fi
}

TASKS=(
01_check_if_snapshots_subvolume_exist
02_check_if_dest_filesystem_is_mounted
03_create_root_snapshot
04_create_others_snaphots
05_delete_old_snapshots_in_snapshots_subvolume
06_fill_array_created_snapshots_and_root_snapshot_variable
07_migrate_root_snapshot
08_migrate_other_snapshots
09_create_backup_subvolume
)

echo "Budu dělat následující tasky:"|BLUE
printf "%s\n" "${TASKS[@]}"|WARN
for task in "${TASKS[@]}"; do
echo "${task}"|BLUE
if ! "${task}"; then
   echo "${task} selhal, končím"|STDERR; exit; else
   echo "${task} úspěšně dokončen."|OK
fi
echo
done
######################################################
else
sudo /bin/bash -c "$0"
fi
exitCode language: PHP (php)
Summary
Jak rychle a efektivně naklonovat disk na menší
Article Name
Jak rychle a efektivně naklonovat disk na menší
Description
Určitě jste slyšeli poučky, že pro klonování disků musíte vždy použít větší disk, než je ten původní. V tomto článku si ukážeme, že to není vždy tak docela pravda. Cest, jak zálohovat disk na menší je vícero, můžeme použít rsync, nebo třeba BTRFS.
Author
Publisher Name
ITisLove.cz
Publisher Logo

Comments

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..