Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 30 de abril de 2013
El objetivo de este artículo es describir uno de mis sistemas de backup (tengo varios). Físicamente reside a varios cientos de kilómetros de mi residencia habitual, así que debe ser algo que no necesite una atención constante, ni una presencia física. En este artículo vamos a ver cómo montar un sistema de backup doméstico con las siguientes características:
En mi caso, la instalación la realizo bajo VirtualBox, en un Windows 8. La máquina tiene un uso ligero, 8 Gigabytes de RAM y un disco duro de un 1 TByte.
Instalo Ubuntu 12.04, por aquello de que tiene soporte hasta 2017. Le doy 50GB de disco duro, todos los cores de la CPU (la máquina tiene un uso ligero) y la mitad de la RAM, 4 Gigabytes.
Una vez instalado, copio allí mi clave pública SSH para poder entrar de forma remota. También instalo OpenVPN como cliente, conectándose a un servidor remoto bajo mi control. Allí le doy una IP privada fija. Esta conexión virtual me permite conectarme al servidor de forma remota aunque la máquina destino esté detrás de un cortafuegos, NAT, etc. Incluso podría hacer cosas como compartir disco por NFS de forma segura.
Procedo a añadir el repositorio del "port" de ZFS a Linux, y lo instalo.
El último paso consiste en hacer que la máquina virtual arranque automáticamente al encender el ordenador. Esto en MS Windows no es trivial, y aún lo es menor porque la máquina virtual debe arrancarse como administrador (se necesitará más adelante). Para ello utilizo VBoxVMService.
Compro un disco duro de 2 TeraBytes y lo instalo en la máquina. Se trata de un modelo "green" de WD, lo que me viene muy bien porque se desactiva y consume muy poco cuando no se está utilizando (el backup diario me lleva cosa de una hora, el resto del tiempo el disco está "idle"). Es un modelo algo lento, pero para este contexto no necesito más, y el consumo eléctrico es más prioritario.
Una vez instalado dentro del ordenador, lo arrancamos y vemos que el firmware lo reconoce. Desde Windows 8 también lo vemos sin problemas. No lo formateamos, eso es trabajo del Linux.
Debemos delegar este disco duro COMPLETO al Linux virtualizado. Si tuviésemos un sistema de ficheros Microsoft NTFS en ese disco podríamos modificar los permisos para poder acceder al disco como el usuario sin privilegios de la máquina, pero quiero una delegación completa y por eso necesito arrancar la máquina virtual como administrador.
Desde Windows 8, abrimos un terminal en modo administrador y tecleamos este comando, dentro del directorio de instalación de VirtualBox:
VBoxManage internalcommands createrawvmdk -filename PATH\DiscoUno.vmdk -rawdisk \\.\PhysicalDrive1
Este comando, si tiene éxito, creará un disco duro virtual en VirtualBox. Entramos en el entorno gráfico y asignamos dicho disco duro virtual (que proporciona acceso "raw" al disco duro físico) a nuestra máquina virtual.
Reiniciamos el ordenador, conectamos al Linux virtualizado y vemos, con un "fdisk", que tenemos acceso al disco duro. Perfecto.
Ahora que vemos el disco duro nuevo desde dentro de la máquina virtual, procedemos a formatearlo. Como no es un disco de arranque y no tengo necesidad de ser compatible con sistemas muy antiguos, procedo a realizar un formateado con GPT. Voy a usar todo el disco para ZFS y puedo crear y destruir datasets de forma dinámica, con tamaños también dinámicos, así que creo una única partición usando todo el disco duro:
# parted -a optimal /dev/sdb GNU Parted 2.3 Using /dev/sdb Welcome to GNU Parted! Type 'help' to view a list of commands. (parted) print Error: /dev/sdb: unrecognised disk label (parted) mklabel gpt (parted) mkpart primary 0% 100% (parted) print Model: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 2000GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 2000GB 2000GB primary (parted) quit Information: You may need to update /etc/fstab.
Con estos pasos formateamos el disco duro con tabla GPT y una única partición que ocupa todo el disco duro menos el primer megabyte. Ese primer megabyte es espacio reservado para, por ejemplo, un sistema de arranque sofisticado. Ese colchón incluso es útil para hacer mirror ZFS de discos duros de igual capacidad nominal pero cuyo número de sectores no es exactamente idéntico. La partición en sí está alineada, de forma que un disco de 4096 bytes por sector no sufra penalización de rendimiento a la hora de escribir.
El disco está virtualizado, y VirtualBox nos dice que tiene sectores de 512 bytes, cuando nosotros sabemos perfectamente (viendo la hoja de características del fabricante) que sus sectores tienen 4096 bytes. Esto será importante más abajo, cuando creemos el ZPOOL ZFS. De momento vamos verificando que todas las estructuras creadas están alineadas correctamente. Es el caso de esta partición.
Sobre esa partición creamos una estructura LUKS, que es la que nos va a proporcionar cifrado:
# mkfs /dev/ram0 mke2fs 1.42 (29-Nov-2011) Discarding device blocks: done Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) Stride=0 blocks, Stripe width=0 blocks 16384 inodes, 65536 blocks 3276 blocks (5.00%) reserved for the super user First data block=1 Maximum filesystem blocks=67108864 8 block groups 8192 blocks per group, 8192 fragments per group 2048 inodes per group Superblock backups stored on blocks: 8193, 24577, 40961, 57345 Allocating group tables: done Writing inode tables: done Writing superblocks and filesystem accounting information: done # mount /dev/ram0 /mnt (Aquí copiamos un fichero "llave", de un par de kbytes de longitud y generado de forma aleatoria, con buena entropía. Lo copio de mi portátil al servidor linux virtualizado, y lo copio dentro de "/mnt", que es una RAMDISK, porque no quiero que este fichero llave acabe en el disco duro) # cryptsetup luksFormat /dev/sdb1 --key-file=/mnt/llave WARNING! ======== This will overwrite data on /dev/sdb1 irrevocably. Are you sure? (Type uppercase yes): YES # cryptsetup luksAddKey /dev/sdc1 --key-file=/mnt/llave Enter new passphrase for key slot: Verify passphrase: # cryptsetup luksDump /dev/sdb1 LUKS header information for /dev/sdb1 Version: 1 Cipher name: aes Cipher mode: cbc-essiv:sha256 Hash spec: sha1 Payload offset: 4096 [...] # cryptsetup luksOpen --key-file /mnt/llave /dev/sdb1 primerdisco # zpool create -o ashift=12 datos /dev/mapper/primerdisco # zpool list NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT datos 1.81T 504K 1.81T 0% 1.00x ONLINE - # zfs set compression=gzip-9 datos # zfs set atime=off datos
Creamos una RAMDISK para contener un fichero clave, lo copiamos en ella y creamos un LUKS con dos claves. La primera clave es el fichero "llave", que es lo que usaremos normalmente. La segunda clave es una passphrase con buena entropía y buen tamaño, que guardamos cuidadosamente para situaciones de crisis. No es lo que usaremos normalmente.
Comprobamos que LUKS mantiene el alineamiento de 4096 bytes que necesitamos para un buen rendimiento de escritura.
Por último, activamos el LUKS del disco duro y creamos encima un ZPOOL ZFS. En ese ZPOOL activamos compresión máxima (es un backup, nos compensa y nos lo podemos permitir) y desactivamos "atime", que machaca mucho el disco y realmente no nos aporta nada. Obsérvese que le indicamos un "ashift" explícito de 12 bits, 4096 bytes. El comando debería detectar automáticamente la configuración del disco, pero muchos discos mienten y además tenemos VirtualBox en medio. Mejor ser explícitos y curarnos en salud. En el caso de que el disco duro realmente tuviese sectores de 512 bytes, no pasaría nada, simplemente perderíamos un poco de capacidad.
A continuación creamos un fichero "montar_datos" con un contenido similar a éste:
Este script se invoca desde otro script en OTRA máquina, la máquina desde la que solicitamos que se realice un backup. El script en esa otra máquina es:zpool export datos cryptsetup luksOpen --key-file /mnt/llave /dev/sdb1 primerdisco && \ zpool import datos && \ echo "OK!!" vars=`ssh-agent` eval "$vars" ssh-add /datos/backups/scripts/id_rsa dd if=/dev/urandom of=/mnt/llave bs=65536 count=2048
ssh root@IP "umount /mnt; mkfs /dev/ram0 && mount /dev/ram0 /mnt && chmod 700 /mnt && umount /mnt && mount /dev/ram0 /mnt" \ && \ scp llave root@IP:/mnt/ \ && \ ssh root@IP "./montar_datos ; ./z-arranque"
Este script se conecta a la máquina de backup, crea la ramdisk, le cambia los permisos para que solo pueda acceder a ella el administrador (es un poco complicado, con el fin de evitar "race conditions" en los que un atacante gana acceso al directorio ANTES de haberle cambiado los permisos), copia el fichero de claves y luego invoca el script en el backup que monta físicamente del ZFS cifrado y destruye dicha clave de cifrado. El fichero "./z-arranque" son cosas mías, como hacer un "ntpdate" o configurar las rutas de red.
Obsérvese que para entrar en las máquinas remotas para hacerles backup necesito una clave SSH, y que dicha clave se guarda en el propio disco de backup. Que, recordemos, en condiciones normales estará cifrado. Además, me pedirá que introduzca una "passphrase".
Hay un script adicional de desmontaje:
ssh-add -D && \ zpool export datos && \ cryptsetup luksClose /dev/mapper/segundodisco && \ echo "DONE!"
Este script destruye la clave SSH que nos permite entrar en las otras máquinas, desmonta el ZPOOL ZFS y desactiva la clave de acceso LUKS al disco duro. En este modo, nuestros backups están seguros, y es el modo "normal" de esta máquina.
En resumen, la máquina virtual no tiene acceso a los datos de backups, salvo mientras se está haciendo el propio backup.
Durante un mes, aproximadamente, estuve transfiriendo 1.2 terabytes de datos de backups e históricos de otro servidor de backup que va a ser eliminado. Este tiempo me permitió evaluar ZFS sobre Linux (yo vengo del mundo Solaris), su combinación con LUKS (cifrado), el impacto de usar sectores de 4096 bytes, etc.
Esta experiencia no estuvo exenta de problemas: incompatibilidad con el "zfs send" de Solaris 10 Update 11, cuelgues esporádicos debido a problemas al recibir un "zfs send" de Solaris en el Linux, etc. Al final lo arreglé sincronizando los snapshots entre ambas máquinas mediante un script muy interesante que será objeto de un artículo futuro.
Una vez que estuve satisfecho del funcionamiento, empecé a hacer los backups diarios de mis sistemas en esta máquina, en vez de en el servidor de backups antiguo, que va a ser eliminado en unos meses. Recordemos que este es uno de mis sistemas de backups, pero tengo varios más.
Después de un mes de buen funcionamiento, compré un segundo disco duro, igual al anterior. Mismo modelo. Es necesario que ambos discos duros tengan la misma capacidad exacta para poder hacer "mirror" entre ellos. Si la diferencia es de unos pocos sectores, se puede aprovechar el megabyte extra que dejamos al formatear con GPT para igualar el tamaño de la partición.
En general, meter dos discos iguales tiene ventajas e inconvenientes. La ventaja más evidente es que puedo hacer un "mirror" sin problemas, porque son trivialmente idénticos en capacidad. Otra ventaja es que cualquier asimetría en el rendimiento de los discos es una buena indicación de problemas. La desventaja fundamental es que comprando los dos discos a la vez y siendo el mismo modelo, es más probable que fallen con poca diferencia de tiempo. No queremos que los dos discos del "mirror" fallen con un mes de diferencia, ¿verdad?.
En este caso compré el segundo disco con un mes de diferencia, y no van a sufrir el mismo uso, así que preferí comprar el mismo modelo por sencillez y porque estoy muy contento con su muy bajo consumo eléctrico cuando no se utiliza.
Una vez instalado el segundo disco, procedemos a hacer las mismas operaciones: crear un dispositivo virtual en el host Windows 8, mapearlo a la máquina virtual, particionarlo, y montar LUKS encima.
Modificamos los scripts de montaje y desmontaje del backup para que abran y cierren ambos discos.
Pero en vez de crear un ZPOOL ZFS en el paso final, lo añadimos al ZPOOL ya existente, como "mirror". Hay que tener mucho cuidado de añadirlo como "mirror" (RAID 1) y no como "stripping" (RAID 0), porque si lo hacemos así tendremos un ZPOOL de 4 terabytes pero sin redundancia:
# zpool attach datos primerdisco segundodisco # zpool status pool: datos state: ONLINE status: One or more devices is currently being resilvered. The pool will continue to function, possibly in a degraded state. action: Wait for the resilver to complete. scan: resilver in progress since Thu Apr 11 20:21:27 2013 646G scanned out of 1.21T at 35.4M/s, 4h43m to go 646G resilvered, 52.30% done config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 primerdisco ONLINE 0 0 0 segundodisco ONLINE 0 0 0 (resilvering) errors: No known data errors
La sincronización de los discos empieza de forma automática.
La máquina opera con normalidad y podemos seguir usándola. Incluso podemos apagarla, y seguirá sincronizándose cuando la encendamos de nuevo.
Podemos ver la actividad detallada de los discos duros. La copia de un disco al otro es muy evidente:
# zpool iostat -v 10 9999 [...] capacity operations bandwidth pool alloc free read write read write ---------------- ----- ----- ----- ----- ----- ----- datos 1.21T 621G 364 27 43.7M 79.4K mirror 1.21T 621G 364 27 43.7M 79.4K primerdisco - - 361 15 43.7M 96.4K segundodisco - - 0 378 0 43.8M ---------------- ----- ----- ----- ----- ----- ----- [...]
Una vez que los discos están sincronizados, seguimos usando el sistema unas semanas para probar bastante bien el disco duro nuevo.
Antes de desconectar el disco duro, hacemos un "scrub" para asegurarnos de que ambos discos duros están en perfectas condiciones y los datos almacenados son consistentes. Si hay cualquier tipo de problemas, ZFS nos los dirá. De hecho lo solucionará si es posible, aprovechando la redundancia que aún tenemos.
Ahora desactivamos el disco duro que vamos a retirar:
# zpool offline datos segundodisco # zpool status pool: datos state: DEGRADED status: One or more devices has been taken offline by the administrator. Sufficient replicas exist for the pool to continue functioning in a degraded state. action: Online the device using 'zpool online' or replace the device with 'zpool replace'. scan: resilvered 1.21T in 24h51m with 0 errors on Fri Apr 12 21:12:41 2013 config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror-0 DEGRADED 0 0 0 primerdisco ONLINE 0 0 0 segundodisco OFFLINE 0 0 0 errors: No known data errors
Ahora hacemos un ciclo de backup normal, solo sobre un disco duro.
Cuando terminamos, reactivamos el disco duro para ver cómo se sincronizan. Esto es básicamente lo que haremos dentro de unos meses, sincronizar los cambios del disco duro más actualizado al disco duro que teníamos archivado offline.
# zpool online datos segundodisco # zpool status pool: datos state: ONLINE scan: resilvered 498M in 0h1m with 0 errors on Thu Apr 18 14:10:29 2013 config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 primerdisco ONLINE 0 0 0 segundodisco ONLINE 0 0 0 errors: No known data errors
Vemos que se sincroniza lo justo, y que lo hace rápido.
Volvemos a poner el disco duro "offline", lo desenchufamos y se lo entregamos a mi amigo para su custodia.
# zpool offline datos segundodisco # zpool status pool: datos state: DEGRADED status: One or more devices has been taken offline by the administrator. Sufficient replicas exist for the pool to continue functioning in a degraded state. action: Online the device using 'zpool online' or replace the device with 'zpool replace'. scan: resilvered 148K in 0h0m with 0 errors on Sat Apr 20 17:35:32 2013 config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror-0 DEGRADED 0 0 0 primerdisco ONLINE 0 0 0 10256769708510827902 OFFLINE 0 0 0 was /dev/mapper/segundodisco errors: No known data errors
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS