Que teñen en común LVM e Matryoshka?

Bo día.
Gustaríame compartir coa comunidade a miña experiencia práctica na construción dun sistema de almacenamento de datos para KVM usando md RAID + LVM.

O programa incluirá:

  • Construíndo md RAID 1 desde NVMe SSD.
  • Montaxe de md RAID 6 desde SSD SATA e unidades normais.
  • Características da operación TRIM/DISCARD en SSD RAID 1/6.
  • Creación dunha matriz md RAID 1/6 de arranque nun conxunto común de discos.
  • Instalar o sistema en NVMe RAID 1 cando non hai soporte NVMe na BIOS.
  • Usando caché LVM e LVM thin.
  • Usando instantáneas BTRFS e enviar/recibir para copia de seguridade.
  • Usando instantáneas finas de LVM e thin_delta para copias de seguridade de estilo BTRFS.

Se estás interesado, consulta cat.

Afirmación

O autor non se fai responsable das consecuencias do uso ou non de materiais/exemplos/códigos/suxestións/datos deste artigo. Ao ler ou utilizar este material de calquera xeito, asume a responsabilidade de todas as consecuencias destas accións. As posibles consecuencias inclúen:

  • SSD NVMe fritos.
  • Recurso de gravación completamente esgotado e falla das unidades SSD.
  • Perda total de todos os datos en todas as unidades, incluídas as copias de seguridade.
  • Hardware informático defectuoso.
  • Perda de tempo, nervios e cartos.
  • Calquera outra consecuencia que non estea enumerada anteriormente.

Ferro

Dispoñibles estaban:

Placa base de aproximadamente 2013 con chipset Z87, completa con Intel Core i7 / Haswell.

  • Procesador 4 núcleos, 8 fíos
  • 32 GB de RAM DDR3
  • 1 x 16 ou 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 conectores SATA 6 de 3 GBps

O adaptador SAS LSI SAS9211-8I pasou ao modo IT/HBA. O firmware habilitado para RAID foi substituído intencionadamente por firmware HBA para:

  1. Podes tirar este adaptador en calquera momento e substituílo por calquera outro que atopes.
  2. TRIM/Discard funcionou normalmente nos discos, porque... no firmware RAID estes comandos non son compatibles en absoluto e ao HBA, en xeral, non lle importa que comandos se transmitan a través do bus.

Discos duros: 8 pezas de HGST Travelstar 7K1000 cunha capacidade de 1 TB nun factor de forma 2.5, como para portátiles. Estas unidades estaban anteriormente nunha matriz RAID 6. Tamén terán un uso no novo sistema. Para almacenar copias de seguridade locais.

Adicionalmente engadiu:

6 unidades SATA SSD modelo Samsung 860 QVO 2TB. Estes SSD requirían un gran volume, a presenza dunha caché SLC, a fiabilidade e un prezo baixo. Necesítase soporte para descartar/cero, que está marcado pola liña en dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

2 unidades de SSD NVMe modelo Samsung SSD 970 EVO 500GB.

Para estes SSD, a velocidade de lectura/escritura aleatoria e a capacidade de recursos para as túas necesidades son importantes. Radiador para eles. Necesariamente. Absolutamente. En caso contrario, fritilos ata que estean crocantes durante a primeira sincronización RAID.

Adaptador StarTech PEX8M2E2 para 2 SSD NVMe instalados en ranura PCIe 3.0 8x. Este, de novo, é só un HBA, pero para NVMe. Diferénciase dos adaptadores baratos en que non require soporte de bifurcación PCIe da placa base debido á presenza dun interruptor PCIe incorporado. Funcionará incluso no sistema máis antigo con PCIe, aínda que sexa un slot PCIe 1 x1.0. Por suposto, á velocidade adecuada. Non hai RAID alí. Non hai unha BIOS incorporada a bordo. Entón, o teu sistema non aprenderá máxicamente a arrincar con NVMe, e moito menos a NVMe RAID grazas a este dispositivo.

Este compoñente debeuse unicamente á presenza dun só 8x PCIe 3.0 gratuíto no sistema e, se hai 2 slots libres, pódese substituír facilmente por dous centavos PEX4M2E1 ou análogos, que se poden mercar en calquera lugar a un prezo de 600 euros. rublos.

O rexeitamento de todo tipo de hardware ou RAID de chipset/BIOS incorporado fíxose deliberadamente, para poder substituír por completo todo o sistema, con excepción dos propios SSD/HDD, conservando todos os datos. O ideal é que poida gardar incluso o sistema operativo instalado cando se mude a un hardware completamente novo/diferente. O principal é que hai portos SATA e PCIe. É como un CD en directo ou unha unidade flash de arranque, só que é moi rápido e un pouco voluminoso.

HumorEn caso contrario, xa sabes o que pasa; ás veces necesitas levar a matriz enteira contigo con urxencia para levar. Pero non quero perder datos. Para iso, todos os medios mencionados están convenientemente situados nas diapositivas nas bahías 5.25 da caixa estándar.

Ben, e, por suposto, para experimentar con diferentes métodos de almacenamento na caché SSD en Linux.

As incursións de hardware son aburridas. Accéndeo. Ou funciona ou non. E con mdadm sempre hai opcións.

Software

Anteriormente, Debian 8 Jessie instalouse no hardware, que está preto de EOL. RAID 6 foi montado a partir dos discos duros mencionados anteriormente emparejados con LVM. Executaba máquinas virtuais en kvm/libvirt.

Porque O autor ten unha experiencia adecuada na creación de unidades flash SATA/NVMe de arranque portátiles e, ademais, para non romper o modelo apt habitual, escolleuse Ubuntu 18.04 como sistema de destino, que xa estivo suficientemente estabilizado, pero aínda ten 3 anos de apoio no futuro.

O mencionado sistema contén todos os controladores de hardware que necesitamos fóra da caixa. Non necesitamos ningún software ou controladores de terceiros.

Preparándose para a instalación

Para instalar o sistema necesitamos Ubuntu Desktop Image. O sistema do servidor ten algún tipo de instalador vigoroso, que mostra unha independencia excesiva que non se pode desactivar colocando a partición do sistema UEFI nun dos discos, estropeando toda a beleza. Polo tanto, só se instala no modo UEFI. Non ofrece ningunha opción.

Non estamos contentos con isto.

Por que?Desafortunadamente, o arranque UEFI é moi mal compatible co software de arranque RAID, porque... Ninguén nos ofrece reservas para a partición UEFI ESP. Hai receitas en liña que suxiren colocar a partición ESP nunha unidade flash nun porto USB, pero este é un punto de falla. Hai receitas que usan o software mdadm RAID 1 con metadatos versión 0.9 que non impiden que a UEFI BIOS vexa esta partición, pero esta vive ata o momento feliz en que a BIOS ou outro SO de hardware escribe algo no ESP e esquécese de sincronizalo con outros. espellos.

Ademais, o arranque UEFI depende da NVRAM, que non se moverá xunto cos discos ao novo sistema, porque forma parte da placa base.

Polo tanto, non imos reinventar unha nova roda. Xa temos unha bicicleta do avó preparada e probada no tempo, agora chamada Legacy/BIOS boot, que leva o orgulloso nome de CSM nos sistemas compatibles con UEFI. Bastarémolo sacar do estante, lubricalo, bombear os pneumáticos e limpalo cun pano húmido.

A versión de escritorio de Ubuntu tampouco se pode instalar correctamente co cargador de arranque Legacy, pero aquí, como din, polo menos hai opcións.

E así, recollemos o hardware e cargamos o sistema desde a unidade flash de arranque de Ubuntu Live. Teremos que descargar paquetes, polo que configuraremos a rede que che funcione. Se non funciona, pode cargar os paquetes necesarios nunha unidade flash con antelación.

Entramos no entorno de Escritorio, iniciamos o emulador de terminal e imos:

#sudo bash

Como...?A liña anterior é o detonante canónico de holiwars sobre sudo. C bоveñen maiores oportunidades eоmaior responsabilidade. A pregunta é se podes asumilo por ti mesmo. Moita xente pensa que usar sudo deste xeito polo menos non é coidadoso. Non obstante:

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc

