Nextcloud: Fehlertolerante Bereitstellung für mittelständische Unternehmen





Es gibt einen sehr coolen Harvester für gemeinsames Projektmanagement, LDAP-Autorisierung, Dateisynchronisierung mit Versionierung und so etwas wie einen Corporate Messenger mit Videokonferenzen, die in den neuesten Versionen vermasselt wurden. Ja, ich spreche von Nextcloud. Einerseits bin ich ein Befürworter des Unix-Weges und einer klaren Aufteilung der Anwendungen in separate Funktionen. Auf der anderen Seite ist dieses Produkt mehr als stabil, es arbeitet seit vielen Jahren in mehreren Projekten ohne Probleme und zusätzliche Pfeifen beeinträchtigen seine Arbeit nicht besonders. Wenn Sie wirklich wollen, können Sie dort fast jedes Spiel schrauben. Die Community ist lebhaft und vervollständigt verschiedene Plugins, die als separate Anwendungen verfügbar sind.



Heute werden wir es bereitstellen. Ich werde keine vollständige Schritt-für-Schritt-Anleitung geben, aber ich werde versuchen, die wichtigsten Punkte der Architektur zu erwähnen, die es wert sind, beachtet zu werden. Schauen wir uns insbesondere den Lastenausgleich, die Datenbankreplikation und die routinemäßige Wartung ohne Dienstunterbrechung an.



Wir werden es in einer ausfallsicheren Version für ein kleines Unternehmen mit 150-1000 Benutzern bereitstellen, aber es wird auch für Heimanwender nützlich sein.



Was brauchen Unternehmen?



Der Hauptunterschied zwischen dem Service auf einem gemütlichen Heimserver aus Eicheln und Streichhölzern aus dem Unternehmenssegment liegt in der Verantwortung der Benutzer. Selbst in meiner Heiminstallation halte ich es jedoch für eine gute Form, Nachrichten an Benutzer mit einer Warnung vor geplanten Arbeiten oder einem möglichen Unfall zu senden. Schließlich kann es sein, dass Ihr Freund am Samstagabend plötzlich beschließt, mit den Daten zu arbeiten, die er mit Ihnen hostet.



Im Falle eines Unternehmens, auch eines kleinen, bedeutet jede einfache wichtige Dienstleistung potenzielle Verluste und Probleme. Vor allem, wenn viele Prozesse an den Service gebunden sind.



Nach meiner Erfahrung ist Nextcloud insbesondere bei kleinen Unternehmen nach mehreren Funktionen gefragt:



  1. Bereitstellung des Zugriffs auf freigegebene Verzeichnisse und Synchronisierung.
  2. Ein Killer-Feature mit der Bereitstellung eines externen Zugriffs innerhalb des Verbandes. Sie können ein ähnliches Produkt von Kollegen und einem anderen Unternehmen integrieren.
  3. Bereitstellung eines externen Zugriffs über eine direkte Verbindung. Es ist sehr hilfreich, wenn Sie beispielsweise in der Druckindustrie arbeiten und große Mengen schwerer Daten mit Kunden austauschen müssen.
  4. Collabora-Dokumenteditor, der auf der Serverseite ausgeführt wird und als Frontend für LibreOffice fungiert.
  5. Chats und Videoanrufe. Ein etwas kontroverses, nicht ganz stabiles Feature, aber es ist da und funktioniert. In der neuesten Version wurde es bereits stabilisiert.


Wir bauen Architektur



Leider steht in den neuesten Versionen die Dokumentation zur Nextcloud Enterprise-Implementierung nur bezahlten Mitgliedern zur Verfügung. Als Referenz können Sie jedoch ältere Handbücher verwenden, die noch gemeinfrei sind.





Typisch für den Heimgebrauch und Einzelinstallationen.



Die All-in-One-Option ist nicht schlecht, solange Sie nur wenige Benutzer haben und sich die Ausfallzeit für die routinemäßige Wartung leisten können. Zum Beispiel während eines Updates. Ein monolithisches Schema mit Platzierung auf einem Knoten hat auch Probleme mit der Skalierung. Daher werden wir die zweite Option ausprobieren.





