Disassembler

Artificial intelligence is no match for natural stupidity.
26dubna2016

Pohádka o klonovaných discích s LVM


Tak jsem dnes po dlouhé době zase objevil jeden kouzelný příkaz, který jedním vrzem udělá automaticky všechno, co potřebuju, a ještě mi k tomu málem uvaří kafe, utře prach a zaleje kvítka. Tentokrát se jedná o příkaz z diskově administračního soudku, který jsem využil při obnově dat z klonovaných LVM oddílů ve virtuálních strojích.

Bylo nebylo


Za devatero horami, devatero řekami a devatero lesy žil jeden král, který měl veškeré své království na virtuálkách ve VMWare. Jelikož bylo celé prostředí ve fázi vývoje a střídavě se na něm dolaďovaly a testovaly různé věci, obsah disků se pomalu, ale jistě měnil. Jednoho krásného dne si král uvědomil, že při testování udělal strašnou botu, a protože zálohování bylo řešeno pomocí snapshotů VM, usmyslel si, že se budou obnovovat snapshoty. Protože ale byly veškeré úkony koordinovány včeličkou projekťačkou, která měla pocit, že jejím jediným úkolem je snášet zákazníkovi modré z nebe v rekordních časech, nikdo se trpaslíčků neptal, jestli za těch 14 dní náhodou nevybudovali něco, co by stálo za to uchovat a na obnovené virtuálky nahrát zpátky. Všechno se muselo udělat ASAP, protože to včelička králi už commitovala. Trpaslíčci se tedy chopili svých konzolek, virtuálky postupně vypnuli a ve stávajícím stavu je odpojili od sítě a zkopírovali na pomocné úložiště, pro případ, kdyby se je náhodou rozmrzelý král rozhodl obšťastnit takovou kratochvílí, jako je třeba obnova obnovy. I stalo se tedy, že hromada konfigurací a podpůrných skriptů zmizela v propadlišti /dev/nullu. Ve chvíli, kdy král po obnově dotestoval a stvrdil svou spokojenost krátkou depeší, trpaslíčci si uvědomili, že jim na virtuálkách najednou něco schází. Začali láteřit a hrozit klávesničkami, když v tom si jeden z nich vzpomněl, že původní virtuálky jsou ještě někde zašité a disky by se daly namountovat a data z nich vykutat.

A teď vážně


Prosté rozjetí původních virtuálek paralelně s obnovenými by představovalo riziko, protože na nich jedou různé joby, komunikují s různými webovými službami a monitoring na nich hlídá všelijaké anomálie. A obnovená virtuálka je samozřejmě produkční (i přesto, že se stále jedná o projekt), takže není možné ji vypnout nebo jakkoliv, byť i jen dočasně, narušit její provoz. Vypadalo, že cestou nejmenšího odporu je tedy přehození disku, kdy by se disk z původní virtuálky za běhu namapoval v obnovené a namountoval na nějaký dočasný mountpoint. Data by se z něj pak jednoduše překopírovala a disk by se zas jen odpojil a zahodil.

Zkusmé přehození virtuálního disku z původního image do obnoveného ukázalo, že na úrovni hypervizoru je to jednoduché jak facka. První oříšek pak přišel po přihlášení do virtuálky samotné. Disk nebyl vidět. Jedná se o stádo Red Hatů v minimální instalaci, takže v tuhle chvíli problémem bylo, jak nově připojený disk vnutit systému. Naštěstí jsou všechny virtuální disky připojeny přes SCSI, takže jednoduché prošťouchnutí sběrnice tenhle problém vyřešilo.

# echo "- - -" > /sys/class/scsi_host/host0/scan

Pak ovšem přišel na řadu další a mnohem nebezpečnější problém. Ukradením disku ze staré virtuálky a připojením do nové došlo k časovému paradoxu a oba disky se anihilovaly. No dobře, k tomu nedošlo, ale bylo by to daleko zajímavější, ne? K čemu ale skutečně došlo, je, že veškeré identifikátory svazku a oddílů teď byly duplikovány. Takže v tuto chvíli je systém dokonale zmaten, protože naprosto bezpečně ví, že nic nepil, ale přesto vidí dvakrát. Všechny disky používají LVM a to teď při pokusu o výpis jednotek začalo hlásit něco takového:

[root@localhost ~]# pvs
  Found duplicate PV KrDHIMuT1k4hGoMcZZffGdjl5xuKHxQj: using /dev/sda2 not /dev/sdb2
  Using duplicate PV /dev/sda2 without holders, replacing /dev/sdb2

Jediné štěstí bylo, že se druhý disk přidával za běhu, protože pokud by se přidal v zastaveném stavu a virtuálka se pak spustila, použil by se původní disk a nikoliv ten obnovený, což by třeba v případě oddílu s databází mohlo být docela zlé. Takže jak z toho ven? Potřeboval bych nějaké čáry a kouzla, které by mi změnily UUID svazku a ideálně jedním vrzem i UUID a název volume grupy, aby nedošlo ke konfliktu názvů VG a LV. A tím se konečně dostávám ke zlatému hřebu tohoto článku. On takový příkaz totiž opravdu existuje. Je jím vgimportclone a jak i název může napovídat, slouží přesně k takovému úkonu, který se chystám provést. Na rozdíl od jiných příkazů pro modifikaci LVM objektů přijímá až směšně málo parametrů, takže celé zaklínadlo pak vypadá jednoduše takto:

