Nextcloud:中型公司的容错部署





有一个非常酷的收割机,用于联合项目管理,LDAP授权,带有版本控制的文件同步,以及带有视频会议的企业Messenger,这些都是最新版本。是的,我说的是Nextcloud。一方面,我是Unix-way的支持者,并且将应用程序明确划分为多个单独的功能。另一方面,该产品不仅稳定,而且在多个项目中已经工作了多年,没有任何问题,并且额外的哨声不会特别影响其工作。如果您确实愿意,那么几乎可以在那里玩任何游戏。该社区非常活跃,并且完全完成了可作为独立应用程序使用的各种插件。



今天,我们将对其进行部署。我不会提供完整的分步说明,但是我将尝试提及值得关注的体系结构关键点。特别是,我们将分析负载平衡,数据库复制和日常维护,而不会中断服务。



我们将为具有150-1000个用户的小型公司部署故障保护版本,但对于家庭用户也将派上用场。



公司需要什么?



由橡子制成的舒适家庭服务器上的服务与公司部门的匹配产品之间的主要区别在于对用户的责任。但是,即使在家庭安装中,我也认为这是一种向用户发送消息的良好形式,其中带有有关计划工作或潜在事故的警告。毕竟,在星期六晚上,您的朋友可能突然决定使用他与您托管的数据。



就公司而言,即使是很小的公司,任何简单的重要服务都意味着潜在的损失和问题。尤其是服务涉及许多流程时。



特别是,根据我的经验,Nextcloud在小型公司中对以下几个功能有需求:



  1. 提供对共享目录和同步的访问。
  2. 联盟内部提供外部访问的杀手级功能。您可以与同事和另一家公司的类似产品集成。
  3. 通过直接链接提供外部访问。例如,如果您从事印刷行业并且需要与客户交换大量的重要数据,则对您有很大帮助。
  4. 在服务器端运行的Collabora文档编辑器,并充当LibreOffice的前端。
  5. 聊天和视频通话。一个有争议的,不是完全稳定的功能,但是它确实存在并且可以正常工作。在最新版本中,它已经稳定。


我们建造建筑



不幸的是,在最新版本中,Nextcloud企业实施文档仅适用于付费会员。但是,作为参考,您可以使用仍在公共领域中的旧版手册。





典型用于家庭和单个安装。



只要您的用户很少,并且可以承受常规维护的停机时间,“多合一”选项就不错。例如,在更新期间。同样,在一个节点上放置一个整体方案在缩放方面存在问题。因此,我们将尝试第二种选择。





对于更高的工作负载,建议使用可扩展的部署选项。



系统的主要组成部分:



  • 1个平衡器。您可以使用HAproxy或Nginx。我将考虑使用Nginx的选项。
  • 2-4件应用程序服务器(Web服务器)。使用php中的主要代码安装Nextcloud本身。
  • 2 DB。在推荐的标准配置中,这是MariaDB。
  • NFS存储。
  • Redis用于缓存数据库查询


平衡器



使用这种架构,您将减少故障点。故障的主要点是负载平衡器。如果不可用,则用户将无法访问该服务。幸运的是,我们将进一步考虑使用相同的nginx对其进行配置,并且它可以毫无问题地保持负载。平衡器上的大多数故障可以通过重新启动守护程序,整个节点或从备份进行部署来解决。在DNS中通过手动将流量切换到另一个位置来配置冷储备并不是多余的。



请注意,平衡器还是客户端的SSL / TLS终止点,与后端的通信可以通过HTTP进行受信任的内部网络的通信,也可以通过其他HTTPS进行,如果到应用程序服务器的流量通过一般的不受信任的通道进行。



数据库



一个典型的解决方案是MySQL / MariaDB在主从复制中的集群执行中。同时,您只有一个活动数据库,第二个数据库在主数据库发生紧急故障或在计划工作期间以热备用模式运行。也可以考虑负载平衡,但是在技术上更加困难。将MariaDB Galera Cluster与master-master复制选项一起使用时,您需要使用奇数个节点,但至少要使用三个。因此,当节点之间的连接断开时,裂脑情况的风险降至最低。



存储



NFS协议提供的最适合您的任何解决方案。对于高负载,请考虑使用IBM Elastic Storage或Ceph。也可以使用与S3兼容的对象存储,但这对于大型安装而言是一种选择。



硬盘或固态硬盘



原则上,对于中型安装,仅使用HDD就足够了。从数据库读取数据时,瓶颈将是iops,这将极大地影响系统的响应能力,但是如果您拥有Redis(可将所有内容缓存在RAM中),那么这将不是一个大问题。同样,部分缓存将存储在应用程序服务器上的memcached中。但是,我建议您尽可能将应用程序服务器托管在SSD上。Web界面感觉更加灵敏。同时,桌面客户端上相同的文件同步将以与对这些节点使用HDD时相同的方式工作。



同步和文件上传的速度将取决于NFS存储的性能。



配置平衡器



