Nextcloud: Implementación tolerante a fallas para empresas medianas





Hay un recolector muy bueno para la gestión conjunta de proyectos, autorización LDAP, sincronización de archivos con control de versiones y algo así como un mensajero corporativo con videoconferencia, que se estropearon en las últimas versiones. Sí, estoy hablando de Nextcloud. Por un lado, soy partidario del método Unix y una clara división de aplicaciones en funciones separadas. Por otro lado, este producto es más que estable, lleva muchos años trabajando en varios proyectos sin problemas y los pitos adicionales no interfieren particularmente con su trabajo. Si realmente quieres, puedes joder casi cualquier juego allí. La comunidad es animada y completa completamente varios complementos que están disponibles como aplicaciones separadas.



Hoy lo estaremos desplegando. No daré una instrucción completa paso a paso, pero trataré de mencionar los puntos clave de la arquitectura a los que vale la pena prestar atención. En particular, analizaremos el equilibrio de carga, la replicación de la base de datos y el mantenimiento de rutina sin interrupción del servicio.



Lo implementaremos en una versión a prueba de fallas para una pequeña empresa con 150-1000 usuarios, pero también será útil para los usuarios domésticos.



¿Qué necesitan las empresas?



La principal diferencia entre el servicio en un acogedor servidor doméstico hecho de bellotas y los partidos del segmento corporativo es la responsabilidad de los usuarios. Sin embargo, incluso en la instalación de mi hogar, considero que es una buena forma enviar mensajes a los usuarios con una advertencia sobre un trabajo planificado o un posible accidente. Después de todo, es el sábado por la noche cuando su amigo puede decidir de repente trabajar con los datos que aloja con usted.



En el caso de una empresa, incluso pequeña, cualquier simple servicio importante significa pérdidas y problemas potenciales. Especialmente si hay muchos procesos vinculados al servicio.



En particular, en mi experiencia, Nextcloud tiene una demanda de varias características entre las pequeñas empresas:



  1. Proporcionar acceso a directorios compartidos y sincronización.
  2. Una característica excelente con la provisión de acceso externo dentro de la federación. Puede integrarse con un producto similar de colegas y otra empresa.
  3. Proporcionar acceso externo a través de un enlace directo. Ayuda mucho si, por ejemplo, trabaja en la industria de la impresión y necesita intercambiar grandes cantidades de datos pesados ​​con los clientes.
  4. Editor de documentos de Collabora que se ejecuta en el lado del servidor y actúa como interfaz para LibreOffice.
  5. Chats y videollamadas. Una característica algo controvertida, no del todo estable, pero está ahí y funciona. Ya se ha estabilizado en la última versión.


Construimos arquitectura



Desafortunadamente, en las últimas versiones, la documentación de implementación de Nextcloud Enterprise solo está disponible para miembros pagos. Sin embargo, como referencia, puede tomar manuales más antiguos que aún son de dominio público.





Típico para uso doméstico e instalaciones individuales.



Una opción "todo en uno" no es mala siempre que tenga pocos usuarios y pueda permitirse el tiempo de inactividad para el mantenimiento de rutina. Por ejemplo, durante una actualización. Además, un esquema monolítico, con ubicación en un nodo, tiene problemas de escala. Por tanto, probaremos la segunda opción.





Opción de implementación escalable recomendada para cargas de trabajo más altas.



Los principales componentes del sistema:



  • 1 Balanceador. Puede utilizar HAproxy o Nginx. Consideraré la opción con Nginx.
  • 2-4 piezas Servidor de aplicaciones (servidor web). La propia instalación de Nextcloud con el código principal en php.
  • 2 DB. En la configuración estándar recomendada, es MariaDB.
  • Almacenamiento NFS.
  • Redis para almacenar en caché las consultas de la base de datos


Balancín



Con esta arquitectura, tendrá menos puntos de falla. El principal punto de falla es el equilibrador de carga. Si no está disponible, los usuarios no podrán acceder al servicio. Afortunadamente, su configuración del mismo nginx es bastante sencilla, como veremos más adelante, y aguanta la carga sin problemas. La mayoría de las fallas en el balanceador se resuelven reiniciando el demonio, todo el nodo o implementando desde una copia de seguridad. No será superfluo tener una reserva fría configurada en otra ubicación con conmutación manual de tráfico a ella en DNS.