Por que non ZFS...?Cando instalamos software no noso ordenador, esencialmente prestamos o noso hardware aos desenvolvedores deste software para que o conduzan.
Cando confiamos neste software a seguridade dos nosos datos, contratamos un préstamo igual ao custo de restauración destes datos, que teremos que pagar algún día.

Desde este punto de vista, ZFS é un Ferrari, e mdadm+lvm é máis como unha bicicleta.

Subxectivamente, o autor prefire prestar unha bicicleta a crédito a persoas descoñecidas en lugar dun Ferrari. Alí, o prezo do tema non é alto. Sen necesidade de dereitos. Máis sinxelo que as normas de tráfico. O aparcamento é gratuíto. A capacidade de campo a través é mellor. Sempre podes unir as pernas a unha bicicleta e podes reparar unha bicicleta coas túas propias mans.

Por que entón BTRFS...?Para iniciar o sistema operativo, necesitamos un sistema de ficheiros compatible con Legacy/BIOS GRUB e, ao mesmo tempo, admite instantáneas en directo. Usarémolo para a partición /boot. Ademais, o autor prefire usar este FS para / (root), sen esquecerse de que para calquera outro software pode crear particións separadas en LVM e montalas nos directorios necesarios.

Non almacenaremos ningunha imaxe de máquinas virtuais ou bases de datos neste FS.
Este FS só se utilizará para crear instantáneas do sistema sen apagalo e despois transferir estas instantáneas a un disco de copia de seguridade mediante enviar/recibir.

Ademais, o autor xeralmente prefire manter un mínimo de software directamente no hardware e executar todo o outro software en máquinas virtuais usando cousas como o reenvío de GPU e controladores de host PCI-USB a KVM a través de IOMMU.

O único que queda no hardware son o almacenamento de datos, a virtualización e a copia de seguridade.

Se confías máis en ZFS, entón, en principio, para a aplicación especificada son intercambiables.

Non obstante, o autor ignora deliberadamente as funcións de espello/RAID e redundancia incorporadas que teñen ZFS, BRTFS e LVM.

Como argumento adicional, BTRFS ten a capacidade de converter as escrituras aleatorias en secuencias, o que ten un efecto moi positivo na velocidade de sincronización de instantáneas/copias de seguridade no disco duro.

Volvemos a escanear todos os dispositivos:

#udevadm control --reload-rules && udevadm trigger

Imos dar unha ollada ao redor:

