Cómo cambiar el tamaño del espacio de intercambio (SWAP) en SmartOS

Nota

En este documento voy a usar los términos SWAP y espacio de intercambio de forma indistinta.

Cuando se realiza una instalación nueva, SmartOS crea un dataset de SWAP del mismo tamaño que la memoria RAM del ordenador. Esto puede ser apropiado para máquinas grandes, pero cuando vamos escasos de memoria un SWAP tan pequeño es problemático porque bajo Illumos (Solaris) no existe el concepto de overcommit de memoria como en Linux. Es decir, un kernel Illumos limita el espacio de direccionamiento disponible para los programas a la suma de la memoria RAM y el espacio de intercambio (SWAP).

Por tanto, en SmartOS es común ampliar el espacio de intercambio.

Una de mis máquinas tiene solo 8GB de RAM y veo lo siguiente:

[root@X ~]# swap -s
total: 3928724k bytes allocated + 221992k reserved = 4150716k used, 8223728k available

[root@X ~]# zfs get all zones/swap
NAME        PROPERTY              VALUE                    SOURCE
zones/swap  type                  volume                   -
zones/swap  creation              vie. dic.  2  8:38 2022  -
zones/swap  used                  8,22G                    -
zones/swap  available             614G                     -
zones/swap  referenced            3,71G                    -
zones/swap  compressratio         2.18x                    -
zones/swap  reservation           none                     default
zones/swap  volsize               7,97G                    local
zones/swap  volblocksize          8K                       default
zones/swap  checksum              on                       default
zones/swap  compression           lz4                      inherited from zones
zones/swap  readonly              off                      default
zones/swap  createtxg             92                       -
zones/swap  copies                1                        default
zones/swap  refreservation        8,22G                    local
zones/swap  guid                  1242262060458694733      -
zones/swap  primarycache          all                      default
zones/swap  secondarycache        all                      default
zones/swap  usedbysnapshots       0                        -
zones/swap  usedbydataset         3,71G                    -
zones/swap  usedbychildren        0                        -
zones/swap  usedbyrefreservation  4,52G                    -
zones/swap  logbias               latency                  default
zones/swap  dedup                 off                      default
zones/swap  mlslabel              none                     default
zones/swap  sync                  standard                 default
zones/swap  refcompressratio      2.18x                    -
zones/swap  written               3,71G                    -
zones/swap  logicalused           8,01G                    -
zones/swap  logicalreferenced     8,01G                    -
zones/swap  snapshot_limit        none                     default
zones/swap  snapshot_count        none                     default
zones/swap  redundant_metadata    all                      default
zones/swap  encryption            off                      default
zones/swap  keylocation           none                     default
zones/swap  keyformat             none                     default
zones/swap  pbkdf2iters           0                        default

Por necesidades de servicio, requiero ampliar bastante el espacio de intercambio que, como he dicho, limita el espacio de direccionamiento total disponible para todos los programas.

En la configuración de zones/swap creada por SmartOS veo que toda la configuración tiene sus valores por defecto [1], con la excepción evidente de volsize y su compañera refreservation.

[1] En arquitecturas SPARC el tamaño de página es de 8KBytes, pero en x86 el tamaño es de 4KBytes. Que en SmartOS el atributo volblocksize tenga el valor de 8KB en x86 es algo en lo que profundizar.

Si tuviésemos espacio de sobra en RAM, podríamos desactivar el SWAP, cambiar su tamaño y volver a activarlo. Pero esto tiene dos problemas:

  1. Sin parar zonas Solaris, es posible que no nos quepa todo el RAM.

  2. SmartOS requiere un dataset de SWAP para poder arrancar. Si no lo encuentra, el arranque fallará. Podemos ver los detalles en el fichero /lib/svc/method/fs-joyent. Ahí se puede ver este código:

    swap -a /dev/zvol/dsk/${SYS_ZPOOL}/swap || \
        fatal "failed to configure swap device"
    

    Por tanto, destruir zones/swap y volver a crearlo, abre una ventana de vulnerabilidad de unos pocos segundos, pero que preferiría evitar. La ley de Murphy, ya se sabe.