Tenga en cuenta que el equilibrador también es un punto de terminación SSL / TLS para sus clientes, y la comunicación con el backend puede ir a través de HTTP para redes internas confiables o con HTTPS adicional si el tráfico al servidor de aplicaciones pasa por canales generales no confiables.



Base de datos



Una solución típica es MySQL / MariaDB en la ejecución del clúster en la replicación maestro-esclavo. Al mismo tiempo, solo tiene una base de datos activa y la segunda funciona en modo de espera en caliente en caso de una falla de emergencia de la principal o durante el trabajo programado. También se puede considerar el equilibrio de carga, pero técnicamente es más difícil. Al usar MariaDB Galera Cluster con la opción de replicación maestro-maestro, debe usar un número impar de nodos, pero al menos tres. Por lo tanto, el riesgo de situaciones de cerebro dividido se minimiza cuando se corta la conectividad entre nodos.



Almacenamiento



Cualquier solución que sea mejor para usted que proporciona el protocolo NFS. Para cargas elevadas, considere IBM Elastic Storage o Ceph. También es posible utilizar el almacenamiento de objetos compatible con S3, pero esta es una opción para instalaciones muy grandes.



HDD o SSD



En principio, para instalaciones de tamaño medio, basta con utilizar únicamente el disco duro. El cuello de botella aquí será iops al leer de la base de datos, lo que afecta en gran medida la capacidad de respuesta del sistema, pero si tiene Redis, que almacena en caché todo en la RAM, esto no será un gran problema. Además, parte del caché se almacenará en memcached en servidores de aplicaciones. Sin embargo, recomendaría alojar sus servidores de aplicaciones en un SSD siempre que sea posible. La interfaz web se siente mucho más receptiva. Al mismo tiempo, la misma sincronización de archivos en los clientes de escritorio funcionará de la misma manera que cuando se usa HDD para estos nodos.



La velocidad de sincronización y carga de archivos estará determinada por el rendimiento de su almacenamiento NFS.



Configurando el balanceador



Como ejemplo, daré una configuración básica simple y nginx eficiente. Sí, varios failover-buns adicionales están disponibles solo en la versión paga, pero incluso en la versión básica cumple perfectamente con su tarea. Tenga en cuenta que el round robin o el equilibrio aleatorio no son adecuados para nosotros, ya que los servidores de aplicaciones almacenan cachés para clientes específicos.

Afortunadamente, esto se resuelve mediante el método ip_hash . En este caso, las sesiones del usuario se asignarán a un backend específico, al que se dirigirán todas las solicitudes del usuario. Este punto se describe en la documentación:

, IP- . IPv4- IPv6- . , . , . .


Desafortunadamente, al usar este método, puede haber problemas con los usuarios que están detrás de una IP dinámica y la cambian constantemente. Por ejemplo, en clientes con Internet móvil, que se pueden lanzar por diferentes rutas al cambiar entre celdas. La cookie adhesiva que resuelve este problema solo está disponible en la versión paga.



El archivo de configuración nginx describe esto de la siguiente manera:



upstream backend {
    ip_hash;

    server backend1_nextcloud.example.com;
    server backend2_nextcloud.example.com;
    server backend3_nextcloud.example.com;
    server backend4_nextcloud.example.com;
}


En este caso, la carga se distribuirá lo más uniformemente posible entre los servidores de aplicaciones, aunque pueden producirse desequilibrios de carga debido a que el cliente está vinculado a una sesión específica. Para instalaciones pequeñas y medianas, esto se puede descuidar. Si sus backends tienen diferente potencia, entonces puede establecer el peso de cada uno de ellos. Luego, el equilibrador intentará distribuir la carga proporcionalmente a los pesos dados:



upstream backend {
    ip_hash;

    server backend1_nextcloud.example.com weight=3;
    server backend2_nextcloud.example.com;
    server backend3_nextcloud.example.com;
}


En el ejemplo dado, de 5 solicitudes recibidas, 3 irán al backend1, 1 al backend2 y 1 al backend3.



Si uno de los servidores de aplicaciones falla, nginx intentará redirigir la solicitud al siguiente servidor de la lista de backends.



Configurando la base de datos



Los detalles de la configuración maestro-esclavo se pueden encontrar en la documentación principal .