#lsscsi && nvme list
[0:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdn
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 S466NXXXXXXX15L Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
/dev/nvme1n1 S5H7NXXXXXXX48N Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7

Disposición do disco

SSD NVMe

Pero non os marcaremos de ningún xeito. De todos os xeitos, a nosa BIOS non ve estas unidades. Entón, irán totalmente ao software RAID. Nin sequera crearemos seccións alí. Se queres seguir o "canon" ou "principalmente", crea unha partición grande, como un disco duro.

Disco duro SATA

Non hai que inventar nada especial aquí. Crearemos unha sección para todo. Crearemos unha partición porque a BIOS ve estes discos e ata pode tentar arrincar desde eles. Incluso instalaremos GRUB nestes discos máis tarde para que o sistema poida facelo de súpeto.

#cat >hdd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sdg
unit: sectors

/dev/sdg1 : start= 2048, size= 1953523120, type=fd, bootable
EOF
#sfdisk /dev/sdg < hdd.part
#sfdisk /dev/sdh < hdd.part
#sfdisk /dev/sdi < hdd.part
#sfdisk /dev/sdj < hdd.part
#sfdisk /dev/sdk < hdd.part
#sfdisk /dev/sdl < hdd.part
#sfdisk /dev/sdm < hdd.part
#sfdisk /dev/sdn < hdd.part

SSD SATA

Aquí é onde as cousas se fan interesantes para nós.

En primeiro lugar, as nosas unidades teñen un tamaño de 2 TB. Isto está dentro do rango aceptable para MBR, que é o que usaremos. Se é necesario, pódese substituír por GPT. Os discos GPT teñen unha capa de compatibilidade que permite que os sistemas compatibles con MBR vexan as 4 primeiras particións se están situadas dentro dos 2 primeiros terabytes. O principal é que a partición de arranque e a partición bios_grub destes discos deberían estar ao principio. Isto incluso permítelle arrincar desde unidades GPT Legacy/BIOS.

Pero este non é o noso caso.

Aquí imos crear dúas seccións. O primeiro terá un tamaño de 1 GB e empregarase para RAID 1 /boot.

O segundo empregarase para RAID 6 e ocupará todo o espazo libre restante excepto unha pequena área sen asignar ao final da unidade.

Que é esta zona sen marcar?Segundo fontes da rede, os nosos SSD SATA teñen a bordo unha caché SLC expansible dinámicamente que varía en tamaño de 6 a 78 gigabytes. Obtemos 6 gigabytes "de balde" debido á diferenza entre "gigabytes" e "gibibytes" na folla de datos da unidade. Os 72 gigabytes restantes están asignados desde o espazo non utilizado.

Aquí hai que ter en conta que temos unha caché SLC e o espazo está ocupado en modo MLC de 4 bits. O que para nós significa efectivamente que por cada 4 gigabytes de espazo libre só obteremos 1 gigabyte de caché SLC.

Multiplica 72 gigabytes por 4 e obtén 288 gigabytes. Este é o espazo libre que non marcaremos para permitir que as unidades fagan un uso completo da caché SLC.

Así, conseguiremos ata 312 gigabytes de caché SLC dun total de seis unidades. De todas as unidades, 2 serán usadas en RAID para a redundancia.

Esta cantidade de caché permitiranos atoparnos moi raramente na vida real cunha situación na que unha escritura non vaia á caché. Isto compensa moi ben o inconveniente máis triste da memoria QLC: a velocidade de escritura extremadamente baixa cando os datos se escriben sen pasar pola caché. Se as túas cargas non se corresponden con isto, recoméndoche que penses moito sobre o tempo que durará o teu SSD con tal carga, tendo en conta o TBW da folla de datos.

#cat >ssd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sda
unit: sectors

/dev/sda1 : start= 2048, size= 2097152, type=fd, bootable
/dev/sda2 : start= 2099200, size= 3300950016, type=fd
EOF
#sfdisk /dev/sda < ssd.part
#sfdisk /dev/sdb < ssd.part
#sfdisk /dev/sdc < ssd.part
#sfdisk /dev/sdd < ssd.part
#sfdisk /dev/sde < ssd.part
#sfdisk /dev/sdf < ssd.part

Creando Arrays

En primeiro lugar, necesitamos cambiar o nome da máquina. Isto é necesario porque o nome de host forma parte do nome da matriz nalgún lugar dentro de mdadm e afecta a algo nalgún lugar. Por suposto, as matrices pódense renomear máis tarde, pero este é un paso innecesario.

#mcedit /etc/hostname
#mcedit /etc/hosts
#hostname
vdesk0

SSD NVMe

#mdadm --create --verbose --assume-clean /dev/md0 --level=1 --raid-devices=2 /dev/nvme[0-1]n1

Por que -asumir-limpar...?Para evitar a inicialización de matrices. Isto é válido para os niveis RAID 1 e 6. Todo pode funcionar sen inicialización se é unha matriz nova. Ademais, inicializar a matriz SSD despois da creación é un desperdicio de recursos TBW. Usamos TRIM/DISCARD sempre que sexa posible en matrices SSD ensambladas para "inicializalas".

Para as matrices SSD, RAID 1 DISCARD é compatible sen prelo.

Para as matrices SSD RAID 6 DISCARD, debes activalo nos parámetros do módulo do núcleo.

Isto só debería facerse se todas as unidades SSD utilizadas en matrices de nivel 4/5/6 neste sistema teñen compatibilidade con discard_zeroes_data. Ás veces atópase con unidades estrañas que lle indican ao núcleo que esta función está soportada, pero de feito non está aí, ou a función non sempre funciona. Polo momento, o soporte está dispoñible en case todas partes, non obstante, hai unidades antigas e firmware con erros. Por este motivo, o soporte DISCARD está desactivado por defecto para RAID 6.

Atención, o seguinte comando destruirá todos os datos das unidades NVMe ao "iniciar" a matriz con "ceros".

#blkdiscard /dev/md0

Se algo sae mal, tenta especificar un paso.

#blkdiscard --step 65536 /dev/md0

SSD SATA

#mdadm --create --verbose --assume-clean /dev/md1 --level=1 --raid-devices=6 /dev/sd[a-f]1
#blkdiscard /dev/md1
#mdadm --create --verbose --assume-clean /dev/md2 --chunk-size=512 --level=6 --raid-devices=6 /dev/sd[a-f]2

Por que tan grande...?Aumentar o tamaño do fragmento ten un efecto positivo na velocidade da lectura aleatoria en bloques ata o tamaño do fragmento incluído. Isto ocorre porque unha operación do tamaño adecuado ou menor pódese completar enteiramente nun único dispositivo. Polo tanto, resúmese o IOPS de todos os dispositivos. Segundo as estatísticas, o 99% do IO non supera os 512K.

RAID ten 6 IOPS por escritura sempre inferior ou igual ás IOPS dunha unidade. Cando, como lectura aleatoria, IOPS pode ser varias veces maior que a dunha unidade, e aquí o tamaño do bloque é de importancia fundamental.
O autor non ve o sentido de intentar optimizar un parámetro que é malo no deseño de RAID 6 e, en cambio, optimiza o que RAID 6 é bo.
Compensaremos a mala escritura aleatoria de RAID 6 cunha caché NVMe e trucos de aprovisionamento fino.

Aínda non activamos DISCARD para RAID 6. Polo que non imos "inicializar" esta matriz polo momento. Farémolo máis tarde, despois de instalar o sistema operativo.

Disco duro SATA

#mdadm --create --verbose --assume-clean /dev/md3 --chunk-size=512 --level=6 --raid-devices=8 /dev/sd[g-n]1

LVM en NVMe RAID

Para obter velocidade, queremos colocar o sistema de ficheiros raíz en NVMe RAID 1 que é /dev/md0.
Non obstante, aínda necesitaremos esta matriz rápida para outras necesidades, como intercambio, metadatos e metadatos LVM-cache e LVM-thin, polo que crearemos un LVM VG nesta matriz.

#pvcreate /dev/md0
#vgcreate root /dev/md0

Imos crear unha partición para o sistema de ficheiros raíz.

#lvcreate -L 128G --name root root

Imos crear unha partición para intercambiar segundo o tamaño da memoria RAM.

#lvcreate -L 32G --name swap root

instalación do SO

En total, temos todo o necesario para instalar o sistema.

Inicie o asistente de instalación do sistema desde o entorno Ubuntu Live. Instalación normal. Só na fase de selección de discos para a instalación, cómpre especificar o seguinte:

  • /dev/md1, - punto de montaxe /boot, FS - BTRFS
  • /dev/root/root (tamén coñecido como /dev/mapper/root-root), - punto de montaxe / (root), FS - BTRFS
  • /dev/root/swap (tamén coñecido como /dev/mapper/root-swap), - use como partición de intercambio
  • Instale o cargador de arranque en /dev/sda

Cando selecciona BTRFS como sistema de ficheiros raíz, o instalador creará automaticamente dous volumes BTRFS chamados "@" para / (raíz) e "@home" para /home.

Comezamos a instalación...

A instalación rematará cunha caixa de diálogo modal que indica un erro ao instalar o cargador de arranque. Desafortunadamente, non poderás saír deste diálogo usando medios estándar e continuar coa instalación. Pechamos a sesión do sistema e iniciamos sesión de novo, rematando nun escritorio Ubuntu Live limpo. Abre o terminal e de novo:

#sudo bash

Cree un ambiente chroot para continuar coa instalación:

#mkdir /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@ /dev/mapper/root-root /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@home /dev/mapper/root-root /mnt/chroot/home
#mount -o defaults,space_cache,noatime,nodiratime,discard /dev/md1 /mnt/chroot/boot
#mount --bind /proc /mnt/chroot/proc
#mount --bind /sys /mnt/chroot/sys
#mount --bind /dev /mnt/chroot/dev

Imos configurar a rede e o nome de host en chroot:

#cat /etc/hostname >/mnt/chroot/etc/hostname
#cat /etc/hosts >/mnt/chroot/etc/hosts
#cat /etc/resolv.conf >/mnt/chroot/etc/resolv.conf

Imos ao ambiente chroot:

#chroot /mnt/chroot

En primeiro lugar, entregaremos os paquetes:

apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm

Imos comprobar e corrixir todos os paquetes que se instalaron mal debido á instalación incompleta do sistema:

#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES

Se algo non funciona, pode que teñas que editar primeiro /etc/apt/sources.list

Axustemos os parámetros do módulo RAID 6 para habilitar TRIM/DISCARD:

#cat >/etc/modprobe.d/raid456.conf << EOF
options raid456 devices_handle_discard_safely=1
EOF

Imos axustar un pouco as nosas matrices:

#cat >/etc/udev/rules.d/60-md.rules << EOF
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/stripe_cache_size", ATTR{md/stripe_cache_size}="32768"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_min", ATTR{md/sync_speed_min}="48000"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_max", ATTR{md/sync_speed_max}="300000"
EOF
#cat >/etc/udev/rules.d/62-hdparm.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/hdparm -B 254 /dev/%k"
EOF
#cat >/etc/udev/rules.d/63-blockdev.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/blockdev --setra 1024 /dev/%k"
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="md*", RUN+="/sbin/blockdev --setra 0 /dev/%k"
EOF

O que era..?Creamos un conxunto de regras udev que farán o seguinte:

  • Establece o tamaño da caché de bloque para RAID 2020 para que sexa o adecuado para 6. O valor predeterminado, ao parecer, non cambiou desde a creación de Linux e non foi o adecuado durante moito tempo.
  • Reserve un mínimo de E/S durante a duración das comprobacións/sincronizacións da matriz. Isto é para evitar que as súas matrices queden atascadas nun estado de sincronización eterna baixo carga.
  • Limite o máximo de E/S durante as comprobacións/sincronización de matrices. Isto é necesario para que a sincronización/comprobación dos RAID SSD non faga as súas unidades nítidas. Isto é especialmente certo para NVMe. (Lembras do radiador? Non estaba bromeando.)
  • Prohibe que os discos deteñan a rotación do eixe (HDD) mediante APM e configure o tempo de espera para os controladores de disco en 7 horas. Podes desactivar completamente APM se as túas unidades poden facelo (-B 255). Co valor predeterminado, as unidades pararanse despois de cinco segundos. Entón, o sistema operativo quere restablecer a caché do disco, os discos xirarán de novo e todo comezará de novo. Os discos teñen un número máximo limitado de rotacións do fuso. Un ciclo predeterminado tan sinxelo pode matar facilmente os teus discos nun par de anos. Non todos os discos padecen isto, pero os nosos son de “portátiles”, coa configuración predeterminada axeitada, que fan que o RAID pareza un mini-MAID.
  • Instalar readahead en discos (rotativos) 1 megabyte - dous bloques consecutivos/anaco RAID 6
  • Desactive readahead nas propias matrices.

Editemos /etc/fstab:

#cat >/etc/fstab << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# file-system mount-point type options dump pass
/dev/mapper/root-root / btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@ 0 1
UUID=$(blkid -o value -s UUID /dev/md1) /boot btrfs defaults,space_cache,noatime,nodiratime,discard 0 2
/dev/mapper/root-root /home btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@home 0 2
/dev/mapper/root-swap none swap sw 0 0
EOF

Por que é iso..?Buscaremos a partición /boot por UUID. A denominación das matrices podería cambiar teoricamente.

Buscaremos as seccións restantes por nomes de LVM na notación /dev/mapper/vg-lv, porque identifican as particións de forma bastante única.

Non usamos UUID para LVM porque O UUID dos volumes LVM e as súas instantáneas poden ser os mesmos.Montar /dev/mapper/root-root.. dúas veces?Si. Exactamente. Característica de BTRFS. Este sistema de ficheiros pódese montar varias veces con diferentes subvols.

Debido a esta mesma función, recomendo nunca crear instantáneas LVM de volumes BTRFS activos. Pode ter unha sorpresa ao reiniciar.

Rexeneremos a configuración de mdadm:

#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf

Axustemos a configuración de LVM:

#cat >>/etc/lvm/lvmlocal.conf << EOF

activation {
thin_pool_autoextend_threshold=90
thin_pool_autoextend_percent=5
}
allocation {
cache_pool_max_chunks=2097152
}
devices {
global_filter=["r|^/dev/.*_corig$|","r|^/dev/.*_cdata$|","r|^/dev/.*_cmeta$|","r|^/dev/.*gpv$|","r|^/dev/images/.*$|","r|^/dev/mapper/images.*$|","r|^/dev/backup/.*$|","r|^/dev/mapper/backup.*$|"] issue_discards=1
}
EOF

O que era..?Activamos a expansión automática das piscinas finas de LVM ao alcanzar o 90 % do espazo ocupado nun 5 % do volume.

Aumentamos o número máximo de bloques de caché para a caché LVM.

Evitamos que LVM busque volumes LVM (PV) en:

  • dispositivos que conteñen caché LVM (cdata)
  • dispositivos almacenados na caché usando a caché LVM, evitando a caché ( _corig). Neste caso, o propio dispositivo almacenado en caché aínda se analizará a través da caché (só ).
  • dispositivos que conteñan metadatos de caché LVM (cmeta)
  • todos os dispositivos en VG coas imaxes do nome. Aquí teremos imaxes de disco de máquinas virtuais e non queremos que LVM no host active volumes pertencentes ao SO convidado.
  • todos os dispositivos en VG co nome de copia de seguridade. Aquí teremos copias de seguridade das imaxes da máquina virtual.
  • todos os dispositivos cuxo nome remate con "gpv" (volume físico para invitados)

Activamos a compatibilidade con DISCARD ao liberar espazo libre en LVM VG. Ten coidado. Isto fará que a eliminación de LVs no SSD dera moito tempo. Isto aplícase especialmente ao SSD RAID 6. Non obstante, segundo o plan, utilizaremos aprovisionamento fino, polo que isto non nos impedirá en absoluto.

Imos actualizar a imaxe initramfs:

#update-initramfs -u -k all

Instala e configura grub:

#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc

Que discos escoller?Todos os que son sd*. O sistema debe poder iniciarse desde calquera unidade SATA ou SSD que funcione.

Por que engadiron os-prober..?Por excesiva independencia e mans brincadeiras.

Non funciona correctamente se un dos RAID está en estado degradado. Tenta buscar o SO nas particións que se usan nas máquinas virtuais que se executan neste hardware.

Se o necesitas, podes deixalo, pero ten en conta todo o anterior. Recomendo buscar receitas para desfacerse das mans traviesas en liña.

Con isto rematamos a instalación inicial. É hora de reiniciar o sistema operativo recentemente instalado. Non esquezas eliminar o Live CD/USB de arranque.

#exit
#reboot

Seleccione calquera dos SSD SATA como dispositivo de arranque.

LVM en SSD SATA

Neste punto, xa iniciamos o novo sistema operativo, configuramos a rede, apt, abrimos o emulador de terminal e iniciamos:

#sudo bash

Continuemos.

"Inicialice" a matriz desde SATA SSD:

#blkdiscard /dev/md2

Se non funciona, proba:

#blkdiscard --step 65536 /dev/md2
Crear LVM VG en SSD SATA:

#pvcreate /dev/md2
#vgcreate data /dev/md2

Por que outro VG..?De feito, xa temos un VG chamado root. Por que non engadir todo nun VG?

Se hai varios PV nun VG, para que o VG se active correctamente, todos os PV deben estar presentes (en liña). A excepción é LVM RAID, que non usamos deliberadamente.

Realmente queremos que se hai un fallo (perda de datos de lectura) nalgún dos arrays RAID 6, o sistema operativo arranque normalmente e nos dea a oportunidade de resolver o problema.

Para iso, no primeiro nivel de abstracción illaremos cada tipo de "medio" físico nun VG separado.

Científicamente falando, diferentes matrices RAID pertencen a diferentes "dominios de fiabilidade". Non deberías crear un punto común adicional de fallo para eles axitándoos nun VG.

A presenza de LVM a nivel de "hardware" permitiranos cortar arbitrariamente pezas de diferentes matrices RAID combinándoas de diferentes xeitos. Por exemplo - correr simultaneamente bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, unha complexa configuración de ZFS con cachés ou calquera outra mestura infernal para tentar comparalo todo.

A nivel de "hardware", non usaremos nada máis que bos e vellos volumes LVM "grosos". A excepción a esta regra pode ser a partición de copia de seguridade.

Creo que a estas alturas moitos lectores xa comezaran a sospeitar algo sobre a boneca aniñada.

LVM en disco duro SATA

#pvcreate /dev/md3
#vgcreate backup /dev/md3

Novo VG de novo..?Realmente queremos que se falla a matriz de discos que utilizaremos para a copia de seguranza de datos, o noso sistema operativo siga funcionando normalmente, mantendo o acceso aos datos que non sexan de copia de seguranza como é habitual. Polo tanto, para evitar problemas de activación de VG, creamos un VG separado.

Configurando a caché LVM

Imos crear un LV en NVMe RAID 1 para usalo como dispositivo de caché.

#lvcreate -L 70871154688B --name cache root

Por que hai tan pouco...?O caso é que os nosos SSD NVMe tamén teñen unha caché SLC. 4 gigabytes de "libre" e 18 gigabytes de dinámica debido ao espazo libre ocupado no MLC de 3 bits. Unha vez esgotada esta caché, os SSD NVMe non serán moito máis rápidos que os nosos SSD SATA con caché. En realidade, por este motivo, non ten sentido facer que a partición da caché LVM sexa moito máis grande que o dobre do tamaño da caché SLC da unidade NVMe. Para as unidades NVMe utilizadas, o autor considera razoable facer 32-64 gigabytes de caché.

O tamaño da partición é necesario para organizar 64 gigabytes de caché, metadatos da caché e copia de seguridade de metadatos.

Ademais, observo que despois dun apagado do sistema sucio, LVM marcará toda a caché como sucia e sincronizarase de novo. Ademais, isto repetirase cada vez que se use lvchange neste dispositivo ata que o sistema se reinicie de novo. Polo tanto, recomendo recrear inmediatamente a caché usando o script axeitado.

Imos crear un LV en SATA RAID 6 para usalo como dispositivo de caché.

#lvcreate -L 3298543271936B --name cache data

Por que só tres terabytes..?De xeito que, se é necesario, pode usar SATA SSD RAID 6 para outras necesidades. O tamaño do espazo almacenado na caché pódese aumentar de forma dinámica, sobre a marcha, sen deter o sistema. Para iso, cómpre deter temporalmente e reactivar a caché, pero a vantaxe distintiva da caché LVM fronte, por exemplo, a bcache é que se pode facer sobre a marcha.

Imos crear un novo VG para almacenar na caché.

#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache

Imos crear un LV no dispositivo almacenado na caché.

#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache

Aquí ocupamos inmediatamente todo o espazo libre en /dev/data/cache para que todas as outras particións necesarias se creasen inmediatamente en /dev/root/cache. Se creaches algo no lugar equivocado, podes movelo usando pvmove.

Imos crear e habilitar a caché:

#lvcreate -y -L 64G -n cache cache /dev/root/cache
#lvcreate -y -L 1G -n cachemeta cache /dev/root/cache
#lvconvert -y --type cache-pool --cachemode writeback --chunksize 64k --poolmetadata cache/cachemeta cache/cache
#lvconvert -y --type cache --cachepool cache/cache cache/cachedata

Por que tal tamaño..?A través de experimentos prácticos, o autor puido descubrir que o mellor resultado se consegue se o tamaño do bloque de caché LVM coincide co tamaño do bloque fino de LVM. Ademais, canto menor sexa o tamaño, mellor será a configuración nunha gravación aleatoria.

64k é o tamaño de bloque mínimo permitido para LVM thin.

Coidado coa redacción..!Si. Este tipo de caché difire a sincronización de escritura no dispositivo almacenado na caché. Isto significa que se se perde a caché, pode perder os datos do dispositivo almacenado na caché. Máis tarde, o autor dirá que medidas, ademais de NVMe RAID 1, se poden tomar para compensar este risco.

Este tipo de caché escolleuse intencionadamente para compensar o pobre rendemento de escritura aleatoria de RAID 6.

Imos comprobar o que temos:

#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize Devices
[cache] 68719476736B cache_cdata(0)
[cache_cdata] 68719476736B /dev/root/cache(0)
[cache_cmeta] 1073741824B /dev/root/cache(16384)
cachedata 3298539077632B cachedata_corig(0)
[cachedata_corig] 3298539077632B /dev/data/cache(0)
[lvol0_pmspare] 1073741824B /dev/root/cache(16640)

Só [cachedata_corig] debería estar situado en /dev/data/cache. Se algo está mal, use pvmove.

Podes desactivar a caché se é necesario cun comando:

#lvconvert -y --uncache cache/cachedata

Isto faise en liña. LVM simplemente sincronizará a caché co disco, elimínaa e renomeará cachedata_corig de novo a cachedata.

Configurando LVM thin

Estimemos aproximadamente canto espazo necesitamos para os metadatos delgados de LVM:

#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 bytes estimated metadata area size for "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"

Redondear ata 4 gigabytes: 4294967296B

Multiplica por dous e engade 4194304B para os metadatos de LVM PV: 8594128896B
Imos crear unha partición separada en NVMe RAID 1 para colocar nela os metadatos finos de LVM e a súa copia de seguridade:

#lvcreate -L 8594128896B --name images root

Para qué..?Aquí pode xurdir a pregunta: por que colocar os metadatos finos de LVM por separado se aínda se almacenarán na caché en NVMe e funcionarán rapidamente.

Aínda que a velocidade é importante aquí, está lonxe de ser a razón principal. O caso é que a caché é un punto de falla. Podería ocorrerlle algo e, se os metadatos delgados de LVM están en caché, fará que todo se perda por completo. Sen metadatos completos, será case imposible montar volumes finos.

Ao mover os metadatos a un volume separado non almacenado en caché, pero rápido, garantimos a seguridade dos metadatos en caso de perda ou corrupción da caché. Neste caso, todos os danos causados ​​pola perda da caché localizaranse dentro de volumes finos, o que simplificará o procedemento de recuperación en ordes de magnitude. Con alta probabilidade, estes danos serán restaurados mediante rexistros FS.

Ademais, se se tomou previamente unha instantánea dun volume fino, e despois diso a caché se sincronizou completamente polo menos unha vez, entón, debido ao deseño interno de LVM thin, a integridade da instantánea estará garantida en caso de perda de caché. .

Imos crear un novo VG que se encargará do thin-provisioning:

#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata

Imos crear unha piscina:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Por que -Z yAdemais do que está realmente destinado este modo - para evitar que os datos dunha máquina virtual se filtren a outra máquina virtual ao redistribuír o espazo - tamén se usa a posta a cero para aumentar a velocidade de escritura aleatoria en bloques inferiores a 64k. Calquera escritura de menos de 64 k nunha área previamente non asignada do volume delgado aliñarase ao bordo de 64 K na caché. Isto permitirá que a operación se realice enteiramente a través da caché, evitando o dispositivo almacenado na caché.

Movemos os LV aos PV correspondentes:

#pvmove -n images/thin-pool_tdata /dev/root/images /dev/cache/cachedata
#pvmove -n images/lvol0_pmspare /dev/cache/cachedata /dev/root/images
#pvmove -n images/thin-pool_tmeta /dev/cache/cachedata /dev/root/images

Comprobamos:

#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize Devices
[lvol0_pmspare] 4294967296B /dev/root/images(0)
thin-pool 274877906944B thin-pool_tdata(0)
[thin-pool_tdata] 274877906944B /dev/cache/cachedata(0)
[thin-pool_tmeta] 4294967296B /dev/root/images(1024)

Imos crear un volume fino para probas:

#lvcreate -V 64G --thin-pool thin-pool --name test images

Instalaremos paquetes para probas e seguimento:

#apt-get install sysstat fio

Deste xeito podes observar o comportamento da nosa configuración de almacenamento en tempo real:

#watch 'lvs --rows --reportformat basic --quiet -ocache_dirty_blocks,cache_settings cache/cachedata && (lvdisplay cache/cachedata | grep Cache) && (sar -p -d 2 1 | grep -E "sd|nvme|DEV|md1|md2|md3|md0" | grep -v Average | sort)'

Así é como podemos probar a nosa configuración:

#fio --loops=1 --size=64G --runtime=4 --filename=/dev/images/test --stonewall --ioengine=libaio --direct=1
--name=4kQD32read --bs=4k --iodepth=32 --rw=randread
--name=8kQD32read --bs=8k --iodepth=32 --rw=randread
--name=16kQD32read --bs=16k --iodepth=32 --rw=randread
--name=32KQD32read --bs=32k --iodepth=32 --rw=randread
--name=64KQD32read --bs=64k --iodepth=32 --rw=randread
--name=128KQD32read --bs=128k --iodepth=32 --rw=randread
--name=256KQD32read --bs=256k --iodepth=32 --rw=randread
--name=512KQD32read --bs=512k --iodepth=32 --rw=randread
--name=4Kread --bs=4k --rw=read
--name=8Kread --bs=8k --rw=read
--name=16Kread --bs=16k --rw=read
--name=32Kread --bs=32k --rw=read
--name=64Kread --bs=64k --rw=read
--name=128Kread --bs=128k --rw=read
--name=256Kread --bs=256k --rw=read
--name=512Kread --bs=512k --rw=read
--name=Seqread --bs=1m --rw=read
--name=Longread --bs=8m --rw=read
--name=Longwrite --bs=8m --rw=write
--name=Seqwrite --bs=1m --rw=write
--name=512Kwrite --bs=512k --rw=write
--name=256write --bs=256k --rw=write
--name=128write --bs=128k --rw=write
--name=64write --bs=64k --rw=write
--name=32write --bs=32k --rw=write
--name=16write --bs=16k --rw=write
--name=8write --bs=8k --rw=write
--name=4write --bs=4k --rw=write
--name=512KQD32write --bs=512k --iodepth=32 --rw=randwrite
--name=256KQD32write --bs=256k --iodepth=32 --rw=randwrite
--name=128KQD32write --bs=128k --iodepth=32 --rw=randwrite
--name=64KQD32write --bs=64k --iodepth=32 --rw=randwrite
--name=32KQD32write --bs=32k --iodepth=32 --rw=randwrite
--name=16KQD32write --bs=16k --iodepth=32 --rw=randwrite
--name=8KQD32write --bs=8k --iodepth=32 --rw=randwrite
--name=4kQD32write --bs=4k --iodepth=32 --rw=randwrite
| grep -E 'read|write|test' | grep -v ioengine

Coidado! Recurso!Este código executará 36 probas diferentes, cada unha durante 4 segundos. A metade das probas son para gravación. Podes gravar moito en NVMe en 4 segundos. Ata 3 gigabytes por segundo. Así, cada carreira de probas de escritura pode consumirche ata 216 gigabytes de recurso SSD.

Ler e escribir mesturados?Si. Ten sentido executar as probas de lectura e escritura por separado. Ademais, ten sentido asegurarse de que todos os cachés estean sincronizados para que unha escritura feita previamente non afecte á lectura.

Os resultados variarán moito durante o primeiro lanzamento e os posteriores a medida que se enchen a caché e o volume fino, e tamén dependendo de se o sistema conseguiu sincronizar os cachés enchidos durante o último lanzamento.

Entre outras cousas, recomendo medir a velocidade nun volume fino xa cheo do que se acaba de tomar unha instantánea. O autor tivo a oportunidade de observar como as escrituras aleatorias aceleran bruscamente inmediatamente despois de crear a primeira instantánea, especialmente cando a caché aínda non está completamente chea. Isto ocorre debido á semántica de escritura de copia en escritura, ao aliñamento da caché e aos bloques de volume finos e ao feito de que as escrituras aleatorias en RAID 6 se converten en lecturas aleatorias de RAID 6 seguidas de escrituras na caché. Na nosa configuración, a lectura aleatoria de RAID 6 é ata 6 veces (o número de SSD SATA na matriz) máis rápida que a escritura. Porque os bloques para CoW son asignados secuencialmente a partir dun grupo fino, entón a gravación, na súa maior parte, tamén se converte en secuencial.

Ambas funcións pódense utilizar para o seu beneficio.

Caché de instantáneas "coherentes".

Para reducir o risco de perda de datos en caso de dano/perda da caché, o autor propón introducir a práctica de rotar as instantáneas para garantir a súa integridade neste caso.

En primeiro lugar, porque os metadatos de volume reducido residen nun dispositivo sen caché, os metadatos serán consistentes e as posibles perdas illaranse dentro dos bloques de datos.

O seguinte ciclo de rotación de instantáneas garante a integridade dos datos dentro das instantáneas en caso de perda de caché:

  1. Para cada volume fino co nome <nome>, cree unha instantánea co nome <nome>.cached
  2. Axustemos o limiar de migración nun valor razoablemente alto: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. No bucle comprobamos o número de bloques sucios na caché: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' ata chegar a cero. Se o cero falta durante moito tempo, pódese crear cambiando temporalmente a caché ao modo de escritura. Non obstante, tendo en conta as características de velocidade das nosas matrices SSD SATA e NVMe, así como o seu recurso TBW, poderás captar rapidamente o momento sen cambiar o modo de caché ou o teu hardware consumirá por completo todo o seu recurso en algúns días. Debido ás limitacións de recursos, o sistema, en principio, non pode estar baixo o 100 % da carga de escritura todo o tempo. Os nosos SSD NVMe con carga de escritura inferior ao 100 % esgotarán completamente o recurso días 3-4. Os SSD SATA só durarán o dobre. Polo tanto, asumiremos que a maior parte da carga se destina á lectura, e temos ráfagas de actividade extremadamente altas a curto prazo, combinadas cunha carga baixa de media para escribir.
  4. En canto captamos (ou fixemos) un cero, cambiamos o nome de <nome>.cached a <nome>.committed. Elimínase o antigo <nome>.committed.
  5. Opcionalmente, se a caché está chea ao 100 %, pódese recrear mediante un script, borrando así. Cun caché medio baleiro, o sistema funciona moito máis rápido ao escribir.
  6. Establece o limiar de migración en cero: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Isto impedirá temporalmente que a caché se sincronice co medio principal.
  7. Agardamos ata que se acumulen moitos cambios na caché #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' ou o temporizador apagarase.
  8. Repetimos de novo.

Por que dificultades co limiar migratorio...?O caso é que na práctica real, unha gravación "aleatoria" en realidade non é completamente aleatoria. Se escribimos algo nun sector de 4 kilobytes de tamaño, hai unha alta probabilidade de que nos próximos minutos se faga un rexistro no mesmo ou nun dos sectores veciños (+- 32K).

Ao establecer o limiar de migración en cero, pospoñemos a sincronización de escritura no SSD SATA e agregamos varios cambios nun bloque de 64K na caché. Isto aforra significativamente o recurso do SSD SATA.

Onde está o código..?Desafortunadamente, o autor considérase insuficientemente competente no desenvolvemento de scripts bash porque é 100% autodidacta e practica o desenvolvemento impulsado por "google", polo que cre que o terrible código que sae das súas mans non debería ser usado por ninguén. outra cousa.

Creo que os profesionais deste campo poderán representar de forma independente toda a lóxica descrita anteriormente, se é necesario, e, quizais, incluso deseñala como un servizo systemd, como intentou facer o autor.

Un esquema de rotación de instantáneas tan sinxelo permitiranos non só ter unha instantánea totalmente sincronizada no SSD SATA, senón que tamén nos permitirá, mediante a utilidade thin_delta, descubrir que bloques se cambiaron despois da súa creación e, así, localizar os danos en os principais volumes, simplificando moito a recuperación.

CORTAR/DESCARTAR en libvirt/KVM

Porque o almacenamento de datos empregarase para KVM que executa libvirt, entón sería unha boa idea ensinar ás nosas máquinas virtuales non só a ocupar espazo libre, senón tamén a liberar o que xa non é necesario.

Isto faise emulando o soporte TRIM/DISCARD en discos virtuais. Para iso, cómpre cambiar o tipo de controlador a virtio-scsi e editar o xml.

#virsh edit vmname
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writethrough' io='threads' discard='unmap'/>
<source dev='/dev/images/vmname'/>
<backingStore/>
<target dev='sda' bus='scsi'/>
<alias name='scsi0-0-0-0'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>

<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</controller>

Tales descartes dos sistemas operativos convidados son procesados ​​correctamente por LVM e os bloques son liberados correctamente tanto na caché como no grupo fino. No noso caso, isto ocorre principalmente de forma atrasada, ao eliminar a seguinte instantánea.

Copia de seguridade BTRFS

Use scripts preparados con extrema precaución e baixo o seu propio risco. O autor escribiu este código el mesmo e exclusivamente para el. Estou seguro de que moitos usuarios experimentados de Linux teñen ferramentas similares e non hai necesidade de copiar as doutra persoa.

Imos crear un volume no dispositivo de copia de seguridade:

#lvcreate -L 256G --name backup backup

Formatémolo en BTRFS:

#mkfs.btrfs /dev/backup/backup

Imos crear puntos de montaxe e montar as subseccións raíz do sistema de ficheiros:

#mkdir /backup
#mkdir /backup/btrfs
#mkdir /backup/btrfs/root
#mkdir /backup/btrfs/back
#ln -s /boot /backup/btrfs
# cat >>/etc/fstab << EOF

/dev/mapper/root-root /backup/btrfs/root btrfs defaults,space_cache,noatime,nodiratime 0 2
/dev/mapper/backup-backup /backup/btrfs/back btrfs defaults,space_cache,noatime,nodiratime 0 2
EOF
#mount -a
#update-initramfs -u
#update-grub

Imos crear directorios para copias de seguridade:

#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot

Imos crear un directorio para os scripts de copia de seguridade:

#mkdir /root/btrfs-backup

Imos copiar o guión:

Moito código bash asustado. Use baixo o seu propio risco. Non escribas cartas con rabia ao autor...#cat >/root/btrfs-backup/btrfs-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".@base"
PEND_SUFFIX=".@pend"
SNAP_SUFFIX=".@snap"
MOUNTS="/backup/btrfs/"
BACKUPS="/backup/btrfs/back/remote/"

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function backup()
{
SOURCE_PATH="$MOUNTS$1"
TARGET_PATH="$BACKUPS$1"
SOURCE_BASE_PATH="$MOUNTS$1$BASE_SUFFIX"
TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
TARGET_BASE_DIR="$(dirname $TARGET_BASE_PATH)"
SOURCE_PEND_PATH="$MOUNTS$1$PEND_SUFFIX"
TARGET_PEND_PATH="$BACKUPS$1$PEND_SUFFIX"
if [ -d "$SOURCE_BASE_PATH" ] then
echo "$SOURCE_BASE_PATH found"
else
echo "$SOURCE_BASE_PATH File not found creating snapshot of $SOURCE_PATH to $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found out of sync with source... removing..."
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
fi
fi
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found"
else
echo "$TARGET_BASE_PATH not found. Synching to $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs receive $TARGET_BASE_DIR
sync
fi
if [ -d "$SOURCE_PEND_PATH" ] then
echo "$SOURCE_PEND_PATH found removing..."
btrfs subvolume delete -c $SOURCE_PEND_PATH
sync
fi
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
if [ -d "$TARGET_PEND_PATH" ] then
echo "$TARGET_PEND_PATH found removing..."
btrfs subvolume delete -c $TARGET_PEND_PATH
sync
fi
echo "Sending $SOURCE_PEND_PATH to $TARGET_PEND_PATH"
btrfs send -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs receive $TARGET_BASE_DIR
sync
TARGET_DATE_SUFFIX=$(suffix)
btrfs subvolume snapshot -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sync
btrfs subvolume delete -c $SOURCE_BASE_PATH
sync
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
mv $SOURCE_PEND_PATH $SOURCE_BASE_PATH
mv $TARGET_PEND_PATH $TARGET_BASE_PATH
sync
}

function list()
{
LIST_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
LIST_TARGET_BASE_DIR="$(dirname $LIST_TARGET_BASE_PATH)"
LIST_TARGET_BASE_NAME="$(basename -s .$BASE_SUFFIX $LIST_TARGET_BASE_PATH)"
find "$LIST_TARGET_BASE_DIR" -maxdepth 1 -mindepth 1 -type d -printf "%fn" | grep "${LIST_TARGET_BASE_NAME/$BASE_SUFFIX/$SNAP_SUFFIX}.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
REMOVE_TARGET_BASE_DIR="$(dirname $REMOVE_TARGET_BASE_PATH)"
btrfs subvolume delete -c $REMOVE_TARGET_BASE_DIR/$2
sync
}

function removeall()
{
DATE_OFFSET="$2"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$1" "$SNAPSHOT"
done < <(list "$1" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1"
;;
"list")
list "$1"
;;
"remove")
wait_lock_or_terminate
remove "$1" "$2"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

Que fai mesmo..?Contén un conxunto de comandos sinxelos para crear instantáneas BTRFS e copialas noutro FS mediante BTRFS enviar/recibir.

O primeiro lanzamento pode ser relativamente longo, porque... Ao principio, copiaranse todos os datos. Os lanzamentos posteriores serán moi rápidos, porque... Só se copiarán os cambios.

Outro script que poñeremos en cron:

Algún código máis bash#cat >/root/btrfs-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RETENTION="-60 day"
$BACKUP_SCRIPT backup root/@
$BACKUP_SCRIPT removeall root/@ "$RETENTION"
$BACKUP_SCRIPT backup root/@home
$BACKUP_SCRIPT removeall root/@home "$RETENTION"
$BACKUP_SCRIPT backup boot/
$BACKUP_SCRIPT removeall boot/ "$RETENTION"
EOF

Que fai..?Crea e sincroniza instantáneas incrementais dos volumes BTRFS listados no FS de copia de seguridade. Despois disto, elimina todas as imaxes creadas hai 60 días. Despois do lanzamento, aparecerán instantáneas datadas dos volumes listados nos subdirectorios /backup/btrfs/back/remote/.

Imos dar os dereitos de execución do código:

#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh

Comprobámolo e poñémolo no cron:

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup
#cat /var/log/syslog | grep btrfs-backup
#crontab -e
0 2 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup

Copia de seguridade fina de LVM

Imos crear un grupo fino no dispositivo de copia de seguridade:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool

Imos instalar ddrescue, porque... os scripts usarán esta ferramenta:

#apt-get install gddrescue

Imos crear un directorio para scripts:

#mkdir /root/lvm-thin-backup

Imos copiar os guións:

Moita festa dentro...#cat >/root/lvm-thin-backup/lvm-thin-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".base"
PEND_SUFFIX=".pend"
SNAP_SUFFIX=".snap"
BACKUPS="backup"
BACKUPS_POOL="thin-pool"

export LVM_SUPPRESS_FD_WARNINGS=1

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function read_thin_id {
lvs --rows --reportformat basic --quiet -othin_id "$1/$2" | awk '{print $2}'
}

function read_pool_lv {
lvs --rows --reportformat basic --quiet -opool_lv "$1/$2" | awk '{print $2}'
}

function read_lv_dm_path {
lvs --rows --reportformat basic --quiet -olv_dm_path "$1/$2" | awk '{print $2}'
}

function read_lv_active {
lvs --rows --reportformat basic --quiet -olv_active "$1/$2" | awk '{print $2}'
}

function read_lv_chunk_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -ochunk_size "$1/$2" | awk '{print $2}'
}