Skalierbare Bereitstellungsoption für höhere Workloads empfohlen.



Die Hauptkomponenten des Systems:



  • 1 Balancer. Sie können HAproxy oder Nginx verwenden. Ich werde die Option mit Nginx in Betracht ziehen.
  • 2-4 Stück Anwendungsserver (Webserver). Die Installation von Nextcloud selbst mit dem Hauptcode in PHP.
  • 2 DB. In der empfohlenen Standardkonfiguration ist dies MariaDB.
  • NFS-Speicher.
  • Redis zum Zwischenspeichern von Datenbankabfragen


Balancer



Mit dieser Architektur haben Sie weniger Fehlerquellen. Der Hauptfehlerpunkt ist der Load Balancer. Wenn es nicht verfügbar ist, können Benutzer den Dienst nicht erreichen. Glücklicherweise ist die Konfiguration desselben Nginx recht einfach, wie wir weiter unten betrachten werden, und es hält die Last ohne Probleme. Die meisten Fehler auf dem Balancer können behoben werden, indem der Dämon oder der gesamte Knoten neu gestartet oder aus einer Sicherung bereitgestellt werden. Es ist nicht überflüssig, eine konfigurierte Kühlreserve an einem anderen Ort zu haben, auf die der manuelle Datenverkehr in DNS umschaltet.



Beachten Sie, dass der Balancer auch ein SSL / TLS-Endpunkt für Ihre Clients ist und die Kommunikation mit dem Backend entweder über HTTP für vertrauenswürdige interne Netzwerke oder mit zusätzlichem HTTPS erfolgen kann, wenn der Datenverkehr zum Anwendungsserver über allgemeine nicht vertrauenswürdige Kanäle erfolgt.



Datenbank



Eine typische Lösung ist MySQL / MariaDB bei der Clusterausführung bei der Master-Slave-Replikation. Gleichzeitig haben Sie nur eine aktive Datenbank, und die zweite arbeitet im Hot-Standby-Modus, falls die Hauptdatenbank im Notfall ausfällt oder während der geplanten Arbeit. Ein Lastausgleich kann ebenfalls in Betracht gezogen werden, ist jedoch technisch schwieriger. Wenn Sie MariaDB Galera Cluster mit der Master-Master-Replikationsoption verwenden, müssen Sie eine ungerade Anzahl von Knoten verwenden, jedoch mindestens drei. Somit wird das Risiko von Split-Brain-Situationen minimiert, wenn die Konnektivität zwischen Knoten unterbrochen wird.



Lager



Jede für Sie am besten geeignete Lösung, die das NFS-Protokoll bietet. Ziehen Sie bei hohen Lasten IBM Elastic Storage oder Ceph in Betracht. Es ist auch möglich, S3-kompatiblen Objektspeicher zu verwenden, dies ist jedoch eher eine Option für sehr große Installationen.



Festplatte oder SSD



Grundsätzlich ist für mittelgroße Installationen die Verwendung nur der Festplatte ausreichend. Der Engpass hier ist iops beim Lesen aus der Datenbank, was die Reaktionsfähigkeit des Systems stark beeinflusst. Wenn Sie jedoch Redis haben, das alles im RAM zwischenspeichert, ist dies kein großes Problem. Außerdem wird ein Teil des Caches in Memcaching auf Anwendungsservern gespeichert. Ich würde jedoch empfehlen, Ihre Anwendungsserver nach Möglichkeit auf einer SSD zu hosten. Das Webinterface fühlt sich viel reaktionsschneller an. In diesem Fall funktioniert dieselbe Dateisynchronisierung auf Desktop-Clients ungefähr genauso wie bei Verwendung der Festplatte für diese Knoten.



Die Geschwindigkeit der Synchronisierung und des Hochladens von Dateien hängt von der Leistung Ihres NFS-Speichers ab.



Balancer konfigurieren



Als Beispiel werde ich eine einfache Grundkonfiguration und effizientes Nginx geben. Ja, verschiedene zusätzliche Failover-Brötchen sind nur in der kostenpflichtigen Version verfügbar, aber selbst in der Basisversion erfüllt es seine Aufgabe perfekt. Bitte beachten Sie, dass Round Robin oder Random Balancing für uns nicht geeignet sind, da Anwendungsserver Caches für bestimmte Clients speichern.

