Danielside

informática softwarelibre divagaciones música

Nextcloud con almacenamiento S3 y directorios compartidos LXC

Hacía un tiempo que quería tener un espacio realmente importante en mi Nextcloud para hacer backup de música y fotos. Pero el espacio no es barato. El servidor sale a unos 6 euros al mes, mientras que un volumen adicional (un disco) de 150Gib en DigitalOcean está a unos 16 euros al mes (iva incluido).

Descubrí que DigitalOcean también ofrece almacenamiento de objetos compatible con la API S3 de Amazon. Este almacenamiento está pensado en principio para servir grandes cantidades de estáticos a modo de CDN, pero al ser mucho más barato (5 dólares los 250Gib) me venía perfectamente para tener un gran espacio de backup, pagando la leve desventaja de un acceso pelín más lento (solo un poco). Al fin y al cabo implica un acceso a una red, mientras que la primera opción era un disco local al VPS.

Así que tenía dos problemas: integrar S3 limpiamente en un Nextcloud en funcionamiento y hacer que fuera visible desde el contenedor LXC con los permisos adecuados.

Montar S3 con FUSE y compartir con LXC

El acceso a los datos en un bucket S3 se realiza mediante una API REST sobre HTTP. En Debian puedes crear un sistema de ficheros basado en FUSE (Filesystem in User SpacE) sobre esa API, creando la ilusión de que es un volumen local al servidor. Primero hay que instalar el paquete s3fs.

La línea en /etc/fstab que yo uso es la siguiente:

s3fs#cubo /mnt/cubo fuse _netdev,allow_other,mp_umask=0022,passwd_file=/root/.passwd-s3fs,url=https://fra1.digitaloceanspaces.com 0 0

El fichero /root/.passwd-s3fs contiene una línea con la key, seguida de dos puntos (:), seguido del secret.

Se monta con mount /mnt/cubo y se desmonta con umount /mnt/cubo.

Una vez montado, voy a crear un directorio por cada contenedor, de manera que puedan tener sus ficheros separados. Por ahora lo usaré solo para nextcloud.

cd /mnt/cubo
mkdir nextcloud

Tal y como está, el propietario de nextcloud es root y dentro del contenedor no tendría permisos. Aquí es cuando viene el asunto de los namespaces. Aquí opera exactamente lo mismo que conté en esta entrada de Telegram, Buildah y Podman, en la sección de montar volúmenes. Es más, la gracia es que aunque estemos usando LXC, si tenemos Podman instalado en el sistema, también podemos usar podman unshare para conseguirlo, pero con una particularidad.

El mapeo de subuids a ids de LXC es un poco diferente al de Podman. En LXC sin privilegios están configurados en cada contenedor, por ejemplo para el de nextcloud (grep idmap ~/.local/share/lxc/nextcloud/config)

lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536

El id 0 (root) del contenedor enlaza al subuid 100000 del host, y así hasta 65536 subuids permitidos contando en secuencia. Para el 1 del contenedor, el 100001 del host, para el 2, el 100002, etc.

Sin embargo Podman tiene esta configuración (podman info|grep idMappings -A 14):

  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536

El id 0 del contenedor Podman (root) está enlazado al id 1000 del host (yo mismo) y desde el 1 en adelante hasta 65536 le corresponden a partir del 100000. El resultado es que para el 1 del contenedor, es el 100000 en el host, para el 2, el 100001, etc (notar como va levemente diferente que en LXC). Recordemos de la entrada anterior que al 500 (usuario de telegram) del contenedor, le correspondía el 100499 en el host.

Vamos a darle entonces los permisos adecuados al directorio nextcloud del punto de montaje del bucket. Quiero que el servidor nginx acceda en lectura y escritura, por tanto debo saber que ids tienen el usuario y el grupo nginx dentro del contenedor LXC:

lxc-attach nextcloud
id -u nginx # resultado: 100
id -g nginx # resultado: 101

Por tanto, desde el punto de vista del espacio de nombres del contenedor, esos ids deben ser los propietarios. Para conseguirlo tenemos dos opciones.

Para contenedores podman recordaremos que podman unshare chown 100:101 nextcloud conseguiría el resultado deseado. Pero como acabamos de ver, el mapeo de LXC es diferente. Así que hacemos:

cd /mnt/cubo
podman unshare chown 101:102 nextcloud

Es cuestión de hacer lo mismo que hacíamos con el contenedor podman, pero sumándole 1 al id que queremos conseguir.

Y la segunda opción es realmente mucho más sencilla, pero no se porqué nadie la cuenta (que yo haya encontrado) ¿No quedan los ficheros en el host con unos propietarios «raros» después del unshare? ¿Por qué no hacerlo con chmod simplemente? Aunque esos usuarios no existan en el host, la siguiente operación…

chown 100100:100101 nextcloud

…conseguirá exactamente el mismo resultado. Lo único que debemos «calcular» previamente es esos dos identificadores. Pero como vimos antes, en el mapeo LXC, al id 100 del contenedor le corresponde el 100100 del host y al id (de grupo) 101 le corresponde el 100101 en el host. Eso se puede saber consultando la configuración del mapeo en el contenedor.

El resultado es el siguiente, visto «desde fuera» (el host) y «desde dentro» (el contenedor LXC de nextcloud):

Para no mentir debo confesar que debería haber una tercera opción para hacer esto usando herramientas de LXC, pero yo la verdad es que no lo he conseguido. Creo que el comando no funciona exactamente como dice la documentación. Se trata de lxc-usernsexec.

