Buen día.
Me gustaría compartir con la comunidad mi experiencia práctica en la construcción de un sistema de almacenamiento de datos para KVM utilizando md RAID + LVM.
El programa incluirá:
- Construyendo md RAID 1 desde NVMe SSD.
- Montaje de md RAID 6 a partir de SSD SATA y unidades normales.
- Características de la operación TRIM/DISCARD en SSD RAID 1/6.
- Creación de una matriz md RAID 1/6 de arranque en un conjunto común de discos.
- Instalar el sistema en NVMe RAID 1 cuando no hay soporte NVMe en el BIOS.
- Usando caché LVM y LVM delgado.
- Usar instantáneas BTRFS y enviar/recibir para respaldo.
- Uso de instantáneas delgadas de LVM y Thin_delta para copias de seguridad de estilo BTRFS.
Si está interesado, consulte cat.
declaración
El autor no asume ninguna responsabilidad por las consecuencias del uso o no uso de materiales/ejemplos/códigos/consejos/datos de este artículo. Al leer o utilizar este material de cualquier forma, usted asume la responsabilidad de todas las consecuencias de estas acciones. Las posibles consecuencias incluyen:
- SSD NVMe fritos crujientes.
- Recursos de grabación completamente agotados y fallas de las unidades SSD.
- Pérdida completa de todos los datos en todas las unidades, incluidas las copias de seguridad.
- Hardware informático defectuoso.
- Tiempo, nervios y dinero perdidos.
- Cualesquiera otras consecuencias que no estén enumeradas anteriormente.
hierro
Disponibles fueron:
Placa base de aproximadamente 2013 con chipset Z87, completa con Intel Core i7 / Haswell.
- Procesador 4 núcleos, 8 hilos
- 32 GB de RAM DDR3
- 1 x 16 o 2 x 8 PCIe 3.0
- 1 x 4 + 1 x 1 PCIe 2.0
- 6 conectores SATA 6 de 3 GB/s
El adaptador SAS LSI SAS9211-8I pasó al modo IT/HBA. El firmware habilitado para RAID se reemplazó intencionalmente con firmware HBA para:
- Puedes tirar este adaptador en cualquier momento y reemplazarlo por cualquier otro que encuentres.
- TRIM/Discard funcionó normalmente en los discos, porque... en el firmware RAID estos comandos no son compatibles en absoluto y al HBA, en general, no le importa qué comandos se transmiten a través del bus.
Discos duros: 8 unidades HGST Travelstar 7K1000 con una capacidad de 1 TB en un formato de 2.5, como para portátiles. Estas unidades estaban anteriormente en una matriz RAID 6. También tendrán un uso en el nuevo sistema. Para almacenar copias de seguridad locales.
Agregado adicionalmente:
6 piezas SSD SATA modelo Samsung 860 QVO 2TB. Estos SSD requerían un gran volumen, se deseaba la presencia de un caché SLC, confiabilidad y un precio bajo. Se requirió soporte para descarte/cero, lo cual se verifica en la línea en dmesg:
kernel: ata1.00: Enabling discard_zeroes_data
2 unidades de SSD NVMe modelo Samsung SSD 970 EVO 500GB.
Para estos SSD, la velocidad de lectura/escritura aleatoria y la capacidad de recursos para sus necesidades son importantes. Radiador para ellos. Necesariamente. Absolutamente. De lo contrario, fríelos hasta que estén crujientes durante la primera sincronización RAID.
Adaptador StarTech PEX8M2E2 para 2 x NVMe SSD instalado en ranura PCIe 3.0 8x. Esto, nuevamente, es solo un HBA, pero para NVMe. Se diferencia de los adaptadores económicos en que no requiere soporte de bifurcación PCIe de la placa base debido a la presencia de un conmutador PCIe incorporado. Funcionará incluso en el sistema más antiguo con PCIe, incluso si se trata de una ranura PCIe 1 x1.0. Naturalmente, a la velocidad adecuada. Allí no hay redadas. No hay BIOS integrado a bordo. Por lo tanto, su sistema no aprenderá mágicamente a arrancar con NVMe y mucho menos a hacer NVMe RAID gracias a este dispositivo.
Este componente se debió únicamente a la presencia de un solo PCIe 8 3.0x libre en el sistema y, si hay 2 ranuras libres, se puede reemplazar fácilmente con dos centavos PEX4M2E1 o análogos, que se pueden comprar en cualquier lugar a un precio de 600 rublos.
El rechazo de todo tipo de hardware o RAID de chipset/BIOS incorporado se hizo deliberadamente, para poder reemplazar completamente todo el sistema, con la excepción de los propios SSD/HDD, conservando todos los datos. Idealmente, para que pueda guardar incluso el sistema operativo instalado al pasar a un hardware completamente nuevo/diferente. Lo principal es que hay puertos SATA y PCIe. Es como un CD en vivo o una unidad flash de arranque, sólo que muy rápido y un poco voluminoso.
humorDe lo contrario, ya sabe lo que sucede: a veces es necesario llevarse urgentemente toda la matriz para llevársela. Pero no quiero perder datos. Para hacer esto, todos los medios mencionados están convenientemente ubicados en las diapositivas de las bahías 5.25 del estuche estándar.
Bueno, y, por supuesto, para experimentar con diferentes métodos de almacenamiento en caché SSD en Linux.
Las incursiones de hardware son aburridas. Encenderlo. O funciona o no. Y con mdadm siempre hay opciones.
Suave
Anteriormente, el hardware estaba instalado. Debian 8 Jessie, que está cerca del final de su vida útil. Se creó un RAID 6 utilizando los discos duros mencionados anteriormente junto con LVM. Estaba funcionando máquina virtual en kvm/libvirt.
Dado que el autor tiene experiencia relevante en la creación de unidades flash SATA/NVMe portátiles de arranque, y también para no romper la plantilla apt habitual, se eligió el sistema de destino. Ubuntu 18.04, que ya se ha estabilizado lo suficiente, pero aún tiene por delante 3 años de soporte.
El sistema mencionado contiene todos los controladores de hardware que necesitamos listos para usar. No necesitamos ningún software ni controladores de terceros.
Preparación para la instalación
Para instalar el sistema necesitaremos Ubuntu Imagen de escritorio. El sistema del servidor tiene un instalador basado en el kernel que muestra una autonomía excesiva y no desactivable, colocando siempre la partición del sistema UEFI en una de las unidades, arruinando la estética general. Por consiguiente, solo se instala en modo UEFI. No ofrece opciones.
No estamos contentos con esto.
¿Por qué?Desafortunadamente, el arranque UEFI es extremadamente poco compatible con el software de arranque RAID, porque... Nadie nos ofrece reservas para la partición UEFI ESP. Hay recetas en línea que sugieren colocar la partición ESP en una unidad flash en un puerto USB, pero este es un punto de falla. Hay recetas que utilizan el software mdadm RAID 1 con metadatos versión 0.9 que no impiden que UEFI BIOS vea esta partición, pero esto dura hasta el feliz momento en que el BIOS u otro sistema operativo de hardware escribe algo en el ESP y se olvida de sincronizarlo con otros. espejos.
Además, el arranque UEFI depende de la NVRAM, que no se moverá junto con los discos al nuevo sistema, porque es parte de la placa base.
Por tanto, no reinventaremos una nueva rueda. Ya tenemos una bicicleta de abuelo lista para usar y probada en el tiempo, ahora llamada bota Legacy/BIOS, que lleva el orgulloso nombre de CSM en sistemas compatibles con UEFI. Simplemente lo sacaremos del estante, lo lubricaremos, inflaremos los neumáticos y lo limpiaremos con un paño húmedo.
Versión de escritorio Ubuntu Tampoco se instala correctamente con el gestor de arranque Legacy, pero al menos hay opciones, como dicen.
Así pues, ensamblamos el hardware e iniciamos el sistema desde una unidad flash de arranque. Ubuntu En directo. Necesitaremos descargar paquetes, así que configuremos la red que tienes funcionando. Si no funciona, puedes descargar los paquetes necesarios a una memoria USB con antelación.
Entramos en el entorno de Escritorio, lanzamos el emulador de terminal y listo:
#sudo bash
Cómo…?La línea de arriba es el desencadenante canónico de holiwars sobre sudo. C bоmayores oportunidades vienen yоmayor responsabilidad. La pregunta es si puedes asumirlo tú mismo. Mucha gente piensa que usar sudo de esta manera al menos no es cuidadoso. Sin embargo:

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc
¿Por qué no ZFS...?Cuando instalamos software en nuestra computadora, esencialmente prestamos nuestro hardware a los desarrolladores de este software para que lo manejen.
Cuando confiamos a este software la seguridad de nuestros datos, solicitamos un préstamo equivalente al costo de restaurar estos datos, que tendremos que pagar algún día.
Desde este punto de vista, ZFS es un Ferrari y mdadm+lvm se parece más a una bicicleta.
Subjetivamente, el autor prefiere prestar una bicicleta a crédito a desconocidos en lugar de un Ferrari. Allí el precio de la emisión no es elevado. No hay necesidad de derechos. Más sencillo que las normas de tráfico. El aparcamiento es gratuito. La capacidad para cruzar el país es mejor. Siempre puedes colocar patas en una bicicleta y puedes repararla con tus propias manos.
¿Por qué entonces BTRFS...?Para iniciar el sistema operativo, necesitamos un sistema de archivos que sea compatible con Legacy/BIOS GRUB listo para usar y que al mismo tiempo admita instantáneas en vivo. Lo usaremos para la partición /boot. Además, el autor prefiere usar este FS para / (root), sin olvidar señalar que para cualquier otro software puede crear particiones separadas en LVM y montarlas en los directorios necesarios.
Ni imágenes máquinas virtualesNo almacenaremos ninguna base de datos en este sistema de archivos.
Este FS solo se usará para crear instantáneas del sistema sin apagarlo y luego transferir estas instantáneas a un disco de respaldo mediante enviar/recibir.
Además, el autor generalmente prefiere mantener un mínimo de software directamente en el hardware y ejecutar todo el resto del software en máquinas virtuales usando cosas como reenviar GPU y controladores de host PCI-USB a KVM a través de IOMMU.
Lo único que queda en el hardware es el almacenamiento de datos, la virtualización y la copia de seguridad.
Si confía más en ZFS, entonces, en principio, para la aplicación especificada son intercambiables.
Sin embargo, el autor ignora deliberadamente las funciones integradas de duplicación/RAID y redundancia que tienen ZFS, BRTFS y LVM.
Como argumento adicional, BTRFS tiene la capacidad de convertir escrituras aleatorias en secuenciales, lo que tiene un efecto extremadamente positivo en la velocidad de sincronización de instantáneas/copias de seguridad en el disco duro.
Volvamos a escanear todos los dispositivos:
#udevadm control --reload-rules && udevadm trigger
Echemos un vistazo a nuestro alrededor:
#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
Diseño del disco
NVMe SSD
Pero no los marcaremos de ninguna manera. De todos modos, nuestro BIOS no ve estas unidades. Entonces, pasarán completamente al RAID por software. Ni siquiera crearemos secciones allí. Si desea seguir el "canon" o "principalmente", cree una partición grande, como un disco duro.
HDD SATA
No es necesario inventar nada especial aquí. Crearemos una sección para todo. Crearemos una partición porque el BIOS ve estos discos e incluso puede intentar arrancar desde ellos. Incluso instalaremos GRUB en estos discos más adelante para que el sistema pueda hacer esto de repente.
#cat >hdd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sdg
unit: sectors
/dev/sdg1 : inicio= 2048, tamaño= 1953523120, tipo=fd, arrancable
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
SATA SSD
Aquí es donde las cosas se ponen interesantes para nosotros.
En primer lugar, nuestras unidades tienen un tamaño de 2 TB. Esto está dentro del rango aceptable para MBR, que es lo que usaremos. Si es necesario, se puede reemplazar con GPT. Los discos GPT tienen una capa de compatibilidad que permite a los sistemas compatibles con MBR ver las primeras 4 particiones si están ubicadas dentro de los primeros 2 terabytes. Lo principal es que la partición de arranque y la partición bios_grub de estos discos deben estar al principio. Esto incluso le permite arrancar desde unidades GPT Legacy/BIOS.
Pero este no es nuestro caso.
Aquí crearemos dos secciones. El primero tendrá un tamaño de 1 GB y se utilizará para RAID 1/arranque.
El segundo se usará para RAID 6 y ocupará todo el espacio libre restante excepto una pequeña área no asignada al final de la unidad.
¿Qué es esta zona no marcada?Según fuentes de la red, nuestros SSD SATA tienen a bordo un caché SLC dinámicamente ampliable con un tamaño de 6 a 78 gigabytes. Obtenemos 6 gigabytes “gratis” debido a la diferencia entre “gigabytes” y “gibibytes” en la hoja de datos de la unidad. Los 72 gigabytes restantes se asignan a partir del espacio no utilizado.
Aquí cabe señalar que tenemos un caché SLC y el espacio está ocupado en modo MLC de 4 bits. Lo que para nosotros significa efectivamente que por cada 4 gigabytes de espacio libre solo obtendremos 1 gigabyte de caché SLC.
Multiplica 72 gigabytes por 4 y obtienes 288 gigabytes. Este es el espacio libre que no marcaremos para permitir que las unidades hagan uso completo del caché SLC.
Así, conseguiremos efectivamente hasta 312 gigas de caché SLC de un total de seis unidades. De todas las unidades, 2 se utilizarán en RAID para lograr redundancia.
Esta cantidad de caché nos permitirá, en muy raras ocasiones, encontrarnos en la vida real con una situación en la que una escritura no vaya al caché. Esto compensa muy bien el inconveniente más triste de la memoria QLC: la velocidad de escritura extremadamente baja cuando los datos se escriben sin pasar por el caché. Si sus cargas no corresponden a esto, le recomiendo que piense detenidamente cuánto durará su SSD bajo dicha carga, teniendo en cuenta el TBW de la hoja de datos.
#cat >ssd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sda
unit: sectors
/dev/sda1 : inicio= 2048, tamaño= 2097152, tipo=fd, arrancable
/dev/sda2 : inicio= 2099200, tamaño= 3300950016, tipo=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 matrices
Primero, necesitamos cambiar el nombre de la máquina. Esto es necesario porque el nombre del host es parte del nombre de la matriz en algún lugar dentro de mdadm y afecta algo en alguna parte. Por supuesto, se puede cambiar el nombre de las matrices más adelante, pero este es un paso innecesario.
#mcedit /etc/hostname
#mcedit /etc/hosts
#hostname
vdesk0
NVMe SSD
#mdadm --create --verbose --assume-clean /dev/md0 --level=1 --raid-devices=2 /dev/nvme[0-1]n1
¿Por qué -suponer-limpio...?Para evitar la inicialización de matrices. Esto es válido para los niveles RAID 1 y 6. Todo puede funcionar sin inicialización si se trata de una matriz nueva. Además, inicializar la matriz SSD tras su creación es un desperdicio de recursos TBW. Usamos TRIM/DISCARD siempre que sea posible en matrices SSD ensambladas para "inicializarlas".
Para matrices SSD, RAID 1 DISCARD se admite de fábrica.
Para matrices SSD RAID 6 DISCARD, debe habilitarlo en los parámetros del módulo del kernel.
Esto solo debe hacerse si todos los SSD utilizados en matrices de nivel 4/5/6 en este sistema tienen soporte funcional para descartar_zeroes_data. A veces te encuentras con unidades extrañas que le dicen al kernel que esta función es compatible, pero en realidad no está ahí o la función no siempre funciona. Por el momento, el soporte está disponible en casi todas partes, sin embargo, hay unidades y firmware antiguos con errores. Por este motivo, la compatibilidad con DISCARD está deshabilitada de forma predeterminada para RAID 6.
Atención, el siguiente comando destruirá todos los datos de las unidades NVMe "inicializando" la matriz con "ceros".
#blkdiscard /dev/md0
Si algo sale mal, intente especificar un paso.
#blkdiscard --step 65536 /dev/md0
SATA SSD
#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 qué tan grande...?Aumentar el tamaño del fragmento tiene un efecto positivo en la velocidad de lectura aleatoria en bloques hasta el tamaño del fragmento inclusive. Esto sucede porque una operación del tamaño apropiado o menor se puede completar completamente en un solo dispositivo. Por lo tanto, se resumen los IOPS de todos los dispositivos. Según las estadísticas, el 99% de las IO no superan los 512K.
RAID 6 IOPS por escritura siempre menor o igual que los IOPS de una unidad. Cuando, como lectura aleatoria, IOPS puede ser varias veces mayor que el de una unidad, y aquí el tamaño del bloque es de importancia clave.
El autor no ve el sentido de intentar optimizar un parámetro que es malo en el diseño de RAID 6 y en su lugar optimiza en qué es bueno RAID 6.
Compensaremos la mala escritura aleatoria de RAID 6 con una caché NVMe y trucos de aprovisionamiento ligero.
Todavía no hemos habilitado DISCARD para RAID 6. Por lo tanto, no “inicializaremos” esta matriz por ahora. Haremos esto más tarde, después de instalar el sistema operativo.
HDD SATA
#mdadm --create --verbose --assume-clean /dev/md3 --chunk-size=512 --level=6 --raid-devices=8 /dev/sd[g-n]1
LVM en RAID NVMe
Para mayor velocidad, queremos colocar el sistema de archivos raíz en NVMe RAID 1, que es /dev/md0.
Sin embargo, seguiremos necesitando esta matriz rápida para otras necesidades, como intercambio, metadatos y metadatos LVM-cache y LVM-thin, por lo que crearemos un LVM VG en esta matriz.
#pvcreate /dev/md0
#vgcreate root /dev/md0
Creemos una partición para el sistema de archivos raíz.
#lvcreate -L 128G --name root root
Creemos una partición para intercambiar según el tamaño de la RAM.
#lvcreate -L 32G --name swap root
instalación del sistema operativo
En total tenemos todo lo necesario para instalar el sistema.
Inicie el asistente de instalación del sistema desde el entorno. Ubuntu En vivo. Instalación estándar. Solo en la etapa de selección de disco necesita especificar lo siguiente:
- /dev/md1, - punto de montaje /boot, FS - BTRFS
- /dev/root/root (también conocido como /dev/mapper/root-root), - punto de montaje / (raíz), FS - BTRFS
- /dev/root/swap (también conocido como /dev/mapper/root-swap), - utilizar como partición de intercambio
- Instale el gestor de arranque en /dev/sda
Cuando selecciona BTRFS como sistema de archivos raíz, el instalador creará automáticamente dos volúmenes BTRFS llamados "@" para / (raíz) y "@home" para /home.
Comencemos la instalación...
La instalación finalizará con un cuadro de diálogo modal que le informará de un error en la instalación del gestor de arranque. Lamentablemente, no podrá cerrar este cuadro de diálogo con las herramientas predeterminadas y continuar con la instalación. Cierre la sesión y vuelva a iniciarla para acceder a un escritorio limpio. Ubuntu En vivo. Abre la terminal y de nuevo:
#sudo bash
Cree un entorno chroot para continuar con la 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
Configuremos la red y el nombre 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
Entremos en el entorno chroot:
#chroot /mnt/chroot
En primer lugar, entregaremos los paquetes:
apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm
Revisemos y arreglemos todos los paquetes que se instalaron mal debido a una instalación incompleta del sistema:
#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES
Si algo no funciona, es posible que deba editar /etc/apt/sources.list primero
Ajustemos los parámetros del módulo RAID 6 para habilitar TRIM/DISCARD:
#cat >/etc/modprobe.d/raid456.conf << EOF
options raid456 devices_handle_discard_safely=1
EOF
Modifiquemos un poco nuestras 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
Qué era..?Hemos creado un conjunto de reglas udev que harán lo siguiente:
- Establezca el tamaño de la caché de bloques RAID 6 en un valor apropiado para 2020. El valor predeterminado parece no haber cambiado desde su creación. Linuxy no ha sido suficiente durante mucho tiempo.
- Reserve un mínimo de IO mientras duren las comprobaciones/sincronizaciones de la matriz. Esto es para evitar que sus matrices se atasquen en un estado de sincronización eterna bajo carga.
- Limite el IO máximo durante las comprobaciones/sincronización de matrices. Esto es necesario para que la sincronización/comprobación de los RAID SSD no queme sus unidades. Esto es especialmente cierto para NVMe. (¿Recuerdas lo del radiador? No estaba bromeando).
- Prohíba que los discos detengan la rotación del eje (HDD) a través de APM y establezca el tiempo de espera para los controladores de disco en 7 horas. Puede desactivar completamente APM si sus unidades pueden hacerlo (-B 255). Con el valor predeterminado, las unidades se detendrán después de cinco segundos. Luego, el sistema operativo quiere restablecer el caché del disco, los discos girarán nuevamente y todo comenzará de nuevo. Los discos tienen un número máximo limitado de rotaciones del husillo. Un ciclo predeterminado tan simple puede destruir fácilmente sus discos en un par de años. No todos los discos sufren esto, pero los nuestros son "portátiles", con la configuración predeterminada adecuada, que hace que el RAID parezca un mini-MAID.
- Instale readahead en discos (rotativos) de 1 megabyte: dos bloques consecutivos/fragmento RAID 6
- Deshabilite la lectura anticipada en las 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
Porqué es eso..?Buscaremos la partición /boot por UUID. En teoría, los nombres de las matrices podrían cambiar.
Buscaremos las secciones restantes por nombres LVM en la notación /dev/mapper/vg-lv, porque identifican particiones de forma bastante única.
No utilizamos UUID para LVM porque El UUID de los volúmenes LVM y sus instantáneas pueden ser los mismos.¿Montar /dev/mapper/root-root... dos veces?Sí. Exactamente. Característica de BTRFS. Este sistema de archivos se puede montar varias veces con diferentes subvols.
Debido a esta misma característica, recomiendo nunca crear instantáneas LVM de volúmenes BTRFS activos. Es posible que recibas una sorpresa cuando reinicies.
Regeneremos la configuración de mdadm:
#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf
Ajustemos la configuración de LVM:
#cat >>/etc/lvm/lvmlocal.conf << EOF
activación {
umbral de autoextensión de grupo delgado=90
porcentaje de autoextensión de piscina delgada=5
}
asignación {
fragmentos máximos del grupo de caché=2097152
}
dispositivos {
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.*$|"]
problemas_descartados=1
}
EOF
Qué era..?Hemos habilitado la expansión automática de los pools delgados LVM al alcanzar el 90% del espacio ocupado por un 5% del volumen.
Hemos aumentado la cantidad máxima de bloques de caché para el caché LVM.
Hemos impedido que LVM busque volúmenes LVM (PV) en:
- dispositivos que contienen caché LVM (cdata)
- dispositivos almacenados en caché usando caché LVM, sin pasar por el caché ( _corig). En este caso, el dispositivo almacenado en caché se seguirá escaneando a través del caché (sólo ).
- dispositivos que contienen metadatos de caché LVM (cmeta)
- todos los dispositivos en VG con el nombre imágenes. Aquí tendremos imágenes de disco de máquinas virtuales y no queremos que LVM en el host active volúmenes que pertenecen al sistema operativo invitado.
- todos los dispositivos en VG con el nombre de copia de seguridad. Aquí tendremos copias de seguridad de las imágenes de las máquinas virtuales.
- todos los dispositivos cuyo nombre termina en “gpv” (volumen físico invitado)
Hemos habilitado la compatibilidad con DISCARD al liberar espacio libre en LVM VG. Ten cuidado. Esto hará que eliminar LV en el SSD lleve bastante tiempo. Esto se aplica especialmente al SSD RAID 6. Sin embargo, según el plan, usaremos aprovisionamiento ligero, por lo que esto no nos obstaculizará en absoluto.
Actualicemos la imagen de initramfs:
#update-initramfs -u -k all
Instalar y configurar grub:
#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc
¿Qué discos elegir?Todos los que son SD*. El sistema debe poder arrancar desde cualquier unidad SATA o SSD que funcione.
¿Por qué agregaron os-prober...?Para una independencia excesiva y manos juguetonas.
No funciona correctamente si uno de los RAID está en estado degradado. Intenta buscar el sistema operativo en las particiones que se utilizan en las máquinas virtuales que se ejecutan en este hardware.
Si lo necesitas puedes dejarlo, pero ten en cuenta todo lo anterior. Recomiendo buscar recetas para deshacerse de las manos traviesas en línea.
Con esto hemos completado la instalación inicial. Es hora de reiniciar en el sistema operativo recién instalado. No olvides quitar el Live CD/USB de arranque.
#exit
#reboot
Seleccione cualquiera de los SSD SATA como dispositivo de arranque.
LVM en SSD SATA
En este punto, ya iniciamos el nuevo sistema operativo, configuramos la red, apt, abrimos el emulador de terminal y ejecutamos:
#sudo bash
Seguimos
“Inicialice” la matriz desde SATA SSD:
#blkdiscard /dev/md2
Si no funciona, intente:
#blkdiscard --step 65536 /dev/md2
Cree LVM VG en SATA SSD:
#pvcreate /dev/md2
#vgcreate data /dev/md2
¿Por qué otro VG...?De hecho, ya tenemos un VG llamado root. ¿Por qué no agregar todo en un solo VG?
Si hay varios PV en un VG, para que el VG se active correctamente, todos los PV deben estar presentes (en línea). La excepción es LVM RAID, que no utilizamos deliberadamente.
Realmente queremos que si hay una falla (pérdida de datos de lectura) en cualquiera de las matrices RAID 6, el sistema operativo arranque normalmente y nos dé la oportunidad de solucionar el problema.
Para hacer esto, en el primer nivel de abstracción aislaremos cada tipo de "medio" físico en un VG separado.
Científicamente hablando, diferentes matrices RAID pertenecen a diferentes "dominios de confiabilidad". No deberías crear un punto común adicional de falla para ellos metiéndolos en un VG.
La presencia de LVM a nivel de “hardware” nos permitirá cortar arbitrariamente piezas de diferentes matrices RAID combinándolas de diferentes maneras. Por ejemplo - ejecutar simultáneamente bcache + LVM Thin, bcache + BTRFS, LVM cache + LVM Thin, una configuración ZFS compleja con cachés, o cualquier otra mezcla infernal para probar y compararlo todo.
A nivel de "hardware", no utilizaremos nada más que los viejos volúmenes LVM "gruesos". La excepción a esta regla puede ser la partición de respaldo.
Creo que en ese momento muchos lectores ya habían comenzado a sospechar algo sobre la muñeca nido.
LVM en disco duro SATA
#pvcreate /dev/md3
#vgcreate backup /dev/md3
¿Nuevo VG otra vez...?Realmente queremos que si falla la matriz de discos que usaremos para la copia de seguridad de los datos, nuestro sistema operativo continúe funcionando normalmente, manteniendo el acceso a los datos que no son de la copia de seguridad como de costumbre. Por lo tanto, para evitar problemas de activación de VG, creamos un VG separado.
Configurar la caché LVM
Creemos un LV en NVMe RAID 1 para usarlo como dispositivo de almacenamiento en caché.
#lvcreate -L 70871154688B --name cache root
¿Por qué hay tan poco...?El caso es que nuestros SSD NVMe también cuentan con caché SLC. 4 gigabytes “libres” y 18 gigabytes dinámicos debido al espacio libre ocupado en el MLC de 3 bits. Una vez agotada esta caché, los SSD NVMe no serán mucho más rápidos que nuestros SSD SATA con caché. En realidad, por esta razón, no tiene sentido para nosotros hacer que la partición de caché LVM sea mucho más grande que el doble del tamaño de la caché SLC de la unidad NVMe. Para las unidades NVMe utilizadas, el autor considera razonable crear entre 32 y 64 gigabytes de caché.
El tamaño de partición indicado es necesario para organizar 64 gigabytes de caché, metadatos de caché y copia de seguridad de metadatos.
Además, observo que después de un apagado sucio del sistema, LVM marcará todo el caché como sucio y se sincronizará nuevamente. Además, esto se repetirá cada vez que se utilice lvchange en este dispositivo hasta que el sistema se reinicie nuevamente. Por lo tanto, recomiendo recrear inmediatamente el caché utilizando el script adecuado.
Creemos un LV en SATA RAID 6 para usarlo como dispositivo en caché.
#lvcreate -L 3298543271936B --name cache data
¿Por qué sólo tres terabytes...?De modo que, si es necesario, puedas utilizar SATA SSD RAID 6 para algunas otras necesidades. El tamaño del espacio en caché se puede aumentar dinámicamente, sobre la marcha, sin detener el sistema. Para hacer esto, debe detener temporalmente y volver a habilitar el caché, pero la ventaja distintiva de LVM-cache sobre, por ejemplo, bcache es que esto se puede hacer sobre la marcha.
Creemos un nuevo VG para el almacenamiento en caché.
#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache
Creemos un LV en el dispositivo almacenado en caché.
#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache
Aquí ocupamos inmediatamente todo el espacio libre en /dev/data/cache para que todas las demás particiones necesarias se crearan inmediatamente en /dev/root/cache. Si creaste algo en el lugar equivocado, puedes moverlo usando pvmove.
Creemos y habilitemos el 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 qué ese tamaño de trozo...?A través de experimentos prácticos, el autor pudo descubrir que el mejor resultado se logra si el tamaño del bloque de caché LVM coincide con el tamaño del bloque delgado LVM. Además, cuanto menor sea el tamaño, mejor funcionará la configuración en una grabación aleatoria.
64k es el tamaño de bloque mínimo permitido para LVM delgado.
¡Tenga cuidado al escribir!Sí. Este tipo de caché difiere la sincronización de escritura en el dispositivo almacenado en caché. Esto significa que si se pierde el caché, es posible que pierda datos en el dispositivo almacenado en caché. Más adelante, el autor os contará qué medidas, además de NVMe RAID 1, se pueden tomar para compensar este riesgo.
Este tipo de caché se eligió intencionalmente para compensar el bajo rendimiento de escritura aleatoria de RAID 6.
Veamos lo que tenemos:
#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)
Solo [cachedata_corig] debe ubicarse en /dev/data/cache. Si algo anda mal, utilice pvmove.
Puede desactivar el caché si es necesario con un comando:
#lvconvert -y --uncache cache/cachedata
Esto se hace en línea. LVM simplemente sincronizará el caché con el disco, lo eliminará y cambiará el nombre de cachedata_corig a cachedata.
Configurando LVM delgado
Calculemos aproximadamente cuánto espacio necesitamos para los 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 hasta 4 gigabytes: 4294967296B
Multiplique por dos y agregue 4194304B para los metadatos de LVM PV: 8594128896B
Creemos una partición separada en NVMe RAID 1 para colocar metadatos delgados de LVM y su copia de seguridad en ella:
#lvcreate -L 8594128896B --name images root
Para qué..?Aquí puede surgir la pregunta: ¿por qué colocar los metadatos ligeros de LVM por separado si aún estarán almacenados en caché en NVMe y funcionarán rápidamente?
Aunque la velocidad es importante aquí, está lejos de ser la razón principal. El caso es que el caché es un punto de falla. Algo podría pasarle, y si los metadatos finos de LVM se almacenan en caché, todo se perderá por completo. Sin metadatos completos, será casi imposible ensamblar volúmenes delgados.
Al mover los metadatos a un volumen separado que no está almacenado en caché, pero que es rápido, garantizamos la seguridad de los metadatos en caso de pérdida o corrupción de la caché. En este caso, todos los daños causados por la pérdida de caché se localizarán dentro de volúmenes delgados, lo que simplificará el procedimiento de recuperación en varios órdenes de magnitud. Con una alta probabilidad, estos daños se repararán utilizando registros FS.
Además, si previamente se tomó una instantánea de un volumen delgado y después de eso el caché se sincronizó completamente al menos una vez, entonces, debido al diseño interno de LVM delgado, la integridad de la instantánea estará garantizada en caso de pérdida de caché. .
Creemos un nuevo VG que será responsable del aprovisionamiento ligero:
#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata
Creemos un grupo:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
¿Por qué -Z y?Además de para qué está destinado realmente este modo (para evitar que los datos de una máquina virtual se filtren a otra máquina virtual al redistribuir el espacio), la puesta a cero se utiliza adicionalmente para aumentar la velocidad de escritura aleatoria en bloques de menos de 64k. Cualquier escritura de menos de 64k en un área previamente no asignada del volumen delgado se alineará con los bordes de 64K en la caché. Esto permitirá que la operación se realice completamente a través del caché, sin pasar por el dispositivo almacenado en caché.
Movamos los LV a los PV correspondientes:
#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
Cheque:
#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)
Creemos un volumen delgado para las pruebas:
#lvcreate -V 64G --thin-pool thin-pool --name test images
Instalaremos paquetes para pruebas y seguimiento:
#apt-get install sysstat fio
Así podrás observar en tiempo real el comportamiento de nuestra configuración de almacenamiento:
#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í es como podemos probar nuestra 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
¡Con cuidado! ¡Recurso!Este código ejecutará 36 pruebas diferentes, cada una de las cuales durará 4 segundos. La mitad de las pruebas son para grabación. Puedes grabar mucho en NVMe en 4 segundos. Hasta 3 gigabytes por segundo. Por lo tanto, cada ejecución de pruebas de escritura puede consumir hasta 216 gigabytes de recursos SSD.
¿Lectura y escritura mezcladas?Sí. Tiene sentido ejecutar las pruebas de lectura y escritura por separado. Además, tiene sentido asegurarse de que todos los cachés estén sincronizados para que una escritura realizada anteriormente no afecte la lectura.
Los resultados variarán mucho durante el primer lanzamiento y los posteriores a medida que el caché y el volumen reducido se llenen, y también dependiendo de si el sistema logró sincronizar los cachés llenados durante el último lanzamiento.
Entre otras cosas, recomiendo medir la velocidad en un volumen delgado ya lleno del que se acaba de tomar una instantánea. El autor tuvo la oportunidad de observar cómo las escrituras aleatorias se aceleran bruscamente inmediatamente después de crear la primera instantánea, especialmente cuando el caché aún no está completamente lleno. Esto sucede debido a la semántica de copia en escritura, la alineación de la caché y los bloques de volumen delgados, y al hecho de que una escritura aleatoria en RAID 6 se convierte en una lectura aleatoria de RAID 6 seguida de una escritura en la caché. En nuestra configuración, la lectura aleatoria de RAID 6 es hasta 6 veces (la cantidad de SSD SATA en la matriz) más rápida que la escritura. Porque Los bloques para CoW se asignan secuencialmente desde un grupo reducido, luego la grabación, en su mayor parte, también se vuelve secuencial.
Ambas características se pueden utilizar a su favor.
Caché de instantáneas "coherentes"
Para reducir el riesgo de pérdida de datos en caso de daño/pérdida de la caché, el autor propone introducir la práctica de rotar instantáneas para garantizar su integridad en este caso.
En primer lugar, debido a que los metadatos de volumen reducido residen en un dispositivo sin caché, los metadatos serán consistentes y las posibles pérdidas se aislarán dentro de los bloques de datos.
El siguiente ciclo de rotación de instantáneas garantiza la integridad de los datos dentro de las instantáneas en caso de pérdida de caché:
- Para cada volumen delgado con el nombre <nombre>, cree una instantánea con el nombre <nombre>.cached
- Establezcamos el umbral de migración en un valor alto razonable:
#lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata - En el bucle verificamos la cantidad de bloques sucios en el caché:
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'hasta llegar a cero. Si el cero falta durante demasiado tiempo, se puede crear cambiando temporalmente la caché al modo de escritura directa. Sin embargo, teniendo en cuenta las características de velocidad de nuestras matrices SSD SATA y NVMe, así como su recurso TBW, podrá captar rápidamente el momento sin cambiar el modo de caché o su hardware consumirá por completo todos sus recursos en unos pocos días. Debido a limitaciones de recursos, el sistema, en principio, no puede estar por debajo del 100% de carga de escritura todo el tiempo. Nuestros SSD NVMe con una carga de escritura del 100 % agotarán completamente el recurso en 3, 4 días. Los SSD SATA solo durarán el doble. Por lo tanto, asumiremos que la mayor parte de la carga se destina a la lectura y que tenemos ráfagas relativamente breves de actividad extremadamente alta combinadas con una carga baja en promedio para la escritura. - Tan pronto como detectamos (o creamos) un cero, cambiamos el nombre de <nombre>.cached a <nombre>.committed. Se elimina el antiguo <nombre>.committed.
- Opcionalmente, si el caché está 100% lleno, se puede recrear mediante un script y así borrarlo. Con la caché medio vacía, el sistema funciona mucho más rápido a la hora de escribir.
- Establezca el umbral de migración en cero:
#lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedataEsto evitará temporalmente que el caché se sincronice con el medio principal. - Esperamos hasta que se acumulen muchos cambios en el caché.
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'o el cronómetro sonará. - Repetimos de nuevo.
¿Por qué las dificultades con el umbral migratorio...?El caso es que, en la práctica real, una grabación “aleatoria” no es del todo aleatoria. Si escribimos algo en un sector de 4 kilobytes de tamaño, existe una alta probabilidad de que en los próximos minutos se realice un registro en el mismo sector o en uno de los vecinos (+- 32K).
Al establecer el umbral de migración en cero, posponemos la sincronización de escritura en el SSD SATA y agregamos varios cambios a un bloque de 64K en el caché. Esto ahorra significativamente el recurso de SATA SSD.
¿Dónde está el código...?Desafortunadamente, el autor se considera insuficientemente competente en el desarrollo de scripts bash porque es 100% autodidacta y practica el desarrollo impulsado por “google”, por lo que cree que el terrible código que sale de sus manos no debería ser utilizado por nadie. demás.
Creo que los profesionales en este campo podrán representar de forma independiente toda la lógica descrita anteriormente, si es necesario, y tal vez incluso diseñarla bellamente como un servicio systemd, como intentó hacer el autor.
Un esquema de rotación de instantáneas tan simple nos permitirá no solo tener constantemente una instantánea completamente sincronizada en el SSD SATA, sino que también nos permitirá, utilizando la utilidad Thin_Delta, descubrir qué bloques se cambiaron después de su creación y, por lo tanto, localizar daños en los volúmenes principales, simplificando enormemente la recuperación.
RECORTAR/DESCARTAR en libvirt/KVM
Porque el almacenamiento de datos se utilizará para KVM que ejecuta libvirt, entonces sería una buena idea enseñar a nuestras VM no solo a ocupar espacio libre, sino también a liberar lo que ya no es necesario.
Esto se hace emulando el soporte TRIM/DISCARD en discos virtuales. Para hacer esto, necesita cambiar el tipo de controlador a virtio-scsi y editar el 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' modelo='virtio-scsi'>
LVM procesa correctamente dichos DISCARDs de los sistemas operativos invitados y los bloques se liberan correctamente tanto en la caché como en el grupo ligero. En nuestro caso, esto ocurre principalmente de forma retrasada, al eliminar la siguiente instantánea.
Copia de seguridad BTRFS
Utilice scripts ya preparados con крайней precaución y bajo su propio riesgoEl autor escribió este código él mismo y exclusivamente para sí mismo. Estoy seguro de que muchos usuarios experimentados Linux Existen recursos y avances similares, y no hay necesidad de copiar a otros.
Creemos un volumen en el dispositivo de respaldo:
#lvcreate -L 256G --name backup backup
Formateémoslo en BTRFS:
#mkfs.btrfs /dev/backup/backup
Creemos puntos de montaje y montemos las subsecciones raíz del sistema de archivos:
#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 valores predeterminados, espacio_caché, noatime, nodiratime 0 2
/dev/mapper/backup-backup /backup/btrfs/back btrfs valores predeterminados, espacio_caché, noatime, nodiratime 0 2
EOF
#montaje -a
#update-initramfs -u
#actualizar-grub
Creemos directorios para copias de seguridad:
#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot
Creemos un directorio para los scripts de respaldo:
#mkdir /root/btrfs-backup
Copiemos el guión:
Mucho código bash aterrador. Úselo bajo su propio riesgo. No escribas cartas enojadas al autor...#cat >/root/btrfs-backup/btrfs-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ARCHIVO_DE_SCRIPCIÓN="$(ruta_real $0)"
SCRIPT_DIR="$(nombre_directorio $SCRIPT_FILE)"
SCRIPT_NAME="$(nombre_base -s .sh $SCRIPT_FILE)"
ARCHIVO_DE_BLOQUEO="/dev/shm/$NOMBRE_DEL_SCRIPT.lock"
PREFIJO_FECHA='%Y-%m-%d'
FORMATO_FECHA=$PREFIJO_FECHA'-%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]'
SUFIJO_BASE=".@base"
PEND_SUFFIX=".@pend"
SUFIJO_SNAP=".@snap"
MONTAJES="/backup/btrfs/"
COPIAS DE SEGURIDAD="/backup/btrfs/back/remote/"
función terminar()
{
echo "$1" >&2
salir de 1
}
función wait_lock()
{
bandada 98
}
función esperar_bloqueo_o_terminar()
{
echo "Esperando bloqueo..."
esperar_bloqueo || terminar "No se pudo obtener el bloqueo. Saliendo..."
echo "Tengo bloqueo..."
}
sufijo de función()
{
FECHA_FORMATEADA=$(fecha +"$FORMATO_FECHA")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
función filter()
{
FECHA_FORMATEADA=$(fecha --fecha="$1" +"$PREFIJO_FECHA")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
función backup()
{
RUTA_DE_ORIGEN="$MONTAJES$1"
RUTA_DESTINATORIA="$COPIA DE SEGURIDAD$1"
RUTA_BASE_DE_ORIGEN="$MONTAJES$1$SUFIJO_BASE"
RUTA_BASE_DESTINATORIA="$COPIA_DE_RESERVA$1$SUFIJO_BASE"
TARGET_BASE_DIR="$(nombre_directorio $TARGET_BASE_PATH)"
RUTA_PEND_DE_ORIGEN="$MONTAJES$1$SUFIJO_PEND"
RUTA_PEND_DESTINATO="$COPIA_DE_RESERVA$1$SUFIJO_PEND"
si [ -d "$SOURCE_BASE_PATH" ]
y luego en
echo "$SOURCE_BASE_PATH encontrado"
más
echo "$SOURCE_BASE_PATH Archivo no encontrado creando instantánea de $SOURCE_PATH a $SOURCE_BASE_PATH"
instantánea de subvolumen btrfs -r $SOURCE_PATH $SOURCE_BASE_PATH
sincronizar
si [ -d "$TARGET_BASE_PATH" ]
y luego en
echo "$TARGET_BASE_PATH no está sincronizado con la fuente... eliminando..."
btrfs subvolumen eliminar -c $TARGET_BASE_PATH
sincronizar
fi
fi
si [ -d "$TARGET_BASE_PATH" ]
y luego en
echo "$TARGET_BASE_PATH encontrado"
más
echo "$TARGET_BASE_PATH no encontrado. Sincronizando con $TARGET_BASE_DIR"
btrfs envía $RUTA_BASE_DE_ORIGEN | btrfs recibe $TARGET_BASE_DIR
sincronizar
fi
si [ -d "$SOURCE_PEND_PATH" ]
y luego en
echo "$SOURCE_PEND_PATH encontrado eliminando..."
btrfs subvolumen eliminar -c $SOURCE_PEND_PATH
sincronizar
fi
instantánea de subvolumen btrfs -r $SOURCE_PATH $SOURCE_PEND_PATH
sincronizar
si [ -d "$TARGET_PEND_PATH" ]
y luego en
echo "$TARGET_PEND_PATH encontrado eliminando..."
btrfs subvolumen eliminar -c $TARGET_PEND_PATH
sincronizar
fi
echo "Enviando $SOURCE_PEND_PATH a $TARGET_PEND_PATH"
btrfs send -p $RUTA_BASE_DE_ORIGEN $RUTA_PEND_DE_ORIGEN | btrfs recibe $TARGET_BASE_DIR
sincronizar
SUFIJO DE FECHA OBJETIVO=$(sufijo)
instantánea de subvolumen btrfs -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sincronizar
btrfs subvolumen eliminar -c $SOURCE_BASE_PATH
sincronizar
btrfs subvolumen eliminar -c $TARGET_BASE_PATH
sincronizar
mv $RUTA_PEND_DE_ORIGEN $RUTA_BASE_DE_ORIGEN
mv $RUTA_PEND_DESTINO $RUTA_BASE_DESTINO
sincronizar
}
lista de funciones()
{
LISTA_RUTA_BASE_DESTINOS="$COPIA_DE_RESERVA$1$SUFIJO_BASE"
LIST_TARGET_BASE_DIR="$(nombre_directorio $LIST_TARGET_BASE_PATH)"
LIST_TARGET_BASE_NAME="$(nombre_base -s .$SUFIJO_BASE $LIST_TARGET_BASE_PATH)"
buscar "$LIST_TARGET_BASE_DIR" -máxprofundidad 1 -minprofundidad 1 -tipo d -printf "%fn" | grep "${LISTA_NOMBRE_BASE_DESTINO/$SUFIJO_BASE/$SUFIJO_SNAP}.$FECHA_EXPRESIÓN_REGULAR"
}
función remove()
{
ELIMINAR_RUTA_BASE_DESTINATORIA="$COPIA_DE_SEGURIDAD$1$SUFIJO_BASE"
ELIMINAR_DIRECCIÓN_BASE_DESTINATARIA="$(nombre_directorio $ELIMINAR_RUTA_BASE_DESTINATARIA)"
btrfs subvolumen eliminar -c $REMOVE_TARGET_BASE_DIR/$2
sincronizar
}
función removeall()
{
DESREF_FECHA="$2"
FILTRO="$(filtro "$DATE_OFFSET")"
mientras lee -r INSTANTÁNEA; hacer
eliminar "$1" "$SNAPSHOT"
hecho < <(lista "$1" | grep "$FILTER")
}
(
COMANDO="$1"
Turno
caso "$COMMAND" en
"--ayuda")
echo "Ayuda"
;;
"sufijo")
sufijo
;;
"filtrar")
filtro "$1"
;;
"respaldo")
esperar_bloquear_o_terminar
copia de seguridad "$1"
;;
"lista")
lista "$1"
;;
"eliminar")
esperar_bloquear_o_terminar
eliminar "$1" "$2"
;;
"eliminartodo")
esperar_bloquear_o_terminar
eliminar todo "$1" "$2"
;;
*)
echo "Ninguno.."
;;
esac
) 98>$ARCHIVO DE BLOQUEO
EOF
¿Qué hace siquiera...?Contiene un conjunto de comandos simples para crear instantáneas BTRFS y copiarlas a otro FS usando el envío/recepción BTRFS.
El primer lanzamiento puede ser relativamente largo, porque... Al principio, se copiarán todos los datos. Los futuros lanzamientos serán muy rápidos, porque... Sólo se copiarán los cambios.
Otro script que pondremos en cron:
Algo más de código bash#cat >/root/btrfs-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ARCHIVO_DE_SCRIPCIÓN="$(ruta_real $0)"
SCRIPT_DIR="$(nombre_directorio $SCRIPT_FILE)"
SCRIPT_NAME="$(nombre_base -s .sh $SCRIPT_FILE)"
BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RETENCIÓN="-60 días"
$BACKUP_SCRIPT raíz de respaldo/@
$BACKUP_SCRIPT removeall root/@ "$RETENCIÓN"
$BACKUP_SCRIPT copia de seguridad raíz/@home
$BACKUP_SCRIPT removeall root/@home "$RETENCIÓN"
$BACKUP_SCRIPT copia de seguridad de arranque/
$BACKUP_SCRIPT removeall boot/ "$RETENCIÓN"
EOF
Qué hace..?Crea y sincroniza instantáneas incrementales de los volúmenes BTRFS enumerados en el FS de respaldo. Después de esto, elimina todas las imágenes creadas hace 60 días. Después del lanzamiento, aparecerán instantáneas fechadas de los volúmenes enumerados en los subdirectorios /backup/btrfs/back/remote/.
Démosle derechos de ejecución al código:
#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh
Comprobémoslo y pongámoslo en el 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 seguridad delgada LVM
Creemos un grupo reducido en el dispositivo de respaldo:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool
Instalemos ddrescue, porque... Los scripts utilizarán esta herramienta:
#apt-get install gddrescue
Creemos un directorio para scripts:
#mkdir /root/lvm-thin-backup
Copiemos los scripts:
Mucha fiesta por 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"
ARCHIVO_DE_SCRIPCIÓN="$(ruta_real $0)"
SCRIPT_DIR="$(nombre_directorio $SCRIPT_FILE)"
SCRIPT_NAME="$(nombre_base -s .sh $SCRIPT_FILE)"
ARCHIVO_DE_BLOQUEO="/dev/shm/$NOMBRE_DEL_SCRIPT.lock"
PREFIJO_FECHA='%Y-%m-%d'
FORMATO_FECHA=$PREFIJO_FECHA'-%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]'
SUFIJO_BASE=".base"
PEND_SUFFIX=".pend"
SNAP_SUFFIX=".snap"
COPIAS DE SEGURIDAD="copia de seguridad"
BACKUPS_POOL="pool delgado"
exportar LVM_SUPPRESS_FD_WARNINGS=1
función terminar()
{
echo "$1" >&2
salir de 1
}
función wait_lock()
{
bandada 98
}
función esperar_bloqueo_o_terminar()
{
echo "Esperando bloqueo..."
esperar_bloqueo || terminar "No se pudo obtener el bloqueo. Saliendo..."
echo "Tengo bloqueo..."
}
sufijo de función()
{
FECHA_FORMATEADA=$(fecha +"$FORMATO_FECHA")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
función filter()
{
FECHA_FORMATEADA=$(fecha --fecha="$1" +"$PREFIJO_FECHA")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
función read_thin_id {
lvs --rows --reportformat basic --quiet -othin_id "$1/$2" | awk '{imprimir $2}'
}
función read_pool_lv {
lvs --rows --reportformat básico --quiet -opool_lv "$1/$2" | awk '{imprimir $2}'
}
función read_lv_dm_path {
lvs --rows --reportformat basic --quiet -olv_dm_path "$1/$2" | awk '{imprimir $2}'
}
función read_lv_active {
lvs --rows --reportformat básico --quiet -olv_active "$1/$2" | awk '{imprimir $2}'
}
función read_lv_chunk_size {
lvs --filas --formatodeinforme básico --quiet --unidades b --nosuffix -ochunk_size "$1/$2" | awk '{imprimir $2}'
}
función read_lv_size {
lvs --filas --formatodeinforme básico --quiet --unidades b --nosuffix -olv_size "$1/$2" | awk '{imprimir $2}'
}
función activar_volumen {
lvchange -ay -Ky "$1/$2"
}
función desactivar_volumen {
lvchange -an "$1/$2"
}
función read_thin_metadata_snap {
estado de dmsetup "$1" | awk '{imprimir $7}'
}
función thindiff()
{
DIFF_VG="$1"
DIFF_SOURCE="$2"
DIFF_TARGET="$3"
DIFF_SOURCE_POOL=$(lectura_pool_lv $DIFF_VG $DIFF_SOURCE)
DIFF_GRUPO_DESTINOS=$(nivel_de_grupo_de_lectura $DIFF_VG $DIFF_DESTINOS)
si [ "$DIFF_SOURCE_POOL" == "" ]
y luego en
(>&2 echo "La fuente LV no es delgada.")
salir de 1
fi
si [ "$DIFF_TARGET_POOL" == "" ]
y luego en
(>&2 echo "El LV objetivo no es delgado.")
salir de 1
fi
si [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
y luego en
(>&2 echo "Los LV de origen y destino pertenecen a diferentes grupos delgados.")
salir de 1
fi
DIFF_POOL_PATH=$(ruta_dm_lv_lectura $DIFF_VG $DIFF_SOURCE_POOL)
DIFF_SOURCE_ID=$(id_fino_de_lectura $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_ID=$(id_fino_de_lectura $DIFF_VG $DIFF_TARGET)
DIFF_POOL_PATH_TPOOL="$DIFF_POOL_PATH-tpool"
DIFF_POOL_PATH_TMETA="$DIFF_POOL_PATH"_tmeta
DIFF_POOL_METADATA_SNAP=$(lectura_de_metadatos_delgados_snap $DIFF_POOL_PATH_TPOOL)
si [ "$DIFF_POOL_METADATA_SNAP" != "-" ]
y luego en
(>&2 echo "Ya existe una instantánea de metadatos del grupo delgado. Se supone que está obsoleta. Se publicará la instantánea de metadatos en 5 segundos.")
sueño 5
Mensaje de configuración de dm $DIFF_POOL_PATH_TPOOL 0 captura de metadatos de lanzamiento
fi
Mensaje de configuración de dm $DIFF_POOL_PATH_TPOOL 0 reserva_metadatos_snap
DIFF_POOL_METADATA_SNAP=$(lectura_de_metadatos_delgados_snap $DIFF_POOL_PATH_TPOOL)
si [ "$DIFF_POOL_METADATA_SNAP" == "-" ]
y luego en
(>&2 echo "Error al crear la instantánea de metadatos del grupo delgado.")
salir de 1
fi
#Mantenemos la salida en variable porque la instantánea de metadatos debe publicarse anticipadamente.
DIFF_DATA=$(delta_delta -m$DIFF_POOL_METADATA_SNAP --snap1 $DIFF_SOURCE_ID --snap2 $DIFF_TARGET_ID $DIFF_POOL_PATH_TMETA)
Mensaje de configuración de dm $DIFF_POOL_PATH_TPOOL 0 captura de metadatos de lanzamiento
eco $"$DIFF_DATA" | grep -E 'diferente|solo_izquierda|solo_derecha' | sed 's/
}
función thinsync()
{
SYNC_VG="$1"
SYNC_PEND="$2"
SYNC_BASE="$3"
OBJETIVO DE SINCRONIZACIÓN="$4"
SYNC_PEND_POOL=$(lectura_pool_lv $SYNC_VG $SYNC_PEND)
TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN=$(tamaño_de_fragmento_de_unidad_de_lectura $SYNC_VG $SYNC_PEND_POOL)
SYNC_PEND_PATH=$(ruta_dm_lv_lectura $SYNC_VG $SYNC_PEND)
activar_volumen $SYNC_VG $SYNC_PEND
mientras lee -r ACCIÓN_SINCRONIZACIÓN DESPLAZAMIENTO_SINCRONIZACIÓN LONGITUD_SINCRONIZACIÓN ; hacer
DESPLAZAMIENTO_DE_SINCRONIZACIÓN_BYTES=$((DESPLAZAMIENTO_DE_SINCRONIZACIÓN * TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN))
LONGITUD_DE_SINCRONIZACIÓN_BYTES=$((LONGITUD_DE_SINCRONIZACIÓN * TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN))
si [ "$SYNC_ACTION" == "copy" ]
y luego en
ddrescue --quiet --force --posición de entrada=$SYNC_OFFSET_BYTES --posición de salida=$SYNC_OFFSET_BYTES --tamaño=$SYNC_LENGTH_BYTES "$SYNC_PEND_PATH" "$SYNC_TARGET"
fi
si [ "$SYNC_ACTION" == "descartar" ]
y luego en
blkdiscard -o $SYNC_OFFSET_BYTES -l $SYNC_LENGTH_BYTES "$SYNC_TARGET"
fi
hecho < <(thindiff "$SYNC_VG" "$SYNC_PEND" "$SYNC_BASE")
}
función discard_volume()
{
DESCARTAR_VG="$1"
DESCARTAR_LV="$2"
DESCARTAR_RUTA_LV=$(read_lv_dm_path "$DESCARTAR_VG" "$DESCARTAR_LV")
si [ "$DISCARD_LV_PATH" != "" ]
y luego en
echo "$DISCARD_LV_PATH encontrado"
más
echo "$DISCARD_LV no encontrado en $DISCARD_VG"
salir de 1
fi
DESCARTAR_GRUPO_LV=$(lectura_grupo_lv $DESCARTAR_VG $DESCARTAR_LV)
TAMAÑO_LV_DESCARTAR=$(tamaño_lv_lectura "$DESCARTAR_VG" "$DESCARTAR_LV")
lvremove -y --quiet "$DESCARGAR_RUTA_LV" || salida 1
lvcreate --thin-pool "$DESCARTAR_GRUPO_LV" -V "$DESCARTAR_TAMAÑO_LV"B --name "$DESCARTAR_LV" "$DESCARTAR_VG" || salida 1
}
función backup()
{
FUENTE_VG="$1"
FUENTE_LV="$2"
TARGET_VG="$COPIAS DE SEGURIDAD"
OBJETIVO_LV="$FUENTE_VG-$FUENTE_LV"
FUENTE_BASE_LV="$FUENTE_LV$SUFIJO_BASE"
OBJETIVO_BASE_LV="$OBJETIVO_LV$SUFIJO_BASE"
FUENTE_PEND_LV="$FUENTE_LV$SUFIJO_PEND"
OBJETIVO_PEND_LV="$OBJETIVO_LV$SUFIJO_PEND"
RUTA BASE DE ORIGEN LV = $(ruta_dm_lv_lectura "$VG_DE_ORIGEN" "$LV_BASE_DE_ORIGEN")
RUTA DE FUENTE_PEND_LV=$(ruta_dm_leer_lv "$FUENTE_VG" "$FUENTE_PEND_LV")
RUTA_DEL_VÍNCULO_BASE_OBJETIVO=$(ruta_dm_del_vínculo_de_lectura "$VG_DEL_VÍNCULO" "$LV_BASE_OBJETIVO")
RUTA OBJETIVO_PEND_LV=$(ruta_dm_lectura_lv "$OBJETIVO_VG" "$OBJETIVO_PEND_LV")
si [ "$SOURCE_BASE_LV_PATH" != "" ]
y luego en
echo "$SOURCE_BASE_LV_PATH encontrado"
más
echo "No se encontró la base de origen al crear la instantánea de $SOURCE_VG/$SOURCE_LV a $SOURCE_VG/$SOURCE_BASE_LV"
lvcreate --quiet --snapshot --name "$FUENTE_BASE_LV" "$FUENTE_VG/$FUENTE_LV" || salida 1
RUTA BASE DE ORIGEN LV = $(ruta_dm_lv_lectura "$VG_DE_ORIGEN" "$LV_BASE_DE_ORIGEN")
activar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
echo "Descartando $SOURCE_BASE_LV_PATH ya que necesitamos iniciar".
GRUPO_BASE_DE_ORIGEN=$(nivel_de_grupo_de_lectura $VG_DE_ORIGEN $LV_BASE_DE_ORIGEN)
TAMAÑO DE TROZO BASE DE ORIGEN = $(tamaño de trozo de lectura $VG DE ORIGEN $GRUPO BASE DE ORIGEN)
descartar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
sincronizar
si [ "$TARGET_BASE_LV_PATH" != "" ]
y luego en
echo "$TARGET_BASE_LV_PATH no está sincronizado con la fuente... eliminando..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || salida 1
RUTA_DEL_VÍNCULO_BASE_OBJETIVO=$(ruta_dm_del_vínculo_de_lectura "$VG_DEL_VÍNCULO" "$LV_BASE_OBJETIVO")
sincronizar
fi
fi
TAMAÑO BASE DE ORIGEN=$(tamaño_lv_lectura "$VG_BASE_DE_ORIGEN" "$LV_BASE_DE_ORIGEN")
si [ "$TARGET_BASE_LV_PATH" != "" ]
y luego en
echo "$TARGET_BASE_LV_PATH encontrado"
más
echo "$TARGET_VG/$TARGET_LV no encontrado. Creando volumen vacío."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || salida 1
echo "Tengo que reiniciar el sistema. Descartando la fuente en $SOURCE_BASE_LV_PATH"
activar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
GRUPO_BASE_DE_ORIGEN=$(nivel_de_grupo_de_lectura $VG_DE_ORIGEN $LV_BASE_DE_ORIGEN)
TAMAÑO DE TROZO BASE DE ORIGEN = $(tamaño de trozo de lectura $VG DE ORIGEN $GRUPO BASE DE ORIGEN)
descartar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
GRUPO_BASE_OBJETIVO=$(nivel_grupo_lectura $VG_OBJETIVO $LV_BASE_OBJETIVO)
TAMAÑO_DE_TROZO_BASE_DESTINATARIO=$(tamaño_de_trozo_lv_lectura $VG_DESTINATARIO $GRUPO_BASE_DESTINATARIO)
RUTA_DEL_VÍNCULO_BASE_OBJETIVO=$(ruta_dm_del_vínculo_de_lectura "$VG_DEL_VÍNCULO" "$LV_BASE_OBJETIVO")
echo "Descartando objetivo en $TARGET_BASE_LV_PATH"
descartar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
sincronizar
fi
si [ "$SOURCE_PEND_LV_PATH" != "" ]
y luego en
echo "$SOURCE_PEND_LV_PATH encontrado eliminando..."
lvremove -y --quiet "$RUTA_LV_PEND_ORIGEN" || salida 1
sincronizar
fi
lvcreate --quiet --snapshot --name "$FUENTE_PEND_LV" "$FUENTE_VG/$FUENTE_LV" || salida 1
RUTA DE FUENTE_PEND_LV=$(ruta_dm_leer_lv "$FUENTE_VG" "$FUENTE_PEND_LV")
sincronizar
si [ "$TARGET_PEND_LV_PATH" != "" ]
y luego en
echo "$TARGET_PEND_LV_PATH encontrado eliminando..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
sincronizar
fi
lvcreate --quiet --snapshot --name "$TARGET_PEND_LV" "$TARGET_VG/$TARGET_BASE_LV" || salida 1
RUTA OBJETIVO_PEND_LV=$(ruta_dm_lectura_lv "$OBJETIVO_VG" "$OBJETIVO_PEND_LV")
TAMAÑO DE LV PENDIDO DE ORIGEN = $(tamaño de lv leído "$VG DE ORIGEN" "$VG DE ORIGEN")
lvresize -L "$TAMAÑO DE LV DE PENDENCIA DE ORIGEN"B "$RUTA DE LV DE PENDENCIA DE DESTINO"
activar_volumen "$TARGET_VG" "$TARGET_PEND_LV"
echo "Sincronizando $SOURCE_PEND_LV_PATH con $TARGET_PEND_LV_PATH"
thinsync "$VG_ORIGEN" "$LV_PEND_ORIGEN" "$LV_BASE_ORIGEN" "$RUTA_LV_PEND_OBJETIVO" || salida 1
sincronizar
SUFIJO DE FECHA OBJETIVO=$(sufijo)
lvcreate --quiet --snapshot --name "$TARGET_LV$TARGET_DATE_SUFFIX" "$TARGET_VG/$TARGET_PEND_LV" || salida 1
sincronizar
lvremove --quiet -y "$RUTA_LV_BASE_DE_ORIGEN" || salida 1
sincronizar
lvremove --quiet -y "$RUTA_LV_BASE_TARGET" || salida 1
sincronizar
lvrename -y "$FUENTE_VG/$FUENTE_PEND_LV" "$FUENTE_BASE_LV" || salida 1
lvrename -y "$TARGET_VG/$TARGET_PEND_LV" "$TARGET_BASE_LV" || salida 1
sincronizar
desactivar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
desactivar_volumen "$SOURCE_VG" "$SOURCE_BASE_LV"
}
función verificar()
{
FUENTE_VG="$1"
FUENTE_LV="$2"
TARGET_VG="$COPIAS DE SEGURIDAD"
OBJETIVO_LV="$FUENTE_VG-$FUENTE_LV"
FUENTE_BASE_LV="$FUENTE_LV$SUFIJO_BASE"
OBJETIVO_BASE_LV="$OBJETIVO_LV$SUFIJO_BASE"
RUTA_DEL_VÍNCULO_BASE_OBJETIVO=$(ruta_dm_del_vínculo_de_lectura "$VG_DEL_VÍNCULO" "$LV_BASE_OBJETIVO")
RUTA BASE DE ORIGEN LV = $(ruta_dm_lv_lectura "$VG_DE_ORIGEN" "$LV_BASE_DE_ORIGEN")
si [ "$SOURCE_BASE_LV_PATH" != "" ]
y luego en
echo "$SOURCE_BASE_LV_PATH encontrado"
más
echo "$SOURCE_BASE_LV_PATH no encontrado"
salir de 1
fi
si [ "$TARGET_BASE_LV_PATH" != "" ]
y luego en
echo "$TARGET_BASE_LV_PATH encontrado"
más
echo "$TARGET_BASE_LV_PATH no encontrado"
salir de 1
fi
activar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
activar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
eco Comparando "$SOURCE_BASE_LV_PATH" con "$TARGET_BASE_LV_PATH"
cmp "$RUTA_LV_BASE_ORIGEN" "$RUTA_LV_BASE_DESTINO"
eco Hecho...
desactivar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
desactivar_volumen "$SOURCE_VG" "$SOURCE_BASE_LV"
}
función resync()
{
FUENTE_VG="$1"
FUENTE_LV="$2"
TARGET_VG="$COPIAS DE SEGURIDAD"
OBJETIVO_LV="$FUENTE_VG-$FUENTE_LV"
FUENTE_BASE_LV="$FUENTE_LV$SUFIJO_BASE"
OBJETIVO_BASE_LV="$OBJETIVO_LV$SUFIJO_BASE"
RUTA_DEL_VÍNCULO_BASE_OBJETIVO=$(ruta_dm_del_vínculo_de_lectura "$VG_DEL_VÍNCULO" "$LV_BASE_OBJETIVO")
RUTA BASE DE ORIGEN LV = $(ruta_dm_lv_lectura "$VG_DE_ORIGEN" "$LV_BASE_DE_ORIGEN")
si [ "$SOURCE_BASE_LV_PATH" != "" ]
y luego en
echo "$SOURCE_BASE_LV_PATH encontrado"
más
echo "$SOURCE_BASE_LV_PATH no encontrado"
salir de 1
fi
si [ "$TARGET_BASE_LV_PATH" != "" ]
y luego en
echo "$TARGET_BASE_LV_PATH encontrado"
más
echo "$TARGET_BASE_LV_PATH no encontrado"
salir de 1
fi
activar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
activar_volumen "$FUENTE_VG" "$FUENTE_BASE_LV"
GRUPO_BASE_DE_ORIGEN=$(nivel_de_grupo_de_lectura $VG_DE_ORIGEN $LV_BASE_DE_ORIGEN)
TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN=$(tamaño_de_fragmento_de_nivel_de_lectura $VG_DE_ORIGEN $GRUPO_BASE_DE_ORIGEN)
echo Sincronizando "$SOURCE_BASE_LV_PATH" con "$TARGET_BASE_LV_PATH"
CMP_OFFSET=0
mientras [[ "$CMP_OFFSET" != "" ]] ; hacer
CMP_MISMATCH=$(cmp -i "$CMP_OFFSET" "$RUTA_LV_BASE_ORIGEN" "$RUTA_LV_BASE_DESTINO" | grep differ | awk '{print $5}' | sed 's/,//g' )
si [[ "$CMP_MISMATCH" != "" ]] ; entonces
CMP_OFFSET=$((CMP_MISMATCH + CMP_OFFSET))
DESPLAZAMIENTO_DE_SINCRONIZACIÓN_BYTES=$(( ( DESPLAZAMIENTO_DE_CMP / TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN ) * TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN ))
LONGITUD_DE_SINCRONIZACIÓN_BYTES=$(( TAMAÑO_DE_BLOQUE_DE_SINCRONIZACIÓN ))
echo "Sincronizando $SYNC_LENGTH_BYTES bytes en $SYNC_OFFSET_BYTES desde $SOURCE_BASE_LV_PATH a $TARGET_BASE_LV_PATH"
ddrescue --quiet --force --posición de entrada=$SYNC_OFFSET_BYTES --posición de salida=$SYNC_OFFSET_BYTES --tamaño=$SYNC_LENGTH_BYTES "$RUTA_LV_BASE_ORIGEN" "$RUTA_LV_BASE_DESTINO"
más
CMP_OFFSET=""
fi
done
eco Hecho...
desactivar_volumen "$TARGET_VG" "$TARGET_BASE_LV"
desactivar_volumen "$SOURCE_VG" "$SOURCE_BASE_LV"
}
lista de funciones()
{
LISTA_FUENTE_VG="$1"
LISTA_FUENTE_LV="$2"
LIST_TARGET_VG="$COPIA DE SEGURIDAD"
LISTA_OBJETIVO_LV="$LISTA_FUENTE_VG-$LISTA_FUENTE_LV"
LISTA_OBJETIVO_BASE_LV="$LISTA_OBJETIVO_LV$SUFIJO_SNAP"
lvs -olv_nombre | grep "$LISTA_OBJETIVO_BASE_LV.$FECHA_REGEX"
}
función remove()
{
ELIMINAR_OBJETIVO_VG="$COPIA DE SEGURIDAD"
ELIMINAR_OBJETIVO_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sincronizar
}
función removeall()
{
DESREF_FECHA="$3"
FILTRO="$(filtro "$DATE_OFFSET")"
mientras lee -r INSTANTÁNEA; hacer
eliminar "$SNAPSHOT"
hecho < <(lista "$1" "$2" | grep "$FILTER")
}
(
COMANDO="$1"
Turno
caso "$COMMAND" en
"--ayuda")
echo "Ayuda"
;;
"sufijo")
sufijo
;;
"filtrar")
filtro "$1"
;;
"respaldo")
esperar_bloquear_o_terminar
copia de seguridad "$1" "$2"
;;
"lista")
lista "$1" "$2"
;;
"thindiff")
thindiff "$1" "$2" "$3"
;;
"thinsync")
sincronización delgada "$1" "$2" "$3" "$4"
;;
"verificar")
esperar_bloquear_o_terminar
verificar "$1" "$2"
;;
"resincronizar")
esperar_bloquear_o_terminar
resincronizar "$1" "$2"
;;
"eliminar")
esperar_bloquear_o_terminar
eliminar "$1"
;;
"eliminartodo")
esperar_bloquear_o_terminar
eliminar todo "$1" "$2" "$3"
;;
*)
echo "Ninguno.."
;;
esac
) 98>$ARCHIVO DE BLOQUEO
EOF
Qué hace...?Contiene un conjunto de comandos para manipular instantáneas delgadas y sincronizar la diferencia entre dos instantáneas delgadas recibidas a través de Thin_delta con otro dispositivo de bloque usando ddrescue y blkdiscard.
Otro script que pondremos en cron:
Un poco más de fiesta#cat >/root/lvm-thin-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ARCHIVO_DE_SCRIPCIÓN="$(ruta_real $0)"
SCRIPT_DIR="$(nombre_directorio $SCRIPT_FILE)"
SCRIPT_NAME="$(nombre_base -s .sh $SCRIPT_FILE)"
SCRIPT_DE_RESPALDO="$DIR_SCRIPT/lvm-thin-backup.sh"
RETENCIÓN="-60 días"
$BACKUP_SCRIPT imágenes de respaldo linux-dev
$BACKUP_SCRIPT imágenes de respaldo win8
$BACKUP_SCRIPT imágenes de respaldo win8-data
#etc
$BACKUP_SCRIPT eliminar todas las imágenes linux-dev "$RETENTION"
$BACKUP_SCRIPT eliminar todas las imágenes win8 "$RETENTION"
$BACKUP_SCRIPT eliminar todas las imágenes win8-data "$RETENTION"
#etc
EOF
Qué hace...?Utiliza el script anterior para crear y sincronizar copias de seguridad de los volúmenes ligeros enumerados. El script dejará instantáneas inactivas de los volúmenes enumerados, que son necesarias para realizar un seguimiento de los cambios desde la última sincronización.
Este script debe editarse, especificando la lista de volúmenes ligeros para los cuales se deben realizar copias de seguridad. Los nombres dados son sólo para fines ilustrativos. Si lo desea, puede escribir un script que sincronice todos los volúmenes.
Demos los derechos:
#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh
Comprobémoslo y pongámoslo en el 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
El primer lanzamiento será largo, porque... Los volúmenes delgados se sincronizarán completamente copiando todo el espacio utilizado. Gracias a los metadatos delgados de LVM, sabemos qué bloques están realmente en uso, por lo que solo se copiarán los bloques de volumen delgados realmente utilizados.
Las ejecuciones posteriores copiarán los datos de forma incremental gracias al seguimiento de cambios a través de metadatos finos LVM.
Vamos a ver que pasó:
#time /root/btrfs-backup/cron-daily.sh
real 0m2,967s
user 0m0,225s
sys 0m0,353s
#tiempo /root/lvm-thin-backup/cron-daily.sh
1m2,710s reales
usuario 0m12,721s
sistema 0m6,671s
#ls -al /backup/btrfs/back/remote/*
/copia de seguridad/btrfs/volver/arranque/remoto:
0 totales
drwxr-xr-x 1 raíz raíz 1260 26 mar 09:11 .
drwxr-xr-x 1 raíz raíz 16 Mar 6 09:30 ..
drwxr-xr-x 1 raíz raíz 322 26 mar 02:00 .@base
drwxr-xr-x 1 raíz raíz 516 6 mar 09:39 .@snap.2020-03-06-09-39-37
drwxr-xr-x 1 raíz raíz 516 6 mar 09:39 .@snap.2020-03-06-09-39-57
...
/copia de seguridad/btrfs/back/remoto/root:
0 totales
drwxr-xr-x 1 raíz raíz 2820 26 mar 09:11 .
drwxr-xr-x 1 raíz raíz 16 Mar 6 09:30 ..
drwxr-xr-x 1 raíz raíz 240 26 mar 09:11 @.@base
drwxr-xr-x 1 raíz raíz 22 mar 26 09:11 @home.@base
drwxr-xr-x 1 raíz raíz 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-35
drwxr-xr-x 1 raíz raíz 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-57
...
drwxr-xr-x 1 raíz raíz 240 de marzo de 6 09:39 @.@snap.2020-03-06-09-39-26
drwxr-xr-x 1 raíz raíz 240 de marzo de 6 09:39 @.@snap.2020-03-06-09-39-56
...
#lvs -olv_name,lv_size imágenes y lvs -olv_name,lv_size copia de seguridad
LV LSIze
desarrollo de Linux 128,00 g
linux-dev.base 128,00g
piscina delgada 1,38t
Windows 8 128,00 g
win8-data 2,00t
win8-data.base 2,00t
win8.base 128,00g
LV LSIze
respaldo 256,00g
imágenes-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
...
imágenes-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
...
imágenes-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
...
piscina delgada <2,09 t
¿Qué tiene esto que ver con las muñecas nido?
Lo más probable es que, dado que los volúmenes lógicos LVM LV pueden ser volúmenes físicos LVM PV para otros VG. LVM puede ser recursivo, como muñecos nido. Esto le da a LVM una flexibilidad extrema.
PS
En el próximo artículo, intentaremos utilizar varios sistemas de almacenamiento móvil/KVM similares como base para crear un clúster de almacenamiento/vm geodistribuido con redundancia en varios continentes utilizando escritorios domésticos, Internet doméstico y redes P2P.
Fuente: habr.com