function read_lv_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -olv_size "$1/$2" | awk '{print $2}'
}

function activate_volume {
lvchange -ay -Ky "$1/$2"
}

function deactivate_volume {
lvchange -an "$1/$2"
}

function read_thin_metadata_snap {
dmsetup status "$1" | awk '{print $7}'
}

function thindiff()
{
DIFF_VG="$1"
DIFF_SOURCE="$2"
DIFF_TARGET="$3"
DIFF_SOURCE_POOL=$(read_pool_lv $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_POOL=$(read_pool_lv $DIFF_VG $DIFF_TARGET)

if [ "$DIFF_SOURCE_POOL" == "" ] then
(>&2 echo "Source LV is not thin.")
exit 1
fi

if [ "$DIFF_TARGET_POOL" == "" ] then
(>&2 echo "Target LV is not thin.")
exit 1
fi

if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ] then
(>&2 echo "Source and target LVs belong to different thin pools.")
exit 1
fi

DIFF_POOL_PATH=$(read_lv_dm_path $DIFF_VG $DIFF_SOURCE_POOL)
DIFF_SOURCE_ID=$(read_thin_id $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_ID=$(read_thin_id $DIFF_VG $DIFF_TARGET)
DIFF_POOL_PATH_TPOOL="$DIFF_POOL_PATH-tpool"
DIFF_POOL_PATH_TMETA="$DIFF_POOL_PATH"_tmeta
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" != "-" ] then
(>&2 echo "Thin pool metadata snapshot already exist. Assuming stale one. Will release metadata snapshot in 5 seconds.")
sleep 5
dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap
fi

dmsetup message $DIFF_POOL_PATH_TPOOL 0 reserve_metadata_snap
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" == "-" ] then
(>&2 echo "Failed to create thin pool metadata snapshot.")
exit 1
fi

#We keep output in variable because metadata snapshot need to be released early.
DIFF_DATA=$(thin_delta -m$DIFF_POOL_METADATA_SNAP --snap1 $DIFF_SOURCE_ID --snap2 $DIFF_TARGET_ID $DIFF_POOL_PATH_TMETA)

dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap

echo $"$DIFF_DATA" | grep -E 'different|left_only|right_only' | sed 's/</"/g' | sed 's/ /"/g' | awk -F'"' '{print $6 "t" $8 "t" $11}' | sed 's/different/copy/g' | sed 's/left_only/copy/g' | sed 's/right_only/discard/g'

}

function thinsync()
{
SYNC_VG="$1"
SYNC_PEND="$2"
SYNC_BASE="$3"
SYNC_TARGET="$4"
SYNC_PEND_POOL=$(read_pool_lv $SYNC_VG $SYNC_PEND)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SYNC_VG $SYNC_PEND_POOL)
SYNC_PEND_PATH=$(read_lv_dm_path $SYNC_VG $SYNC_PEND)

activate_volume $SYNC_VG $SYNC_PEND

while read -r SYNC_ACTION SYNC_OFFSET SYNC_LENGTH ; do
SYNC_OFFSET_BYTES=$((SYNC_OFFSET * SYNC_BLOCK_SIZE))
SYNC_LENGTH_BYTES=$((SYNC_LENGTH * SYNC_BLOCK_SIZE))
if [ "$SYNC_ACTION" == "copy" ] then
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SYNC_PEND_PATH" "$SYNC_TARGET"
fi

if [ "$SYNC_ACTION" == "discard" ] then
blkdiscard -o $SYNC_OFFSET_BYTES -l $SYNC_LENGTH_BYTES "$SYNC_TARGET"
fi
done < <(thindiff "$SYNC_VG" "$SYNC_PEND" "$SYNC_BASE")
}