Glücklicherweise wird dies mit der Methode ip_hash gelöst . In diesem Fall werden die Sitzungen des Benutzers einem bestimmten Backend zugewiesen, an das alle Anforderungen des Benutzers gerichtet werden. Dieser Punkt wird in der Dokumentation beschrieben:

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


Leider kann es bei dieser Methode zu Problemen mit Benutzern kommen, die sich hinter einer dynamischen IP befinden und diese ständig ändern. Zum Beispiel auf Clients mit mobilem Internet, die beim Wechseln zwischen Zellen auf verschiedenen Wegen eingesetzt werden können. Sticky Cookie, das dieses Problem löst, ist nur in der kostenpflichtigen Version verfügbar.



Die Nginx-Konfigurationsdatei beschreibt dies wie folgt:



upstream backend {
    ip_hash;

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


In diesem Fall wird die Last so gleichmäßig wie möglich auf die Anwendungsserver verteilt, obwohl Lastungleichgewichte auftreten können, weil der Client an eine bestimmte Sitzung gebunden ist. Bei kleinen bis mittelgroßen Installationen kann dies vernachlässigt werden. Wenn Ihre Backends eine unterschiedliche Leistung haben, können Sie das Gewicht jedes einzelnen einstellen . Dann versucht der Balancer, die Last proportional zu den angegebenen Gewichten zu verteilen:



upstream backend {
    ip_hash;

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


Im angegebenen Beispiel gehen von 5 empfangenen Anforderungen 3 an Backend1, 1 an Backend2 und 1 an Backend3.



Wenn einer der Anwendungsserver ausfällt, versucht nginx, die Anforderung aus der Liste der Backends auf den nächsten Server umzuleiten.



Datenbank konfigurieren



Details zur Master-Slave-Konfiguration finden Sie in der Hauptdokumentation .



Schauen wir uns einige wichtige Punkte an. Zunächst erstellen wir einen Benutzer für die Datenreplikation:



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


Dann bearbeiten wir die Master-Konfiguration:



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


Nehmen Sie im Bereich des Blocks "Protokollierung und Replikation" die erforderlichen Änderungen vor:



[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


Auf dem Slave konfigurieren wir die Konfiguration:



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


Nehmen Sie im Bereich des Blocks "Protokollierung und Replikation" die erforderlichen Änderungen vor:



    [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


Starten Sie beide Server neu:



sudo systemctl restart mariadb


Als Nächstes müssen Sie die Datenbank in Slave kopieren.

Auf dem Master führen wir zuerst eine Tabellensperre durch:



flush tables with read lock;


Und dann schauen wir uns den Status an:




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


Verlassen Sie die Datenbankkonsole nicht, sonst werden die Sperren entfernt!

Von hier aus benötigen wir master_log_file und master_log_pos für die Slave-Konfiguration.

Sperren entleeren und entfernen:




sudo mysqldump -u root nextcloud > nextcloud.sql



    > unlock tables;
    > exit;


Dann importieren wir den Dump in Slave und starten den Daemon neu:




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


Richten Sie danach die Replikation in der Konsole ein:




    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;


Starten und überprüfen:




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


Die Antwort sollte keine Fehler enthalten und zwei Punkte zeigen den Erfolg des Verfahrens an:



Slave_IO_Running: Yes
Slave_SQL_Running: Yes


Bereitstellen von Anwendungsknoten



Es gibt verschiedene Bereitstellungsoptionen:



  1. einrasten
  2. Docker-Image
  3. manuelles Update


Snap ist hauptsächlich für Ubuntu verfügbar. Es ist ziemlich gut darin, komplexe proprietäre Anwendungen bereitzustellen, aber standardmäßig. Aber es hat eine Funktion, die in einer industriellen Umgebung ziemlich ärgerlich ist - es aktualisiert seine Pakete mehrmals am Tag automatisch. Sie müssen auch zusätzliche Zugriffe nach außen durchschauen, wenn Sie ein streng abgegrenztes internes Netzwerk haben. Gleichzeitig ist das Spiegeln der Repositorys im Inneren nicht ganz trivial.



Ja, es gibt Abonnementkanäle und Hauptversionen, theoretisch sollte es nicht wechseln, sondern darüber nachdenken. Ich würde die vollständige Kontrolle über den Aktualisierungsprozess empfehlen, insbesondere da dies häufig mit einer Änderung der Datenstruktur in der Datenbank einhergeht.



Docker-Image ist eine gute Option, insbesondere wenn Ihre Infrastruktur bereits auf Kubernetes ausgeführt wird. Derselbe Redis-Knoten wird wahrscheinlich nach den Anwendungsservern zum Cluster wechseln.



Wenn Sie nicht über die entsprechende Infrastruktur verfügen, ist das manuelle Aktualisieren und Bereitstellen von tar.gz sehr bequem und kontrollierbar.



Denken Sie daran, dass Sie einen Webserver auf dem Anwendungsserver installieren müssen, um eingehende Anforderungen zu verarbeiten. Ich würde ein Bundle von nginx + php-fpm7.4 empfehlen. Mit den neuesten Versionen von php-fmp haben sich Leistung und Reaktionsfähigkeit erheblich verbessert.



Konfigurieren von SSL / TLS



Sie sollten auf jeden Fall auf TLS 1.3 zählen, wenn Sie eine Neuinstallation durchführen und es keine Probleme mit Nginx-Paketen gibt, die von der Aktualität des Systems openssl abhängen. Insbesondere 0-RTT und andere Extras ermöglichen es manchmal, die Client-Wiederverbindung aufgrund von Caching erheblich zu beschleunigen. Die Sicherheit ist auch höher, da veraltete Protokolle herausgeschnitten werden.



Ich werde die eigentliche Konfiguration für den nginx-Anwendungsserver angeben, der über TLS mit dem Balancer kommuniziert:



Nginx-Konfiguration
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;
    }
}


Routinewartung



Denken Sie daran, dass Sie in einer industriellen Umgebung minimale und keine Ausfallzeiten für Upgrades oder noch mehr Backups bereitstellen müssen. Die Hauptschwierigkeit hierbei ist die Abhängigkeit des Status der Metadaten in der Datenbank und der Dateien selbst, die über NFS oder Objektspeicher verfügbar sind.



Beim Upgrade von Anwendungsservern auf eine neue Nebenversion treten keine besonderen Probleme auf. Der Cluster muss jedoch noch in den Wartungsmodus geschaltet werden, um die Datenbankstruktur zu aktualisieren.

Schalten Sie den Balancer zum Zeitpunkt der geringsten Belastung aus und fahren Sie mit der Aktualisierung fort.



Danach führen wir den manuellen Aktualisierungsprozess aus dem heruntergeladenen tar.gz durch, während wir die Konfigurationsdatei config.php speichern. Das Aktualisieren großer Installationen über das Internet ist eine sehr schlechte Idee!

Wir aktualisieren über die Befehlszeile:



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


Danach schalten wir den Balancer ein und senden Datenverkehr an den aktualisierten Server. Zu diesem Zweck entfernen wir alle nicht aktualisierten Anwendungsserver aus dem Balancing:



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;
}


Der Rest der Knoten wird schrittweise aktualisiert und in Betrieb genommen. In diesem Fall ist kein Occup-Upgrade erforderlich! Sie müssen nur die PHP-Dateien ersetzen und die Konfiguration speichern.



Beim Sichern müssen Sie die Replikation auf Slave stoppen und gleichzeitig einen Speicherauszug von Metadaten aus der Datenbank durchführen und gleichzeitig einen Snapshot der Dateien im Speicher erstellen. Sie müssen sie paarweise speichern. Die Wiederherstellung sollte in ähnlicher Weise aus einem Datenbankspeicherauszug und Dateien für denselben Zeitraum durchgeführt werden. Andernfalls ist ein Datenverlust möglich, da sich die Datei möglicherweise im Speicher befindet, jedoch keine Metadaten in der Datenbank enthält.










All Articles