Echemos un vistazo a algunos puntos clave. Primero, creamos un usuario para la replicación de datos:



create user 'replicant'@'%' identified by 'replicant_password';
grant replication slave on *.* to replicant;
flush privileges;


Luego editamos la configuración maestra:



sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf 


En el área del bloque "Registro y replicación", realice las modificaciones necesarias:



[mysqld]
log-bin         = /var/log/mysql/master-bin
log-bin-index   = /var/log/mysql/master-bin.index
binlog_format   = mixed
server-id       = 01
replicate-do-db = nextcloud
bind-address = 192.168.0.6


En el esclavo, configuramos la configuración:



sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf 


En el área del bloque "Registro y replicación", realice las modificaciones necesarias:



    [mysqld]
    server-id       = 02
    relay-log-index = /var/log/mysql/slave-relay-bin.index
    relay-log       = /var/log/mysql/slave-relay-bin
    replicate-do-db = nextcloud
    read-only = 1
    bind-address    = 192.168.0.7


Reinicie ambos servidores:



sudo systemctl restart mariadb


A continuación, deberá copiar la base de datos en esclavo.

En el Master, primero realizamos el bloqueo de la tabla:



flush tables with read lock;


Y luego miramos el estado:




    MariaDB [(none)]> show master status;
    +-------------------+----------+--------------+------------------+
    | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |
    +-------------------+----------+--------------+------------------+
    | master-bin.000001 |      772 |              |                  |
    +-------------------+----------+--------------+------------------+
    1 row in set (0.000 sec)


¡No salga de la consola de la base de datos, de lo contrario se eliminarán los bloqueos!

Necesitaremos master_log_file y master_log_pos desde aquí para la configuración del esclavo.

Vaciar y quitar cerraduras:




sudo mysqldump -u root nextcloud > nextcloud.sql



    > unlock tables;
    > exit;


Luego importamos el volcado a Slave y reiniciamos el demonio:




sudo mysqldump -u root nextcloud < nextcloud.sql
sudo systemctl restart mariadb


Después de eso, configure la replicación en la consola:




    MariaDB [(none)]> change master 'master01' to     
    master_host='192.168.0.6',     
    master_user='replicant',     
    master_password='replicant_password',     
    master_port=3306,     
    master_log_file='master-bin.000001',     
    master_log_pos=772,     
    master_connect_retry=10,     
    master_use_gtid=slave_pos;


Lanzamos y comprobamos:




> start slave 'master01';
show slave 'master01' status\G;


No debe haber errores en la respuesta y dos puntos indicarán el éxito del procedimiento:



Slave_IO_Running: Yes
Slave_SQL_Running: Yes


Implementar nodos de aplicación



Hay varias opciones de implementación:



  1. chasquido
  2. docker-image
  3. actualización manual


Snap está disponible principalmente para Ubuntu. Es bastante bueno para entregar aplicaciones propietarias complejas, pero por defecto. Pero tiene una característica que es bastante molesta en un entorno industrial: actualiza automáticamente sus paquetes varias veces al día. También tendrá que cortar accesos adicionales al exterior si tiene una red interna delimitada rígidamente. Al mismo tiempo, reflejar sus repositorios en el interior no es del todo trivial.



Sí, hay canales de suscripción y lanzamientos importantes, en teoría, no debería cambiar, pero piénselo. Recomendaría un control total sobre el proceso de actualización, especialmente porque a menudo va acompañado de un cambio en la estructura de datos en la base de datos.



Docker-image es una buena opción, especialmente si su infraestructura ya se está ejecutando en Kubernetes. Es probable que el mismo nodo de Redis vaya al clúster después de los servidores de aplicaciones.



Si no tiene la infraestructura para hacer esto, entonces la actualización e implementación manualmente desde tar.gz es bastante conveniente y controlable.



Recuerde que necesitará instalar un servidor web en el servidor de aplicaciones para manejar las solicitudes entrantes. Recomendaría un montón de nginx + php-fpm7.4. Con las últimas versiones de php-fmp, el rendimiento y la capacidad de respuesta han mejorado significativamente.



Configuración de SSL / TLS



Definitivamente debe contar con TLS 1.3 si está realizando una nueva instalación y no hay problemas con los paquetes nginx que dependen de la actualización del sistema openssl. En particular, 0-RTT y otras ventajas permiten en ocasiones acelerar significativamente la reconexión del cliente debido al almacenamiento en caché. La seguridad también es mayor debido a la eliminación de protocolos obsoletos.



