Reemplazar un disco de arranque ZFS en Solaris 10
En agosto de 2015 me llamó por teléfono un antiguo cliente para pedirme que le ayudase a reemplazar uno de los discos de arranque ZFS de un viejo Solaris 10 que aún tiene en producción. La tarea no es trivial porque a) hay poca documentación al respecto, b) la versión de Solaris 10 que usa está muy anticuada y tiene problemas con el arranque ZFS muy conocidos y arreglados en versiones posteriores y c) en ese momento yo me encontraba a 600 Km de Madrid y tendría que actuar con "manos remotas".
A la hora de trabajar con Solaris hay que entender algunos conceptos diferentes a otros Sistemas Operativos:
-
Aunque lo que se lleva ahora es un sistema de particionado de disco EFI, si tu instalación Solaris es antigua, tendrás los discos formateados con Slices [1]. La diferencia entre las particiones tradicionales y los Slices Solaris es que en Solaris los Slices residen DENTRO de una partición. Es semejante a utilizar una partición primaria de la BIOS de un PC para crear dentro particiones lógicas. La idea es similar.
En un disco Solaris tradicional es muy típico tener una única partición primaria en el disco que, interiormente, está subdividida en diez Slices de diferentes tamaños.
[1] En el mundo Solaris, este tipo de formato se llama SMI. Más información, por ejemplo, en SMI and EFI disk label on Solaris.
-
El soporte ZFS en el gestor de arranque GRUB en Solaris 10 es mínimo y más si se trata de una versión no actualizada del sistema operativo. Esto hace que, por ejemplo, GRUB no pueda encontrar los discos de arranque si se cambian de posición física, aunque técnicamente a ZFS le de igual porque guarda sus UUID dentro del propio disco y le da lo mismo que se hayan movido.
Por el mismo motivo (soporte mínimo en GRUB), no podemos usar ZFS en los discos de arranque a menos que el zpool [2] de arranque conste de un único VDEV [3] formado por un solo disco o por discos en espejo.
Por lo mismo también, si estamos usando discos de arranque ZFS en espejo, hay que instalar GRUB en todos y cada uno de ellos.
[2] En ZFS un zpool es la colección de discos que forman una unidad de almacenaje.
[3] En ZFS, un vdev es cada una de las unidades de disco virtuales que forman un zpool. Cada vdev o disco virtual puede estar formado por un solo disco, por varios discos duros en espejo o por configuraciones de redundancia RAID-Z, RAID-Z2 o RAID-Z3.
-
Diferentes subsistemas Solaris 10 identifican los discos duros de forma diferente e inconsistente. Hay que tener mucho cuidado para no meter la pata y reemplazar el disco duro que no es. Este problema me ha mordido el culo varias veces aunque nunca de forma catastrófica. Mide todo tres veces para cortar una y comprueba varias veces lo que parece evidente.
Por ejemplo, fíjate más abajo en que ZFS habla del disco c1d0s0, mientras que en format el disco que nos interesa es el c2d0.
Es muy confuso. Ten mucho cuidado.
-
Se pueden ver otras restricciones en ZFS Root Pool Configuration Requirements. Por ejemplo:
Disks that are designated for booting in a ZFS root pool must be less than 2 TBs in size on both SPARC based and x86 based systems.
En este caso los discos de arranque ZFS están en un zpool formado por dos discos en espejo.
Los pasos a seguir son los siguientes:
-
Primero vemos cuál es el estado de salud del zpool:
root@tesalia /# zpool status pool: datos state: DEGRADED status: One or more devices has experienced an unrecoverable error. An attempt was made to correct the error. Applications are unaffected. action: Determine if the device needs to be replaced, and clear the errors using 'zpool clear' or replace the device with 'zpool replace'. see: http://www.sun.com/msg/ZFS-8000-9P scrub: scrub completed after 17h1m with 0 errors on Thu Jun 18 02:22:07 2015 config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror DEGRADED 0 0 0 c1d0s0 DEGRADED 0 0 39 too many errors c5t1d0s0 ONLINE 1 0 0 errors: No known data errors
Está claro que un disco está dando problemas.
-
Lo más simple sería reemplazar el disco duro problemático por uno nuevo y volver a sincronizar el zpool ZFS. El problema es que durante esa reconstrucción no tendremos redundancia y cualquier error de disco que nos encontremos será catastrófico.
En configuraciones en espejo, mi consejo es enchufar un tercer disco duro, añadirlo al espejo y esperar a que ZFS complete la sincronización. De esta forma si la sincronización encuentra un error en uno de los discos originales, lo podrá solucionar leyendo del segundo disco original. Es poco probable que dos discos duros fallen exactamente en el mismo sector.
En este caso conectamos un nuevo disco duro por USB.
-
Identificamos los discos duros:
root@tesalia /# cfgadm -alv Ap_Id Receptacle Occupant Condition Information When Type Busy Phys_Id sata0/0::dsk/c1d0 connected configured ok Mod: ST3250823AS FRev: 3.03 SN: 3ND1LKMP unavailable disk n /devices/pci@0,0/pci108e,5348@7:0 sata0/1::dsk/c5t1d0 connected configured ok Mod: WDC WD7500AAKS-00RBA0 FRev: 30.04G30 SN: WD-WCAPT1056964 unavailable disk n /devices/pci@0,0/pci108e,5348@7:1 sata1/0 empty unconfigured ok unavailable sata-port n /devices/pci@0,0/pci108e,5348@8:0 sata1/1 empty unconfigured ok unavailable sata-port n /devices/pci@0,0/pci108e,5348@8:1 usb0/1 connected configured ok Mfg: Digital Stream Corp. Product: USB - PS/2 Active Adapter NConfigs: 1 Config: 0 <no cfg str descr> unavailable usb-kbd n /devices/pci@0,0/pci108e,5348@2:1 usb0/2 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:2 usb0/3 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:3 usb0/4 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:4 usb0/5 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:5 usb0/6 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:6 usb0/7 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:7 usb0/8 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2:8 usb1/1 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:1 usb1/2 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:2 usb1/3 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:3 usb1/4 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:4 usb1/5 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:5 usb1/6 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:6 usb1/7 connected configured ok Mfg: <undef> Product: USB Storage NConfigs: 1 Config: 0 <no cfg str descr> unavailable usb-storage n /devices/pci@0,0/pci108e,5348@2,1:7 usb1/8 empty unconfigured ok unavailable unknown n /devices/pci@0,0/pci108e,5348@2,1:8
Aquí podemos ver perfectamente los dos discos duros internos y el disco conectado por USB.
Una forma más compacta y clara de verlo es:
root@tesalia /# iostat -En c0t0d0 Soft Errors: 0 Hard Errors: 0 Transport Errors: 0 Vendor: MATSHITA Product: DVD-ROM SR-8178 Revision: PZ16 Serial No: Size: 0.00GB <0 bytes> Media Error: 0 Device Not Ready: 0 No Device: 0 Recoverable: 0 Illegal Request: 12 Predictive Failure Analysis: 0 c3t0d0 Soft Errors: 0 Hard Errors: 0 Transport Errors: 0 Vendor: WDC WD10 Product: 02F9YZ-09H1JL1 Revision: 0041 Serial No: Size: 1000.20GB <1000204886016 bytes> Media Error: 0 Device Not Ready: 0 No Device: 0 Recoverable: 0 Illegal Request: 0 Predictive Failure Analysis: 0 c1d0 Soft Errors: 0 Hard Errors: 0 Transport Errors: 0 Vendor: ATA Product: ST3250823AS Revision: 3.03 Serial No: Size: 250.06GB <250059349504 bytes> Media Error: 0 Device Not Ready: 0 No Device: 0 Recoverable: 0 Illegal Request: 69 Predictive Failure Analysis: 0 c5t1d0 Soft Errors: 0 Hard Errors: 18202 Transport Errors: 0 Vendor: ATA Product: WDC WD7500AAKS-0 Revision: 4G30 Serial No: Size: 750.16GB <750156373504 bytes> Media Error: 0 Device Not Ready: 0 No Device: 0 Recoverable: 0 Illegal Request: 70 Predictive Failure Analysis: 0
-
En Solaris gestionamos el particionado de disco con el comando fdisk, pero la gestión de slices se hace con el comando format. Lo que nos interesa ahora es ver cómo está organizado el disco duro que queremos reemplazar:
root@tesalia /# format -e Searching for disks...done AVAILABLE DISK SELECTIONS: 0. c2d0 <DEFAULT cyl 45597 alt 2 hd 255 sec 126> /pci@0,0/pci108e,5348@7/disk@1,0 1. c3t0d0 <DEFAULT cyl 60798 alt 2 hd 255 sec 126> /pci@0,0/pci108e,5348@2,1/storage@7/disk@0,0 2. c5t0d0 <DEFAULT cyl 60562 alt 2 hd 128 sec 63> /pci@0,0/pci108e,5348@7/disk@0,0 Specify disk (enter its number): Veamos la configuración del disco a reemplazar: Specify disk (enter its number): 2 selecting c5t0d0 [disk formatted] FORMAT MENU: disk - select a disk type - select (define) a disk type partition - select (define) a partition table current - describe the current disk format - format and analyze the disk fdisk - run the fdisk program repair - repair a defective sector label - write label to the disk analyze - surface analysis defect - defect list management backup - search for backup labels verify - read and display labels save - save new disk/partition definitions inquiry - show vendor, product and revision scsi - independent SCSI mode selects cache - enable, disable or query SCSI disk cache volname - set 8-character volume name !<cmd> - execute <cmd>, then return quit format> p PARTITION MENU: 0 - change `0' partition 1 - change `1' partition 2 - change `2' partition 3 - change `3' partition 4 - change `4' partition 5 - change `5' partition 6 - change `6' partition 7 - change `7' partition 9 - change `9' partition select - select a predefined table modify - modify a predefined partition table name - name the current table print - display the current table label - write partition map and label to the disk !<cmd> - execute <cmd>, then return quit partition> p Current partition table (original): Total disk cylinders available: 60562 + 2 (reserved cylinders) Part Tag Flag Cylinders Size Blocks 0 root wm 3 - 60561 232.86GB (60559/0/0) 488347776 1 unassigned wu 0 0 (0/0/0) 0 2 backup wm 0 - 60561 232.87GB (60562/0/0) 488371968 3 unassigned wu 0 0 (0/0/0) 0 4 unassigned wu 0 0 (0/0/0) 0 5 unassigned wu 0 0 (0/0/0) 0 6 unassigned wu 0 0 (0/0/0) 0 7 unassigned wu 0 0 (0/0/0) 0 8 boot wu 0 - 0 3.94MB (1/0/0) 8064 9 alternates wu 1 - 2 7.88MB (2/0/0) 16128 partition>
El zpool en sí reside en el slice 0, de 232GB. Nótese la presencia de los slices 8 y 9 en las primeras pistas del disco duro [4].
[4] Según la documentación oficial de Solaris:
7 Legacy Issues
7.1 The Solaris x86 Boot Partition
For x86 systems, the Solaris boot partition is a very small (about 10MB) primary fdisk partition in PCFS (FAT) format. It was introduced in the Solaris 2.6 release, primarily because BIOS firmware at that time could not access disk blocks beyond 1024 cylinders. It was necessary to put boot code in a small area near the beginning of the disk. The boot partition allowed the Solaris OS to be installed on a non-boot disk with minimal space requirements on the boot disk.
With modern systems, there is no longer a need for a boot partition. From the Solaris 10 1/06 OS onward, users no longer have the option to create a boot partition. When upgrading from an old release, an existing x86 boot partition may be retained to keep the system bootable without manual intervention. In such cases, the x86 boot partition is mounted at /stubboot and contains GRUB boot blocks and a menu file /stubboot/boot/grub/menu.lst. The user can eliminate the x86 boot partition by deleting the /stubboot entry from /etc/vfstab, fixing up /boot/grub/menu.lst, and reconfiguring BIOS to boot from the Solaris disk partition.
El formato de slices del segundo disco duro de arranque es:
Current partition table (original): Total disk cylinders available: 45597 + 2 (reserved cylinders) Part Tag Flag Cylinders Size Blocks 0 unassigned wm 3 - 45596 698.54GB (45594/0/0) 1464935220 1 unassigned wm 0 0 (0/0/0) 0 2 backup wu 0 - 45596 698.58GB (45597/0/0) 1465031610 3 unassigned wm 0 0 (0/0/0) 0 4 unassigned wm 0 0 (0/0/0) 0 5 unassigned wm 0 0 (0/0/0) 0 6 unassigned wm 0 0 (0/0/0) 0 7 unassigned wm 0 0 (0/0/0) 0 8 boot wu 0 - 0 15.69MB (1/0/0) 32130 9 alternates wm 1 - 2 31.38MB (2/0/0) 64260
Aquí tenemos reservados 698GB de disco para el zpool ZFS, pero como estamos trabajando con un espejo, el tamaño total disponible es el del disco duro más pequeño. Es decir, unos 232 GB. El disco duro que vamos a añadir es de un terabyte, así que si reemplazamos el disco duro de 250GB por el de 1TB, tendríamos disponibles 698GB (el disco duro más pequeño sería el de 750GB).
-
En nuestro caso el disco duro nuevo a reemplazar es el de 750GB, quedándonos con el prehistórico disco de 250GB (que sigue funcionando como un campeón). En condiciones normales dejaría el disco duro de 1TB particionado con los slices de forma que se pudiese aprovechar el terabyte de almacenamiento si en el futuro se reemplazase el disco duro de 250GB, pero el cliente afirma que esta máquina es legacy y que, aunque se actualice el hardware, nunca necesitarían más capacidad de almacenamiento. Si ese dejase de ser el caso, me volverían a contratar para realizar los cambios necesarios :-).
Por tanto dejo la tabla de particiones y de slices del disco duro nuevo (conectado por USB) de la siguiente manera.
Primero particionamos el disco con una única partición ocupando el disco duro entero:
root@tesalia /# fdisk /dev/rdsk/c3t0d0p0 No fdisk table exists. The default partition for the disk is: a 100% "SOLARIS System" partition Type "y" to accept the default partition, otherwise type "n" to edit the partition table. y
A continuación dividimos esa partición en slices semejantes a la configuración actual:
root@tesalia /# format -e ... Current partition table (original): Total disk cylinders available: 60797 + 2 (reserved cylinders) Part Tag Flag Cylinders Size Blocks 0 root wm 3 - 15203 232.89GB (15201/0/0) 488408130 1 unassigned wm 0 0 (0/0/0) 0 2 backup wu 0 - 60796 931.46GB (60797/0/0) 1953407610 3 unassigned wm 0 0 (0/0/0) 0 4 unassigned wm 0 0 (0/0/0) 0 5 unassigned wm 0 0 (0/0/0) 0 6 unassigned wm 0 0 (0/0/0) 0 7 unassigned wm 0 0 (0/0/0) 0 8 boot wu 0 - 0 15.69MB (1/0/0) 32130 9 alternates wu 1 - 2 31.38MB (2/0/0) 64260
-
Ahora instalamos GRUB en el disco duro nuevo (en el USB):
root@tesalia /# /sbin/installgrub /boot/grub/stage1 /boot/grub/stage2 /dev/rdsk/c3t0d0s0 stage1 written to partition 0 sector 0 (abs 32130) stage2 written to partition 0, 272 sectors starting at 50 (abs 32180)
-
A continuación tenemos dos opciones. La primera es añadir el nuevo disco duro al espejo de arranque ZFS y luego eliminar el disco duro problemático. En principio debería ser la opción recomendada, pero, en cambio, lo que hice fue decirle a ZFS que reemplazase un disco duro por otro:
root@tesalia /# zpool replace datos c1d0s0 c3t0d0s0 Please be sure to invoke installgrub(1M) to make 'c3t0d0s0' bootable. Make sure to wait until resilver is done before rebooting.
Es de agradecer que Solaris nos recuerde la necesidad de instalar GRUB en el nuevo disco duro, cosa que ya hemos hecho en el paso anterior.
-
Ahora es cuestión de sentarse y esperar a que ZFS termine la sincronización de discos duros:
root@tesalia /# zpool iostat -v 10 999999999 capacity operations bandwidth pool used avail read write read write -------------- ----- ----- ----- ----- ----- ----- datos 114G 118G 20 50 69.8K 208K mirror 114G 118G 20 50 69.8K 208K replacing - - 9 60 34.0K 238K c1d0s0 - - 8 9 463K 96.9K c3t0d0s0 - - 0 54 0 267K c5t1d0s0 - - 8 9 461K 96.9K -------------- ----- ----- ----- ----- ----- ----- root@tesalia /# zpool status pool: datos state: DEGRADED 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. scrub: resilver in progress for 0h2m, 0.02% done, 196h42m to go config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror DEGRADED 0 0 0 replacing DEGRADED 0 0 0 c1d0s0 DEGRADED 0 0 0 too many errors c3t0d0s0 ONLINE 0 0 0 26.4M resilvered c5t1d0s0 ONLINE 0 0 0 errors: No known data errors root@tesalia /# zpool status pool: datos state: DEGRADED 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. scrub: resilver in progress for 7h30m, 8.73% done, 78h35m to go config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror DEGRADED 0 0 0 replacing DEGRADED 0 0 0 c1d0s0 DEGRADED 0 0 0 too many errors c3t0d0s0 ONLINE 0 0 0 9.95G resilvered c5t1d0s0 ONLINE 0 0 0 errors: No known data errors
-
La sincronización acaba por completarse y vemos algo así:
root@tesalia /# zpool status pool: datos state: ONLINE scrub: resilver completed after 37h29m with 0 errors on Fri Aug 14 07:36:23 2015 config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror ONLINE 0 0 0 c3t0d0s0 ONLINE 0 0 0 113G resilvered c5t1d0s0 ONLINE 0 0 0 errors: No known data errors
-
Dado que la versión de Solaris 10 instalada en esa máquina es muy antigua y el soporte ZFS de su GRUB es absolutamente mínimo, no basta con quitar el disco duro problemático, desenchufar el USB y meterlo dentro del ordenador.
Y aquí es donde entra mi know how.
ZFS mantiene dentro de los discos duros información sobre dónde están conectados. Esa información [5] se actualiza al importar un zpool, pero GRUB no hacer una importación de verdad. Como ya he dicho varias veces, GRUB se limita a implementar lo justo de ZFS como para poder acceder al kernel y a la ramdisk. Es gracioso ver a GRUB arrancar desde un disco duro, ver que en él está el zpool ZFS de arranque, cargar el kernel y la ramdisk e... ignorar que está arrancando de ese disco y hacer caso a la información de posición presente dentro del zpool ZFS, anticuada si hemos apagado la máquina y movido los discos duros de sitio. El GRUB comunica esa información incorrecta el kernel y el sistema se reinicia con un panic.
Señalo como muy importante que esto no es un problema de ZFS. Al hacer una importación ZFS localiza los discos duros implicados estén donde estén y actualiza esa información. Da igual que los hayamos cambiado de sitio. El problema es GRUB, que hace caso a esa información sin más.
[5] Por si tienes curiosidad, en ZFS ese campo presente en cada disco duro de un zpool se llama devicepath. Es accesible, por ejemplo, con el comando zdb. Ejemplo:
root@tesalia zfs# zdb datos version=15 name='datos' state=0 txg=381092364 pool_guid=12797187271194429978 hostid=125396546 hostname='tesalia' vdev_tree type='root' id=0 guid=12797187271194429978 children[0] type='mirror' id=0 guid=5350612910402650640 whole_disk=0 metaslab_array=13 metaslab_shift=30 ashift=9 asize=250060210176 is_log=0 children[0] type='disk' id=0 guid=9809302441882960710 path='/dev/dsk/c3t0d0s0' devid='id1,sd@f0779664255cb28300007cc280000/a' phys_path='/pci@0,0/pci108e,5348@2,1/storage@7/disk@0,0:a' whole_disk=0 DTL=4205 children[1] type='disk' id=1 guid=4987106528736522244 path='/dev/dsk/c1d0s0' devid='id1,sd@AWDC_WD7500AAKS-00RBA0=_____WD-WCAPT1056964/a' phys_path='/pci@0,0/pci108e,5348@7/disk@0,0:a' whole_disk=0 DTL=421 children[2] type='disk' id=2 guid=2320059404737416760 path='/dev/dsk/c2d0s0' devid='id1,sd@AWDC_WD1002F9YZ-09H1JL1=_____WD-WMC5K0D3NK1S/a' phys_path='/pci@0,0/pci108e,5348@7/disk@1,0:a' whole_disk=0 DTL=4207
-
Debido al punto anterior, tenemos dos opciones. La primera es arrancar desde un CD o DVD Solaris 10 con una versión igual o superior a la instalada. Lamentablemente mi cliente no es previsor y no dispone de un CD/DVD apropiado. Craso error [6]. Yo dispongo de una imagen ISO de todas las versiones publicadas de Solaris 10, pero antes de enviársela (y perder otro día) decidimos probar arrancando el sistema desde GRUB con la opción de arranque a prueba de fallos. Esta opción carga el kernel y la ramdisk, pero no intenta utilizar el disco duro para nada más. No se intenta montar nada.
Una vez que reposicionamos los discos duros de la forma definitiva, arrancamos en el modo a prueba de fallos. Esto arranca un sistema mínimo que no intenta acceder al disco duro. Desde ese entorno importamos el zpool con:
# zpool import datos # zpool export datos
La importación actualizará la información devicepath de los discos duros.
[6] Afortunadamente la versión de ZFS de esta versión prehistórica de Solaris 10 está tan anticuada que el zpool podría importarse desde sistemas operativos abiertos derivados de Solaris, como Illumos o SmartOS:
-
Ahora reiniciamos el sistema de forma normal y... ¡FUNCIONA!.
-
Por último comprobamos que el zpool no contiene errores y todo está correcto:
# zpool scrub datos # zpool status pool: datos state: ONLINE scrub: scrub completed after 10h55m with 0 errors on Sat Aug 22 00:55:47 2015 config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror ONLINE 0 0 0 c1d0s0 ONLINE 0 0 0 c5t1d0s0 ONLINE 0 0 0 errors: No known data errors
Algunas páginas adicionales sobre este tema:
- Hilo "[solarisx86] Solaris 10 Update 8, ZFS boot/root zpool and moving disks around" iniciado el 17 de agosto de 2015 en la lista de correo "solarisx86", no archivado de forma pública.
- ZFS root mirroring, unable to attach mirror disk.
Nota
Otro problema que tuve durante todo el proceso es que el USB empezó a fallar mientras replicaba los discos. Problemas con el cable. Esto hizo todo el proceso penosamente lento y falló varias veces. Una vez que reemplazamos el cable, problema solucionado.
Os ahorraré los detalles y los juramentos que se oyeron esos días.