Aprovechando que he montado un nuevo volumen Raid1
con dos discos duros Hitachi HGST_HTS721010A9E630 en mi servidor personal en Madrid he decidido migrar algunos contenedores desde el viejo volumen en Western Digital, especialmente porque el throughput del nuevo volumen de discos Hitachi es muy superior al volumen con discos Western Digital.
En algunos casos he optado por hacer instalaciones limpias, en lugar de mover el contenedor de volumen; como por ejemplo el contenedor que corre el nodo de NexctCloud en Madrid. Así que he aprovechado la ocasión para documentar el proceso en forma de post.
Algunas Consideraciones previas:
- El servicio de NextCloud está clústerizado en los nodos Madrid/Sevilla. Aunque debido a que la línea de Madrid es mucho mejor (300MB/300MB frente a 30MB/3MB), no se hace balanceo; por defecto todo el tráfico es enrutado hacia el nodo de Madrid, por lo que a efectos prácticos el servicio no dispone de alta disponibilidad (se precisa intervención humana para cambiar la DNS).
- Las comunicaciones entre los nodos de Madrid y Sevilla tiene lugar a través de un túnel VPN montado con
OpenVPN
. - La base de datos es un clúster multimaster de MariaDB 10.1 con un nodo en Madrid y otro en Sevilla además de un Árbitro en Madrid para evitar situaciones de split-brain. Lo idóneo sería contar con una tercera ubicación donde colocar el Árbitro, aunque de momento con este sistema no he tenido problemas en 2 años a pesar de tener numerosas caídas, especialmente del nodo de Sevilla.
- El clúster multimaster de MariaDB esta alojado en otro contenedor, por lo que solo es necesario permitir el login desde la IP del nuevo contenedor de NextCloud; es decir, este punto no se toca en está entrada.
- La replicación a nivel de archivos se consigue a través de
GlusterFS
, con unbrick
en Madrid y otro en Sevilla. - Los nodos del GlusterFS corren dentro del propio contenedor de NextCloud. Para evitar la caída del servicio se añadirá un nuevo nodo al clúster de GlusterFS, y una vez el nuevo servidor este configurado, sincronizado y operativo se eliminará el viejo nodo.
- Redis se usa como cache local para gestionar los bloqueos transaccionales, es decir, no existe un clúster de Redis por los motivos expuestos en el punto 1.
Preparar el SO
Añadir repositorios EPEL e IUS
En este caso se hará uso del repositorios IUS en lugar de REMI, ya que IUS no remplaza paquetes base del sistema, más información: The SafeRepo Initiative (IUS) y Replacement of the base packages? (REMI).
|
|
Además será necesario añadir el repositorio de GlusterFS, en mi caso mi clúster usa los paquetes de la versión GlusterFS 3.8.
|
|
Instalar paquetes
|
|
Configurar GlusterFS
Antes de poder añadir el servidor como nuevo nodo del clúster, es necesario iniciar el demonio y activar el inicio automático del demonio con el sistema:
|
|
Establecer una relación de confianza con el nuevo nodo
Desde cualquiera de los nodos actuales de clúster GlusterFS es necesario indicar al clúster que hay un nuevo nodo y que deben de establecer una relación de confianza con él, esto se hace invocando gluster peer probe <host>
, ejemplo:
|
|
Es posible comprobar que se ha añadido el nuevo nodo al clúster mediante gluster peer status
, ejemplo:
|
|
Y ejecutado desde el nuevo nodo:
|
|
Añadir un nuevo brick al volumen GlusterFS
En terminología GlusterFS un brick
es un volumen de almacenamiento alojado en un nodo (servidor), donde dicho nodo posee una relación de confianza con el resto de nodos del clúster. Es posible añadir nuevos brick
al clúster a través del comando gluster volume add-brick <volume-name> replica <total-brick-number> <host>:<path>
, donde:
<volume-name>
, es el nombre del volumen de almacenamiento a replicar.<total-brick-number>
, es el número total de copias, de cada archivo, a mantener. Puesto que el objetivo es replicar los datos en todos los nodos, este número debe corresponder al número total de bricks para el volumen. En mi caso corresponde al número de nodos, ya que solo monto una vez el volumen en cada nodo.<host>:<path>
, indica el nuevo nodo a añadir así como la ruta absoluta donde deben ser almacenados los datos.
A continuación un ejemplo:
|
|
Es posible consultar el estado del volumen mediante gluster vol info <volume-name>
, ejemplo:
|
|
Configurar el sistema de cache
Lo recomendado es usar APCu para cacher en memoria las versiones compiladas de las páginas PHP y Redis para cachear el Bloqueo Transaccional de Archivos. Más información en Nextcloud 12 Server Administration Manual - Configuring Memory Caching y una pequeña comparación de APCu vs Redis en una Raspberry Pi en owncloudarchive/pi-image - Caching.
Configurar Redis
Puesto que Redis se usará solo desde la máquina local, he optado por un socket Unix-domain en lugar de un socket TCP/IP, es posible leer algunas pinceladas de la diferencia entre ambos tipos de sockets en el apartado socket Unix-domain vs socket TCP/IP del post Apache: Instalar y configurar PHP-FPM (FatsCGI PRocess Manager).
Para ello, editar el archivo de configuración /etc/redis.conf
y ajustar los siguientes parámetros:
|
|
Donde,
port 0
, hará que Redis no escuche en un socket TCP.unixsocket
, indica la ubicación del socket Unix-domain.unixsocketperm
, indica los permisos del socket Unix-domain. Lo normal es dar solo permisos de escritura al usuario, aunque este caso se han dado también al grupo ya que añadiremos el usuariophp-fpm
al gruporedis
para que puede leer y escribir en el socket unix de Redis.supervised systemd
, indica a Redis que debe enviarREADY=1
al$NOTIFY_SOCKET
de systemd cuando este listo.
Por último iniciar el demonio y activar el inicio automático del demonio con el sistema:
|
|
Configurar PHP-FPM 7.1
Editar el archivo de configuración /etc/php-fpm.d/www.conf
y ajustar los siguientes parámetros:
|
|
Es muy importante no olvidar que el demonio php-fpm
debe poder acceder tanto a los archivos PHP como al socket unix de Redis, por ello es necesario añadir al usuario php-fpm
al grupo de nginx
y redis
.
|
|
Si olvidamos el paso anterior, puesto que php-fpm no podrá escribir en el socket unix de Redis, en el log de php-fpm se registrarán errores similares a:
|
|
Por último iniciar el demonio y activar el inicio automático del demonio con el sistema:
|
|
Configurar NGINX
Hay que tener en cuenta que uso un HAProxy como SSL Offloading por lo que el servidor de nginx se limita a crear el php-handler y el virtualhost:
php-handler -> Crear el archivo
/etc/nginx/conf.d/0-php-handler.conf
con el siguiente contenido:123upstream php-handler {server unix:/var/run/php-fpm/www.sock;}Virtualhost -> Crear el archivo
/etc/nginx/conf.d/cloud.guillen.io.conf
con el siguiente contenido:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788server {listen 80;server_name cloud.guillen.io;# Add headers to serve security related headersadd_header X-Content-Type-Options nosniff;add_header X-XSS-Protection "1; mode=block";add_header X-Robots-Tag none;add_header X-Download-Options noopen;add_header X-Permitted-Cross-Domain-Policies none;# Path to the root of your installationroot /var/www/html/nextcloud/;location = /robots.txt {allow all;log_not_found off;access_log off;}location = /.well-known/carddav {return 301 $scheme://$host/remote.php/dav;}location = /.well-known/caldav {return 301 $scheme://$host/remote.php/dav;}# set max upload sizeclient_max_body_size 2048M;fastcgi_buffers 64 4K;# Enable gzip but do not remove ETag headersgzip on;gzip_vary on;gzip_comp_level 4;gzip_min_length 256;gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;location / {rewrite ^ /index.php$uri;}location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {deny all;}location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {deny all;}location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {fastcgi_split_path_info ^(.+\.php)(/.*)$;include fastcgi_params;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_param PATH_INFO $fastcgi_path_info;#Avoid sending the security headers twicefastcgi_param modHeadersAvailable true;fastcgi_param front_controller_active true;fastcgi_pass php-handler;fastcgi_intercept_errors on;fastcgi_request_buffering off;}location ~ ^/(?:updater|ocs-provider)(?:$|/) {try_files $uri/ =404;index index.php;}# Adding the cache control header for js and css files# Make sure it is BELOW the PHP blocklocation ~ \.(?:css|js|woff|svg|gif)$ {try_files $uri /index.php$uri$is_args$args;add_header Cache-Control "public, max-age=15778463";add_header X-Content-Type-Options nosniff;add_header X-XSS-Protection "1; mode=block";add_header X-Robots-Tag none;add_header X-Download-Options noopen;add_header X-Permitted-Cross-Domain-Policies none;# Optional: Don't log access to assetsaccess_log off;}location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {try_files $uri /index.php$uri$is_args$args;# Optional: Don't log access to other assetsaccess_log off;}}
Configurar NextCloud
Cache
Se usará APCu para la cache de objetos locales y Redis para la cache del Bloqueo de Archivos Transacionales.
Editar el archivo /var/www/html/nextcloud/config/config.php
y añadir:
|
|
Clean URL
- Añadir
'htaccess.RewriteBase' => '/',
al archivo/var/www/html/nextcloud/config/config.php
. - Ejecutar
su - nginx -c "php occ maintenance:update:htaccess"
para recrear el archivo htaccess.
Tareas de NextCloud
Añadir las siguientes tareas programadas mediante el comando crontab -u nginx -e
:
|
|