Backup doméstico seguro con Linux, cifrado y ZFS (VII): Cambio de tarjeta microSD y redimensionamiento LUKS/LVM/FS

Este artículo documenta el proceso de actualización de mi sistema de backup doméstico, en producción desde 2014.

1   Antecedentes y motivación

Algunos antecedentes que tal vez deberías leer antes de meterte con este artículo:

Por supuesto es conveniente curiosear un poco la historia y la motivación de la tecnología:

El sistema operativo instalado en mi ProLiant es una distribución Ubuntu 14.04. Estoy contento con ella, pero tengo dos problemas:

  1. El soporte de seguridad de Ubuntu 14.04 termina en abril de 2019. Es un buen momento para actualizar el sistema, aprovechando que estoy de vacaciones en casa de mis padres. Al migrar ahora a Ubuntu 18.04 puedo despreocuparme hasta 2023.

  2. El soporte de ZFS en Linux sigue estando un poco verde y es conveniente instalar las actualizaciones (frecuentes) publicadas en ZFS on Linux. La versión de ZFS que tengo instalada en este servidor es la 0.6.5.11, publicada el 10 julio de 2017. La rama 0.6.5 de ZFS on Linux está cerrada y no tendrá más actualizaciones.

    La primera versión de la rama 0.7 se publicó el 27 de julio de 2017. La versión actual es la 0.7.12, publicada el 13 de noviembre de 2018. La rama 0.7 tiene mejoras importantes y es compatible con el kernel de Ubuntu 14.04, pero el repositorio oficial del proyecto ya no publica actualizaciones para este sistema operativo. Para poder usar la rama 0.7 de ZFS on Linux tengo que tener una versión de Ubuntu más actualizada... a menos que esté pendiente de la publicación de nuevas actualizaciones de ZFS on Linux, que compile ZFS a mano y me preocupe de recompilarlo de nuevo cada vez que me entra una actualización del kernel.

2   No tengo sitio. Hay que copiar la tarjeta microSD y ampliar el tamaño disponible

Mi servidor ProLiant puede arrancar desde una tarjeta microSD y, de hecho, es el método que estoy empleando yo. Uso una vieja tarjeta microSD de clase 4 y 4 GB de capacidad. Pequeña y lenta, pero perfecta para el uso que le doy.

Lamentablemente 4 GB no son suficientes para realizar una actualización completa del sistema en la misma tarjeta microSD. Necesito una tarjeta más grande.

Aprovecho una tarjeta microSD de 16 GB y categoría A1. Además del espacio, esta tarjeta es bastante más rápida que la original y esto debería acelerar la actualización del sistema considerablemente.

Los pasos son los siguientes:

  1. Conecto la tarjeta original de 4 GB a mi ordenador. Leo su contenido con el comando:

    # ddrescue /dev/sdb /tmp/Proliant-SD-20181226-Ubuntu_14_04.dump
    

    Esto genera un fichero de 4 GB.

  2. Envío dicho fichero a mi nube personal, como copia de seguridad (copia de seguridad del sistema de copia de seguridad...). Esta operación se puede realizar de forma simultánea con las siguientes, no hace falta esperar a que se complete la transmisión.

    Disponer de esta copia en otro lugar me permite revertir o repetir todo el proceso si las cosas van mal. Nunca está de más tener un plan B.

  3. Desenchufamos la tarjeta original de 4 GB y conectamos al portátil la tarjeta nueva de 16 GB. Copiamos en ella la tarjeta original:

    # ddrescue /tmp/Proliant-SD-20181226-Ubuntu_14_04.dump /dev/sdb --force
    

    Hay que indicar el parámetro --force porque la tarjeta microSD ya existe y el comando ddrescue intenta evitar que metamos la pata. Por supuesto, es crítico asegurarse de que vamos a sobreerscribir el dispositivo de almacenamiento correcto. Si nos equivocamos y sobreescribimos el disco duro, nos esperarán unas vacaciones muy ocupadas y agobiantes.

    Nota

    El comando ddrescue es conveniente, pero, si no disponemos de él, podemos usar el venerable comando dd.

  4. Desenchufamos la tarjeta nueva y la instalamos en el servidor ProLiant. Lo arrancamos.