例如,我将给出一个简单的基本配置和有效的nginx。是的,仅在付费版本中提供了各种其他故障转移按钮,但即使在基本版本中,它也可以完美地完成其任务。请注意,轮循或随机平衡不适合我们,因为应用程序服务器存储特定客户端的缓存。

幸运的是,可以使用ip_hash方法解决此问题。在这种情况下,用户会话将分配给特定的后端,来自用户的所有请求都将定向到该后端。这一点在文档中进行了描述:

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


不幸的是,使用这种方法时,动态IP背后的用户可能会遇到问题,并且不断地对其进行更改。例如,在具有移动Internet的客户端上,当在单元之间进行切换时,可能会沿着不同的路线抛出该客户端。解决此问题的粘性cookie仅在付费版本中可用。



Nginx配置文件对此进行了如下描述:



upstream backend {
    ip_hash;

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


在这种情况下,负载将在应用程序服务器之间尽可能平均地分布,尽管由于客户端绑定到特定会话,所以可能会出现负载不平衡。对于中小型安装,这可以忽略。如果您的后端功能不同,则可以设置每个后端的权重然后,平衡器将尝试按给定的重量按比例分配负载:



upstream backend {
    ip_hash;

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


在给定的示例中,在收到的5个请求中,有3个将进入后端1,1进入后端2,1进入后端3。



如果其中一个应用程序服务器发生故障,nginx将尝试将请求从后端列表重定向到下一个服务器。



配置数据库



主从配置的详细信息可以在主文档中找到



让我们看一些关键点。首先,我们创建一个用于数据复制的用户:



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


然后我们编辑主配置:



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


在“记录和复制”块的区域中,进行必要的编辑:



[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


在从站上配置配置:



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


在“记录和复制”块的区域中,进行必要的编辑:



    [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


重新启动两个服务器:



sudo systemctl restart mariadb


接下来,您需要将数据库复制到从站。

在主服务器上,我们首先执行表锁定:



flush tables with read lock;


然后我们看一下状态:




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


不要退出数据库控制台,否则锁将被删除!

从这里我们需要master_log_file和master_log_pos来配置Slave。

转储和删除锁:




sudo mysqldump -u root nextcloud > nextcloud.sql



    > unlock tables;
    > exit;


然后,我们将转储导入到Slave并重新启动守护程序:




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


之后,在控制台中设置复制:




    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;


我们启动并检查:




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


答案应该没有错误,两点将表明该过程成功:



Slave_IO_Running: Yes
Slave_SQL_Running: Yes


部署应用程序节点



有几个部署选项:



  1. 码头工人形象
  2. 手动更新


Snap主要适用于Ubuntu。默认情况下,它擅长交付复杂的专有应用程序。但是它具有一个在工业环境中令人讨厌的功能-它每天自动几次更新其软件包。如果您具有严格分隔的内部网络,则还必须查看对外部的其他访问。同时,在内部镜像其存储库也不是一件容易的事。



是的,从理论上讲,有订阅渠道和主要发行版本,不应切换,但请考虑一下。我建议完全控制更新过程,特别是因为它通常伴随着数据库中数据结构的更改。



Docker映像是一个不错的选择,尤其是当您的基础架构已经在Kubernetes上运行时。同一Redis节点很可能会在应用程序服务器之后进入群集。



如果您没有执行此操作的基础结构,那么从tar.gz进行手动更新和部署将非常方便且可控。



请记住,您将需要在应用程序服务器上安装Web服务器来处理传入的请求。我会推荐一堆Nginx + php-fpm7.4。使用最新版本的php-fmp,性能和响应能力得到了显着改善。



配置SSL / TLS



如果您要进行新安装,则绝对应该指望TLS 1.3,并且依赖于系统openssl的最新性的nginx软件包没有问题。特别是,0-RTT和其他功能有时会由于缓存而极大地加快客户端重新连接的速度。由于淘汰了过时的协议,因此安全性也更高。



我将给出nginx应用服务器的实际配置,该服务器通过TLS与平衡器通信:



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


例行维修



请记住,在工业环境中,您需要为升级甚至更多备份提供最少零停机时间。这里的主要困难是数据库和文件本身中元数据状态的依赖关系,可通过NFS或对象存储获得这些信息。



将应用程序服务器升级到新的次要版本时,没有特殊问题。但是群集仍需要切换到维护模式以更新数据库结构。

在负载最小时关闭平衡器,然后继续进行更新。



之后,我们从下载的tar.gz对其进行手动更新过程,同时保存config.php配置文件。在大型安装上通过Web更新是一个非常糟糕的主意!

我们通过命令行更新:



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


之后,我们打开平衡器并将流量发送到更新的服务器。为此,我们从平衡中删除所有未更新的应用程序服务器:



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


其余节点正在逐步更新并投入运行。在这种情况下,无需执行occ升级!您只需要替换php文件并保存配置即可。



备份时,您需要停止复制到从属服务器,并从数据库中同时执行元数据转储,同时创建存储中文件的快照。您需要成对存储。恢复应该从数据库转储和文件执行相同的时间。否则,可能会导致数据丢失,因为文件可能在存储中,但数据库中没有元数据。










All Articles