Es un poco más rudimentario porque tengo que pasarle los mapeos al comando. En el manual dice que no es obligatorio, pero al menos a mi me da error. Hay que mapear el 0 del contenedor (root) a mi usuario (1000) y luego el resto de usuarios que te interesen, en este caso los de nginx. Al menos para consulta sí que funciona:

cd /mnt/cubo
lxc-usernsexec -m b:0:1000:1 -m b:100:100100:2 -- ls -aln

Lo que se hace con la b es indicar que queremos mapear tanto uids como gids y en este caso nos interesaban el 100 y el 101 de nginx, por eso la cuenta es 2. El resultado es este:

total 5
drwxr-xr-x 1 65534 65534    0 ene  1  1970 .
drwxr-xr-x 3 65534 65534 4096 ene  5 13:35 ..
drwxr-xr-x 1   100   101    0 ene  5 12:38 nextcloud

Como vemos, salen los ids correctos del contenedor. Pero según man lxc-usernsexec debería poder cambiar los permisos, pero el resultado es operación no permitida.

Compartir con LXC

Nextcloud en mi VPS se ejecuta dentro de un contenedor LXC sin privilegios. En la tecnología LXC, compartir directorios entre el contenedor y el host no está tan bien documentado como en Docker y Podman, pero funciona. Esta es la línea que va en la configuración del contenedor:

lxc.mount.entry = /mnt/cubo/nextcloud mnt/cubo none rw,bind,create=dir 0 0

Es un poco rara porque el punto de montaje no se debe poner en ruta absoluta (el segundo parámetro). Esto montará /mnt/cubo/nextcloud en /mnt/cubo del contenedor. Si no existe el punto de montaje, lo creará. Como hemos asignado permisos desde el host de manera que nginx tenga acceso a esos ficheros, no habrá mayor problema (la solución bestia que se suele ver en internet consiste en un chmod 777 al punto de montaje en el host).

Nextcloud y S3

Cuando la instancia de Nextcloud es nueva, es fácil hacer que sea el medio de almacenamiento por defecto. Cuando ya está en funcionamiento y no quieres romper cosas (créeme cuando te digo que NO te interesa simplemente cambiar la ruta de los ficheros en configuración) es un poco más delicado.

Se puede usar el plugin de almacenamiento externo, de forma que cualquier usuario puede seleccionar el volumen S3 para meter datos en una carpeta aparte de su cuenta, pero el problema con esta opción es que deja de poder compartir archivos con otros usuarios.

Otros administradores copian todos los ficheros de todas las cuentas al punto de montaje del bucket S3 (montado con s3fs) y hacen que el directorio de datos Nextcloud (misma ruta) sea un enlace simbólico hacia ese punto de montaje del bucket. Pero la desventaja de esta opción es que todos los usuarios se ven obligados a usar S3 (más lento, depende de la red).

Tiré por la vía de en medio. Tengo usuarios en disco local (acceso rápido, uso diario) y creo usuarios nuevos cuando se necesita que almacenen datos a modo de backup (música, fotos, etc). En lugar de hacer que todo el almacenamiento de Nextcloud sea un enlace simbólico, solo lo hago con algunos usuarios.

Supongamos que dentro del contenedor, la ruta que tenemos configurada para los datos de Nextcloud es /opt/nextcloud/data. Dentro de ese directorio Nextcloud crea uno por cada usuario.

Creo un usuario por la interfaz Nextcloud de la forma habitual (por ejemplo danielside-musica).

Ahora tengo que crear ese directorio en el host y darle los permisos adecuados para que el contenedor no tenga problemas de escritura, usamos la opción con un simple chown:

cd /mnt/cubo/nextcloud
mkdir danielside-musica
chown 100100:100101 buena-musica

Después hago lo siguiente:

lxc-attach nextcloud
cd /opt/nextcloud/data
rmdir danielside-musica
ln -s /mnt/cubo/buena-musica

Recordemos que /opt/nextcloud/data es un directorio «del contenedor» mientras que /mnt/cubo es el punto de montaje contra el punto de montaje de s3, valga la redundancia.

Las comillas anteriores de «del contenedor» vienen porque en realidad los contenedores no son máquinas virtuales, es una ficción. Son todo ficheros del host, pero cuando hacemos el cambio de namespaces con lxc-attach parece que estamos en una máquina virtual. Todos los ficheros de un contenedor pueden verse así desde el host:

ls -al ~/.local/share/lxc/nextcloud/rootfs

Conclusiones

Esto es un experimento que por ahora funciona bien. Es lento subir varios GiB de ficheros pero por ahora he subido 35 Gib de música sin problemas. El acceso desde la interfaz de Nextcloud es el esperado (se nota el retraso en las operaciones de leer y escribir, pero completamente usable). Compartir con otros usuarios funciona. También he probado a mover un usuario ya existente a s3 y todo sigue funcionando bien, no se han roto ni los compartidos. En este caso previo a cargarse el directorio del usuario en /opt/nextcloud/data lo que se hace es sincronizar con el nuevo destino con rsync. Además, sin instalo la aplicación Audio Player de Nextcloud, gano un reproductor online para todas mi música:

Como digo, funcionar funciona, pero el tiempo dirá si encuentro algún problema. Si algún avezado administrador de Nextcloud ve algún problema, por favor que hable 😉

ACTUALIZACIÓN Noviembre 2022: No va nada bien este sistema con el cliente móvil de Nextcloud. A veces puedes subir archivos, otras veces no. Es un comportamiento inestable. Usar solo este sistema para almacenamiento a largo plazo.


Archivado en categoría(s) Contenedores, GNU/Linux, Música

Enlace permanente



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.