# vgimportclone -n nova_vg konfliktni_pv

Pak už stačí jen oddíly zaktivovat pomocí

# lvchange -a y nova_vg

A může se vesele mountovat.

Transkript


Příkaz vgimportclone produkuje na první pohled docela děsivý výstup, ale není třeba se bát, protože všechny změny probíhají s vypnutým LVM metadata daemonem, takže je neregistruje mapper a projeví se až nakonec, až příkaz dokončí celou rošádu. Ještě by se slušelo podotknout, že disk se změněnými UUID samozřejmě nepůjde jednoduše vrátit zpět do původního stavu. Dalo by se zde sice čarovat s vgcfgbackup a vgcfgrestore, ale v mém případě se jednalo pouze o jednorázovou operaci, bez předpokladu, že se původní disk musí zachovat stále funkční v původní VM. Na testovacím prostředí pak výstupy příkazů v jednotlivých fázích vypadají takto:

# Initial state
[root@localhost ~]# pvs
  PV         VG     Fmt  Attr PSize PFree
  /dev/sda2  rootvg lvm2 a--  9.47g    0
[root@localhost ~]# vgs
  VG     #PV #LV #SN Attr   VSize VFree
  rootvg   1   2   0 wz--n- 9.47g    0
[root@localhost ~]# lvs
  LV   VG     Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root rootvg -wi-ao---- 8.47g
  swap rootvg -wi-ao---- 1.00g


# Attach the cloned drive and rescan SCSI
[root@localhost ~]# echo "- - -" > /sys/class/scsi_host/host0/scan


# Before vgimportclone
[root@localhost ~]# pvs
  Found duplicate PV KrDHIMuT1k4hGoMcZZffGdjl5xuKHxQj: using /dev/sda2 not /dev/sdb2
  Using duplicate PV /dev/sda2 without holders, replacing /dev/sdb2
  PV         VG     Fmt  Attr PSize PFree
  /dev/sda2  rootvg lvm2 a--  9.47g    0
[root@localhost ~]# vgs
  Found duplicate PV KrDHIMuT1k4hGoMcZZffGdjl5xuKHxQj: using /dev/sda2 not /dev/sdb2
  Using duplicate PV /dev/sda2 without holders, replacing /dev/sdb2
  VG     #PV #LV #SN Attr   VSize VFree
  rootvg   1   2   0 wz--n- 9.47g    0
[root@localhost ~]# lvs
  Found duplicate PV KrDHIMuT1k4hGoMcZZffGdjl5xuKHxQj: using /dev/sda2 not /dev/sdb2
  Using duplicate PV /dev/sda2 without holders, replacing /dev/sdb2
  LV   VG     Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root rootvg -wi-ao---- 8.47g
  swap rootvg -wi-ao---- 1.00g


# Magic
[root@localhost ~]# vgimportclone -n newvg /dev/sdb2
  Found duplicate PV KrDHIMuT1k4hGoMcZZffGdjl5xuKHxQj: using /dev/sda2 not /dev/sdb2
  Using duplicate PV /dev/sda2 without holders, replacing /dev/sdb2
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  WARNING: Activation disabled. No device-mapper interaction will be attempted.
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  Physical volume "/tmp/snap.QStnCdJU/vgimport0" changed
  1 physical volume changed / 0 physical volumes not changed
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  WARNING: Activation disabled. No device-mapper interaction will be attempted.
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  Volume group "rootvg" successfully changed
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  WARNING: lvmetad is running but disabled. Restart lvmetad before enabling it!
  Volume group "rootvg" successfully renamed to "newvg"
Notifying lvmetad about changes since it was disabled temporarily.
(This resolves any WARNING message about restarting lvmetad that appears above.)
  Reading all physical volumes.  This may take a while...
  Found volume group "rootvg" using metadata type lvm2
  Found volume group "newvg" using metadata type lvm2


# After vgimportclone
[root@localhost ~]# vgs
  VG     #PV #LV #SN Attr   VSize VFree
  newvg    1   2   0 wz--n- 9.47g    0
  rootvg   1   2   0 wz--n- 9.47g    0
[root@localhost ~]# pvs
  PV         VG     Fmt  Attr PSize PFree
  /dev/sda2  rootvg lvm2 a--  9.47g    0
  /dev/sdb2  newvg  lvm2 a--  9.47g    0
[root@localhost ~]# vgs
  VG     #PV #LV #SN Attr   VSize VFree
  newvg    1   2   0 wz--n- 9.47g    0
  rootvg   1   2   0 wz--n- 9.47g    0
[root@localhost ~]# lvs
  LV   VG     Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root newvg  -wi------- 8.47g
  swap newvg  -wi------- 1.00g
  root rootvg -wi-ao---- 8.47g
  swap rootvg -wi-ao---- 1.00g

No a raději se mě neptejte, proč se vlastně obnova virtuálek dělala zrovna takto, když existuje tisíc a jeden jiných a pravděpodobně i lepších způsobů. Na to já jsem malý pán.