function discard_volume()
{
DISCARD_VG="$1"
DISCARD_LV="$2"
DISCARD_LV_PATH=$(read_lv_dm_path "$DISCARD_VG" "$DISCARD_LV")
if [ "$DISCARD_LV_PATH" != "" ] then
echo "$DISCARD_LV_PATH found"
else
echo "$DISCARD_LV not found in $DISCARD_VG"
exit 1
fi
DISCARD_LV_POOL=$(read_pool_lv $DISCARD_VG $DISCARD_LV)
DISCARD_LV_SIZE=$(read_lv_size "$DISCARD_VG" "$DISCARD_LV")
lvremove -y --quiet "$DISCARD_LV_PATH" || exit 1
lvcreate --thin-pool "$DISCARD_LV_POOL" -V "$DISCARD_LV_SIZE"B --name "$DISCARD_LV" "$DISCARD_VG" || exit 1
}

function backup()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
SOURCE_PEND_LV="$SOURCE_LV$PEND_SUFFIX"
TARGET_PEND_LV="$TARGET_LV$PEND_SUFFIX"
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "Source base not found creating snapshot of $SOURCE_VG/$SOURCE_LV to $SOURCE_VG/$SOURCE_BASE_LV"
lvcreate --quiet --snapshot --name "$SOURCE_BASE_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo "Discarding $SOURCE_BASE_LV_PATH as we need to bootstrap."
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
sync
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found out of sync with source... removing..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || exit 1
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
sync
fi
fi
SOURCE_BASE_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_BASE_LV")
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_VG/$TARGET_LV not found. Creating empty volume."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Have to rebootstrap. Discarding source at $SOURCE_BASE_LV_PATH"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
TARGET_BASE_POOL=$(read_pool_lv $TARGET_VG $TARGET_BASE_LV)
TARGET_BASE_CHUNK_SIZE=$(read_lv_chunk_size $TARGET_VG $TARGET_BASE_POOL)
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
echo "Discarding target at $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ] then
echo "$SOURCE_PEND_LV_PATH found removing..."
lvremove -y --quiet "$SOURCE_PEND_LV_PATH" || exit 1
sync
fi
lvcreate --quiet --snapshot --name "$SOURCE_PEND_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
sync
if [ "$TARGET_PEND_LV_PATH" != "" ] then
echo "$TARGET_PEND_LV_PATH found removing..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
sync
fi
lvcreate --quiet --snapshot --name "$TARGET_PEND_LV" "$TARGET_VG/$TARGET_BASE_LV" || exit 1
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")
SOURCE_PEND_LV_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_PEND_LV")
lvresize -L "$SOURCE_PEND_LV_SIZE"B "$TARGET_PEND_LV_PATH"
activate_volume "$TARGET_VG" "$TARGET_PEND_LV"
echo "Synching $SOURCE_PEND_LV_PATH to $TARGET_PEND_LV_PATH"
thinsync "$SOURCE_VG" "$SOURCE_PEND_LV" "$SOURCE_BASE_LV" "$TARGET_PEND_LV_PATH" || exit 1
sync