Ahora tendremos un sistema de ficheros de 4 GB en una tarjeta de 16 GB. Habrá que reparticionar la tarjeta para aprovechar el espacio extra, teniendo cuidado de no borrar sus datos.

  1. El formato de la tarjeta nueva es el siguiente:

    # fdisk /dev/sdc
    
    Command (m for help): p
    
    Disk /dev/sdc: 15.9 GB, 15931539456 bytes
    64 heads, 32 sectors/track, 15193 cylinders, total 31116288 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x000c0c07
    
       Device Boot      Start         End      Blocks   Id  System
    /dev/sdc1   *        2048      499711      248832   83  Linux
    /dev/sdc2          501758     7714815     3606529    5  Extended
    /dev/sdc5          501760     7714815     3606528   83  Linux
    
    Command (m for help):
    

    Vemos perfectamente que el particionado ocupa 4 GB, pero la tarjeta tiene una capacidad de 16 GB.

  2. La clave para reparticionar el disco sin perder datos es que las particiones nuevas empiecen exactamente en el mismo sector que las originales.

    Por tanto, tomamos nota de esos valores.

  3. La partición que nos interesa es la 5, pero para ampliarla será necesario ampliar su contenedor, que es la partición 2. Primero las destruimos:

    Command (m for help): d
    Partition number (1-5): 5
    
    Command (m for help): d
    Partition number (1-5): 2
    
    Command (m for help): p
    
    Disk /dev/sdc: 15.9 GB, 15931539456 bytes
    64 heads, 32 sectors/track, 15193 cylinders, total 31116288 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x000c0c07
    
       Device Boot      Start         End      Blocks   Id  System
    /dev/sdc1   *        2048      499711      248832   83  Linux
    
    Command (m for help):
    

    De momento la cosa tiene buena pinta.

  4. A continuación creamos las particiones otra vez. Es muy importante que el sector de inicio sea exactamente el mismo que el original.

    Veamos la creación de la partición contenedora:

    Command (m for help): n
    Partition type:
       p   primary (1 primary, 0 extended, 3 free)
       e   extended
    Select (default p): e
    Partition number (1-4, default 2): 2
    First sector (499712-31116287, default 499712): 501758
    Last sector, +sectors or +size{K,M,G} (501758-31116287, default 31116287):
    Using default value 31116287
    
    Command (m for help):
    

    Obsérvese que usamos el tipo extended para indicar un contenedor de particiones y que no aceptamos la sugerencia del sector de inicio que nos proporciona fdisk. Usamos exactamente el sector de inicio original. Obviamente la partición ocupa todo el espacio disponible en la microSD.

  5. Intentemos crear ahora la partición lógica:

    Command (m for help): n
    Partition type:
       p   primary (1 primary, 1 extended, 2 free)
       l   logical (numbered from 5)
    Select (default p): l
    Adding logical partition 5
    First sector (503806-31116287, default 503806): 501760
    Value out of range.
    First sector (503806-31116287, default 503806): ^C
    

    Aquí tenemos un problema, ya que el sector de inicio sugerido no nos sirve, pero no nos acepta el sector de inicio que necesitamos. ¡Pánico!.

    Ante esta situación, salimos del programa a lo burro. fdisk ha acumulado los cambios solicitados en memoria, pero no actualiza la tabla de particiones de la microSD hasta que se lo digamos, así que en realidad no hemos hecho ningún cambio.

  6. Probamos ahora con el comando parted, más capaz que el vetusto fdisk:

    # parted /dev/sdc
    GNU Parted 2.3
    Using /dev/sdc
    Welcome to GNU Parted! Type 'help' to view a list of commands.
    (parted) unit s
    (parted) p
    Model: HP iLO Internal SD-CARD (scsi)
    Disk /dev/sdc: 31116288s
    Sector size (logical/physical): 512B/512B
    Partition Table: msdos
    
    Number  Start    End       Size      Type      File system  Flags
     1      2048s    499711s   497664s   primary   ext2         boot
     2      501758s  7714815s  7213058s  extended
     5      501760s  7714815s  7213056s  logical
    
    (parted) resizepart 2 31116287
    (parted) resizepart 5 31116287
    (parted) p
    Model: HP iLO Internal SD-CARD (scsi)
    Disk /dev/sdc: 31116288s
    Sector size (logical/physical): 512B/512B
    Partition Table: msdos
    
    Number  Start    End        Size       Type      File system  Flags
    1      2048s    499711s    497664s    primary   ext2         boot
    2      501758s  31116287s  30614530s  extended
    5      501760s  31116287s  30614528s  logical
    
    (parted) quit
    
  7. Ahora el particionado es correcto y aprovecha todo el espacio de la tarjeta microSD de 16 GB. Debemos notificar este hecho a LUKS [1]:

    # cryptsetup status /dev/mapper/sda5_crypt
    /dev/mapper/sda5_crypt is active and is in use.
    type:    LUKS1
    cipher:  aes-xts-plain64
    keysize: 512 bits
    device:  /dev/sdc5
    offset:  4096 sectors
    size:    7208960 sectors
    mode:    read/write
    # cryptsetup resize /dev/mapper/sda5_crypt
    # cryptsetup status /dev/mapper/sda5_crypt
    /dev/mapper/sda5_crypt is active and is in use.
    type:    LUKS1
    cipher:  aes-xts-plain64
    keysize: 512 bits
    device:  /dev/sdc5
    offset:  4096 sectors
    size:    30610432 sectors
    mode:    read/write
    

    Podemos apreciar perfectamente el efecto sobre LUKS.

  8. Ya con el tamaño LUKS actualizado, procedemos a notificar el cambio al sistema LVM [1]:

    # pvdisplay
      --- Physical volume ---
      PV Name               /dev/mapper/sda5_crypt
      VG Name               csi
      PV Size               3.44 GiB / not usable 3.00 MiB
      Allocatable           yes (but full)
      PE Size               4.00 MiB
      Total PE              879
      Free PE               0
      Allocated PE          879
      PV UUID               EOEHC1-giJC-wc8u-0ZdX-gTOD-OpaN-98MSgP
    
    # pvresize /dev/mapper/sda5_crypt
      Physical volume "/dev/mapper/sda5_crypt" changed
      1 physical volume(s) resized / 0 physical volume(s) not resized
    # pvdisplay
      --- Physical volume ---
      PV Name               /dev/mapper/sda5_crypt
      VG Name               csi
      PV Size               14.60 GiB / not usable 1.50 MiB
      Allocatable           yes
      PE Size               4.00 MiB
      Total PE              3736
      Free PE               2857
      Allocated PE          879
      PV UUID               EOEHC1-giJC-wc8u-0ZdX-gTOD-OpaN-98MSgP
    

    Podemos apreciar perfectamente el efecto sobre LVM.

  9. A continuación ampliaríamos el tamaño del volumen lógico con lvresize y luego el tamaño del sistema de ficheros con resize2fs. En esta ocasión voy a aprovechar el consejo que me dio @frangdlt, tal y como documento en Backup doméstico seguro con Linux, cifrado y ZFS (IV): Redimensionamiento LVM e identificación de discos duros:

    @jcea puedes usar lvresize -r; funciona bien siempre para crecer y decrecer e incluso desmonta fs. Así jamás te equivocas de orden :)

    Hacemos lo siguiente:

    # df -k /
    Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/dm-1        3008336 2407604    448572  85% /
    # lvresize -r -l+100%FREE /dev/mapper/csi-root
      Extending logical volume root to 14.11 GiB
      Logical volume root successfully resized
    resize2fs 1.42.9 (4-Feb-2014)
    Filesystem at /dev/mapper/csi-root is mounted on /; on-line resizing required
    old_desc_blocks = 1, new_desc_blocks = 1
    The filesystem on /dev/mapper/csi-root is now 3697664 blocks long.
    
    # df -k /
    Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/dm-1       14535424 2409064  11508420  18% /
    # pvdisplay
      --- Physical volume ---
      PV Name               /dev/mapper/sda5_crypt
      VG Name               csi
      PV Size               14.60 GiB / not usable 1.50 MiB
      Allocatable           yes (but full)
      PE Size               4.00 MiB
      Total PE              3736
      Free PE               0
      Allocated PE          3736
      PV UUID               EOEHC1-giJC-wc8u-0ZdX-gTOD-OpaN-98MSgP
    

    Podemos apreciar perfectamente el efecto sobre el volumen lógico de LVM y sobre el sistema de ficheros creado sobre él [1].

    Estamos dando todo el espacio libre al volumen lógico root. Normalmente no será lo que deseemos hacer, ya que la ventaja de LVM es poder ir asignando espacio a los diferentes volúmenes lógicos a medida que lo necesiten en vez de darles todo el espacio de golpe. En este caso esto es lo más sencillo porque nuestro paso final será volver a utilizar la tarjeta microSD original.

[1] (1, 2, 3)

Recuerda que mi configuración de almacenamiento es la siguiente:

  1. Particionado físico de la tarjeta de memoria microSD.
  2. Una partición de datos, pequeña, contiene directamente el sistema de arranque, el típico directorio /boot/.
  3. La otra partición de datos está cifrada directamente con LUKS.
  4. Sobre esta partición cifrada instalo LVM.
  5. Sobre ese grupo LVM, creo diferentes volúmenes lógicos. Uno de ellos será el volumen lógico root. Otro volumen lógico puede ser el swap del sistema, aunque no es muy buena idea hacer swap sobre una microSD, y menos aún si es lenta (clase 4, por ejemplo).

Tras todos estos pasos tenemos los 4 GB de contenido cifrado de la tarjeta microSD original en una tarjeta de 16 GB, aprovechando todo el espacio extra.