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_systemu
Code 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 true
Code 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 true
Code language: JavaScript (javascript)
btrfs subvolume snapshot /mnt/backup/zaloha-systemu /mnt/backup/@
btrfs subvolume delete /mnt/backup/zaloha-systemu
Code 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
exit
Code language: PHP (php)
Kdo jsem:
IT nadšenec žijící v Kladně, system engineer, sysadmin, správce informačních systémů.
Proč jsem si založil tento web: někdo si píše deníček, někdo hraje hry, mne linux a svobodný software nepouští ani ve volném čase. Takže sem občas hodím nějakou svojí poznámku, která někomu možná pomůže.
Navíc, kdyby někdo potřeboval pomoc, tak mne může díky webu najít.
S čím vám mohu pomoci:
Počítač: Rád Vám pomohu s omlazením vašeho počítače (desktopu, notebooku) nebo sestavím nový.
Server: Chcete mít vlastní webový server, NAS (síťové úložiště), VPN server nebo CLOUD? Provozovat e-shop? I v tom vám mohu pomoci.
Domácí síť: Potřebujete vyřešit domácí síť, nejste spokojeni s rychlostí a kvalitou WiFi? Potřebujete připojit zařízení k VPN?
Počítač pro home office: Potřebujete nutně počítač pro home office, ale nechcete platit moc?
Počítač pro online výuku: Jste učitel nebo žák/student, či máte doma někoho, kdo je? Nemáte na čem pracovat? Pomohu vám to vyřešit.
Mám rád počítače a moderní technologie, Linux, zejména distribuce Ubuntu a Debian, CentOS a Rhel mi také nejsou cizí. Ve své práci se starám o více než 6000 linuxových serverů (Ubuntu, Red Hat Enterprise Linux, CentOS).
V minulosti jsem kromě správy linuxových serverů získal zkušenosti i se správou Windows Serverů a instalací a konfigurací linuxových a windowsových terminálů (například na docházku, nebo pro jídelny).
Jiří Meitner
IČ: 73763004
Fyzická osoba zapsaná v živnostenském rejstříku
podpora@ITisLove.cz
WhatsApp
Karla Tomana 830
Kladno
Středočeský kraj
272 04
Česko
Systémový inženýr, Linux administáror, správce informačních systémů, admin, IT specialista, IT help
Napsat komentář