TARGET_DATE_SUFFIX=$(suffix)
lvcreate --quiet --snapshot --name "$TARGET_LV$TARGET_DATE_SUFFIX" "$TARGET_VG/$TARGET_PEND_LV" || exit 1
sync
lvremove --quiet -y "$SOURCE_BASE_LV_PATH" || exit 1
sync
lvremove --quiet -y "$TARGET_BASE_LV_PATH" || exit 1
sync
lvrename -y "$SOURCE_VG/$SOURCE_PEND_LV" "$SOURCE_BASE_LV" || exit 1
lvrename -y "$TARGET_VG/$TARGET_PEND_LV" "$TARGET_BASE_LV" || exit 1
sync
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function verify()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Comparing "$SOURCE_BASE_LV_PATH" with "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function resync()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)

echo Syncronizing "$SOURCE_BASE_LV_PATH" to "$TARGET_BASE_LV_PATH"

CMP_OFFSET=0
while [[ "$CMP_OFFSET" != "" ]] ; do
CMP_MISMATCH=$(cmp -i "$CMP_OFFSET" "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH" | grep differ | awk '{print $5}' | sed 's/,//g' )
if [[ "$CMP_MISMATCH" != "" ]] ; then
CMP_OFFSET=$(( CMP_MISMATCH + CMP_OFFSET ))
SYNC_OFFSET_BYTES=$(( ( CMP_OFFSET / SYNC_BLOCK_SIZE ) * SYNC_BLOCK_SIZE ))
SYNC_LENGTH_BYTES=$(( SYNC_BLOCK_SIZE ))
echo "Synching $SYNC_LENGTH_BYTES bytes at $SYNC_OFFSET_BYTES from $SOURCE_BASE_LV_PATH to $TARGET_BASE_LV_PATH"
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
else
CMP_OFFSET=""
fi
done
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function list()
{
LIST_SOURCE_VG="$1"
LIST_SOURCE_LV="$2"
LIST_TARGET_VG="$BACKUPS"
LIST_TARGET_LV="$LIST_SOURCE_VG-$LIST_SOURCE_LV"
LIST_TARGET_BASE_LV="$LIST_TARGET_LV$SNAP_SUFFIX"
lvs -olv_name | grep "$LIST_TARGET_BASE_LV.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_VG="$BACKUPS"
REMOVE_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sync
}