Así que los pasos que vamos a seguir son los siguientes:

  1. Creamos un dataset para un segundo espacio de intercambio y lo añadimos al sistema. Tendrá el tamaño del espacio de intercambio original:

    [root@X ~]# zfs create -V 8G zones/swap2
    [root@X ~]# swap -a /dev/zvol/dsk/zones/swap2
    
  2. Ahora desactivamos el espacio de intercambio original. Esto provocará que se mueva el contenido de ese SWAP al nuevo. Esto puede ser costoso para la máquina, dependiendo de cuánto espacio esté en uso. Puede ser conveniente parar algunos servicios de la máquina para aliviar el proceso. En mi caso me interesa el espacio de intercambio para tener una capacidad de direccionamiento más alta, más que como alivio de la RAM.

    [root@X ~]# time swap -d /dev/zvol/dsk/zones/swap
    
    real    30m57.348s
    user    0m0.001s
    sys     0m10.711s
    

    Durante el proceso de borrado, podemos ver como un SWAP se está marcado como "en proceso de borrado" y va liberando espacio, mientras que el otro se va ocupando. El proceso es muy lento, no obstante. Paciencia [2]:

    [root@X ~]# swap -hl
    swapfile             dev    swaplo   blocks     free
    /dev/zvol/dsk/zones/swap 90,1        4K    7,97G    6,49G INDEL
    /dev/zvol/dsk/zones/swap2 90,3        4K    8,00G    7,89G
    
    [2]

    Aparentemente lo que hace el kernel Illumos es volver a traer a memoria las páginas almacenadas en el espacio de intercambio y dejar que el Sistema Operativo (mover al SWAP las páginas que hace más tiempo que no se usan, cuando vamos justos de RAM) haga su trabajo. Este proceso es ineficiente. Sería mucho más rápido simplemente copiar el contenido de un SWAP a otro, pero el código actual funciona también cuando se elimina el SWAP por completo. Digamos que el código es más general.

  3. Ahora que ya no estamos usando el viejo espacio de intercambio, podemos cambiar su tamaño. Esto puede hacerse sin destruir el dataset primero, y el cambio es transaccional. Es decir, aunque SmartOS falle en este momento, el sistema arrancará con normalidad, ya sea con el tamaño antiguo o el nuevo:

    [root@X ~]# zfs set volsize=64G zones/swap
    

    Este cambio de tamaño ajusta automáticamente refreservation.

    [root@X ~]# zfs get all zones/swap
    NAME        PROPERTY              VALUE                    SOURCE
    zones/swap  type                  volume                   -
    zones/swap  creation              vie. dic.  2  8:38 2022  -
    zones/swap  used                  66,0G                    -
    zones/swap  available             605G                     -
    zones/swap  referenced            3,71G                    -
    zones/swap  compressratio         2.18x                    -
    zones/swap  reservation           none                     default
    zones/swap  volsize               64G                      local
    zones/swap  volblocksize          8K                       default
    zones/swap  checksum              on                       default
    zones/swap  compression           lz4                      inherited from zones
    zones/swap  readonly              off                      default
    zones/swap  createtxg             92                       -
    zones/swap  copies                1                        default
    zones/swap  refreservation        66,0G                    local
    zones/swap  guid                  1242262060458694733      -
    zones/swap  primarycache          all                      default
    zones/swap  secondarycache        all                      default
    zones/swap  usedbysnapshots       0                        -
    zones/swap  usedbydataset         3,71G                    -
    zones/swap  usedbychildren        0                        -
    zones/swap  usedbyrefreservation  62,3G                    -
    zones/swap  logbias               latency                  default
    zones/swap  dedup                 off                      default
    zones/swap  mlslabel              none                     default
    zones/swap  sync                  standard                 default
    zones/swap  refcompressratio      2.18x                    -
    zones/swap  written               3,71G                    -
    zones/swap  logicalused           8,01G                    -
    zones/swap  logicalreferenced     8,01G                    -
    zones/swap  snapshot_limit        none                     default
    zones/swap  snapshot_count        none                     default
    zones/swap  redundant_metadata    all                      default
    zones/swap  encryption            off                      default
    zones/swap  keylocation           none                     default
    zones/swap  keyformat             none                     default
    zones/swap  pbkdf2iters           0                        default
    
  4. Ahora añadimos el SWAP primitivo, con el nuevo tamaño, y eliminamos el SWAP temporal que creamos al principio. Con suerte este proceso será rápido si teníamos RAM suficiente y aún no hemos mandado muchas páginas al SWAP:

    [root@X ~]# swap -a /dev/zvol/dsk/zones/swap
    [root@X ~]# time swap -d /dev/zvol/dsk/zones/swap2
    
    real    0m1.847s
    user    0m0.000s
    sys     0m0.381s
    
    [root@X ~]# swap -hl
    swapfile             dev    swaplo   blocks     free
    /dev/zvol/dsk/zones/swap 90,1        4K   64,00G   64,00G
    
    [root@X ~]# zfs destroy zones/swap2
    
  5. Vemos que todo esté como debe:

    [root@X ~]# swap -s
    total: 3935688k bytes allocated + 224224k reserved = 4159912k used, 67279576k available
    
    [root@X ~]# swap -lh
    swapfile             dev    swaplo   blocks     free
    /dev/zvol/dsk/zones/swap 90,1        4K   64,00G   63,96G
    

Uso de múltiples espacios de intercambio

En vez de cambiar el tamaño del espacio de intercambio, se podrían utilizar varios volúmenes de SWAP. Eso funcionaría con un kernel Illumos normal, a través del fichero /etc/vfstab, pero en SmartOS eso no funciona así. En SmartOS el fichero /etc/vfstab se crea de forma dinámica, no es persistente entre reinicios.

En SmartOS habría que añadir los espacios de intercambio adicionales a través de un servicio lanzado desde /opt/custom/smf/.