Daré la configuración real para el servidor de aplicaciones nginx, que se comunica con el equilibrador a través de TLS:



Configuración de Nginx
upstream php-handler {
 server unix:/var/run/php/php7.4-fpm.sock;
}

server {
    listen 80;
    server_name backend1_nextcloud.example.com;
    # enforce https
    root /var/www/nextcloud/;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    ssl_early_data on;
#    listen [::]:443 ssl http2;
    server_name backend1_nextcloud.example.com;

    # Path to the root of your installation
    root /var/www/nextcloud/;
    # Log path
    access_log /var/log/nginx/nextcloud.nginx-access.log;
    error_log /var/log/nginx/nextcloud.nginx-error.log;
    ### SSL CONFIGURATION ###
        ssl_certificate /etc/letsencrypt/live/backend1_nextcloud.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/backend1_nextcloud.example.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/backend1_nextcloud.example.com/fullchain.pem;
        ssl_dhparam /etc/ssl/certs/dhparam.pem;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        #ssl_ciphers "EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:!AES128";
        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POL>
        ssl_session_cache shared:SSL:50m;
        ssl_session_timeout 5m;

        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.4.4 8.8.8.8;

        add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload' always;
###   SSL ###

    # Add headers to serve security related headers
    # Before enabling Strict-Transport-Security headers please read into this
    # topic first.
    # add_header Strict-Transport-Security "max-age=15768000;
    # includeSubDomains; preload;";
    #
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # The following 2 rules are only needed for the user_webfinger app.
    # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
    # last;

    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 size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip 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-fon>

    # Uncomment if your server is build with the ngx_pagespeed module
    # This module is currently not supported.
    #pagespeed off;

    location / {
        rewrite ^ /index.php;
    }

    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\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
        fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
        set $path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;
        # Avoid sending the security headers twice
        fastcgi_param modHeadersAvailable true;
        # Enable pretty urls
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

    # Adding the cache control header for js, css and map files
    # Make sure it is BELOW the PHP block
    location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
        #
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" always;

        # Optional: Don't log access to assets
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }
}


Mantenimiento de rutina



Recuerde, en un entorno industrial, debe proporcionar un tiempo de inactividad mínimo y cero para las actualizaciones o incluso más copias de seguridad. La dificultad clave aquí es la dependencia del estado de los metadatos en la base de datos y los propios archivos, que están disponibles a través de NFS o almacenamiento de objetos.



Al actualizar los servidores de aplicaciones a una nueva versión secundaria, no hay problemas especiales. Pero el clúster aún debe transferirse al modo de mantenimiento para actualizar la estructura de la base de datos.

Apague el equilibrador en el momento de menor carga y continúe con la actualización.



Después de eso, llevamos a cabo el proceso de actualización manual en él desde el tar.gz descargado, mientras guardamos el archivo de configuración config.php. ¡Actualizar a través de la web en grandes instalaciones es una muy mala idea!

Actualizamos a través de la línea de comando:



sudo -u www-data php /var/www/nextcloud/occ upgrade


Después de eso, encienda el balanceador y envíe tráfico al servidor actualizado. Para hacer esto, eliminamos todos los servidores de aplicaciones no actualizados del equilibrio:



upstream backend {
    ip_hash;

    server backend1_nextcloud.example.com;
    server backend2_nextcloud.example.com down;
    server backend3_nextcloud.example.com down;
    server backend4_nextcloud.example.com down;
}


El resto de nodos se van actualizando y poniendo en funcionamiento paulatinamente. En este caso, no es necesario realizar la actualización occ. Solo necesita reemplazar los archivos php y guardar la configuración.



Al realizar una copia de seguridad, debe detener la replicación en esclavo y realizar un volcado simultáneo de metadatos de la base de datos simultáneamente con la creación de una instantánea de los archivos en el almacenamiento. Necesitas almacenarlos en pares. La recuperación debe realizarse de manera similar desde un volcado de base de datos y archivos durante el mismo período. De lo contrario, es posible la pérdida de datos, ya que el archivo puede estar en el almacenamiento, pero no tener metadatos en la base de datos.










All Articles