function removeall()
{
DATE_OFFSET="$3"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$SNAPSHOT"
done < <(list "$1" "$2" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1" "$2"
;;
"list")
list "$1" "$2"
;;
"thindiff")
thindiff "$1" "$2" "$3"
;;
"thinsync")
thinsync "$1" "$2" "$3" "$4"
;;
"verify")
wait_lock_or_terminate
verify "$1" "$2"
;;
"resync")
wait_lock_or_terminate
resync "$1" "$2"
;;
"remove")
wait_lock_or_terminate
remove "$1"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2" "$3"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

Que fai...?Contén un conxunto de comandos para manipular instantáneas finas e sincronizar a diferenza entre dúas instantáneas finas recibidas mediante thin_delta a outro dispositivo de bloque mediante ddrescue e blkdiscard.

Outro script que poñeremos en cron:

Un pouco máis de bash#cat >/root/lvm-thin-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/lvm-thin-backup.sh"
RETENTION="-60 days"

$BACKUP_SCRIPT backup images linux-dev
$BACKUP_SCRIPT backup images win8
$BACKUP_SCRIPT backup images win8-data
#etc

$BACKUP_SCRIPT removeall images linux-dev "$RETENTION"
$BACKUP_SCRIPT removeall images win8 "$RETENTION"
$BACKUP_SCRIPT removeall images win8-data "$RETENTION"
#etc

EOF

Que fai...?Usa o script anterior para crear e sincronizar copias de seguridade dos volumes finos enumerados. O script deixará instantáneas inactivas dos volumes listados, que son necesarios para rastrexar os cambios desde a última sincronización.

Este script debe ser editado, especificando a lista de volumes finos para os que se deben facer copias de seguridade. Os nomes indicados son só para fins ilustrativos. Se o desexa, pode escribir un script que sincronice todos os volumes.

Imos dar os dereitos:

#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh

Comprobámolo e poñémolo no cron:

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup
#cat /var/log/syslog | grep lvm-thin-backup
#crontab -e
0 3 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup

O primeiro lanzamento será longo, porque... os volumes finos sincronizaranse totalmente copiando todo o espazo usado. Grazas aos metadatos finos de LVM, sabemos que bloques están realmente en uso, polo que só se copiarán os bloques de volume fino realmente utilizados.

As execucións posteriores copiarán os datos de forma incremental grazas ao seguimento de cambios mediante metadatos finos de LVM.

A ver que pasou:

#time /root/btrfs-backup/cron-daily.sh
real 0m2,967s
user 0m0,225s
sys 0m0,353s

#time /root/lvm-thin-backup/cron-daily.sh
real 1m2,710s
user 0m12,721s
sys 0m6,671s

#ls -al /backup/btrfs/back/remote/*
/backup/btrfs/back/remote/boot:
total 0
drwxr-xr-x 1 root root 1260 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 322 мар 26 02:00 .@base
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 240 мар 26 09:11 @.@base
drwxr-xr-x 1 root root 22 мар 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
...
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
...

#lvs -olv_name,lv_size images && lvs -olv_name,lv_size backup
LV LSize
linux-dev 128,00g
linux-dev.base 128,00g
thin-pool 1,38t
win8 128,00g
win8-data 2,00t
win8-data.base 2,00t
win8.base 128,00g
LV LSize
backup 256,00g
images-linux-dev.base 128,00g
images-linux-dev.snap.2020-03-08-10-09-11 128,00g
images-linux-dev.snap.2020-03-08-10-09-25 128,00g
...
images-win8-data.base 2,00t
images-win8-data.snap.2020-03-16-14-11-55 2,00t
images-win8-data.snap.2020-03-16-14-19-50 2,00t
...
images-win8.base 128,00g
images-win8.snap.2020-03-17-04-51-46 128,00g
images-win8.snap.2020-03-18-03-02-49 128,00g
...
thin-pool <2,09t

Que ten que ver isto coas bonecas aniñadas?

O máis probable é, dado que os volumes lóxicos de LVM LV poden ser volumes físicos de LVM PV para outros VG. LVM pode ser recursivo, como as bonecas aniñadas. Isto dálle a LVM unha flexibilidade extrema.

PS

No seguinte artigo, tentaremos utilizar varios sistemas de almacenamento móbil/KVM similares como base para crear un clúster de almacenamento/vm xeodistribuído con redundancia en varios continentes utilizando escritorios domésticos, Internet doméstico e redes P2P.

Fonte: www.habr.com

Engadir un comentario