Zwölf Builds

Eine private Nextcloud auf NVMe mit Objektspeicher dahinter

Nextcloud auf einer Ryzen-Instanz mit Postgres und Redis auf lokaler NVMe, jede Benutzerdatei lebt in einem Bucket auf einem Storage-Node, verbunden durch einen privaten Tunnel.

Was damit gebaut wird

Zwei Maschinen. Eine Ryzen-Instanz betreibt Nextcloud, PHP-FPM, Postgres und Redis, alles auf lokalen Gen4-NVMe-Laufwerken, wo die latenzempfindliche Arbeit stattfindet. Der Storage-Knoten daneben speichert den eigentlichen Dateiinhalt in einem Objekt-Bucket, wo die billigen Terabyte leben. Dazwischen liegt ein WireGuard-Tunnel, sodass nichts in diesem Arrangement irgendetwas anderem ausgesetzt ist.

Die Aufteilung ist wichtig, weil Nextcloud mit einem primären Objekt-Store das Schreiben von Benutzerdaten auf die lokale Festplatte vollständig einstellt. Ihre NVMe enthält Datenbank und Code; der Bucket enthält jedes Byte, das jemand hochlädt. Der Ausbau von zweihundert Gigabyte auf zwanzig Terabyte wird dann zu einem Storage-Knoten-Upgrade und nicht zu einer Migration.

Bevor Sie beginnen

  • Ein R-8 für die Anwendung und ein S-50 für den Bucket, idealerweise in derselben Stadt, damit die Tunnel-Roundtrip-Zeit unter einer Millisekunde liegt.
  • Ein Tunnel zwischen ihnen. Die WireGuard-Anleitung baut genau das auf; verwenden Sie 10.9.0.1 für die App und 10.9.0.2 für den Storage-Knoten.
  • Ein Hostname, der auf die App-Instanz zeigt, cloud.example.com unten.

1. Objektspeicher auf dem Storage-Knoten

MinIO ist eine einzelne Binärdatei und benötigt nichts weiter. Holen Sie es, verifizieren Sie es, installieren Sie es:

cd /usr/local/bin
wget https://dl.min.io/server/minio/release/linux-amd64/minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio.sha256sum
sha256sum -c minio.sha256sum
chmod +x minio
useradd -r -s /sbin/nologin minio
install -d -o minio -g minio /srv/objects

Der Bulk-Tier auf einem Storage-Knoten ist separat von der NVMe gemountet. Legen Sie den Bucket auf den Bulk-Tier und lassen Sie die NVMe für den Write-Cache, wofür sie da ist. Dann die Unit, unter /etc/systemd/system/minio.service:

[Unit]
Description=MinIO
After=network-online.target [email protected]
Wants=network-online.target

[Service]
User=minio
Group=minio
Environment=MINIO_ROOT_USER=nextcloud
Environment=MINIO_ROOT_PASSWORD=<forty random characters>
ExecStart=/usr/local/bin/minio server /srv/objects --address 10.9.0.2:9000
Restart=always
LimitNOFILE=65535
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/srv/objects

[Install]
WantedBy=multi-user.target

Das Binden an die Tunnel-Adresse statt an alles ist die wichtige Zeile. Der Objekt-Store hat nichts am öffentlichen Interface zu suchen, und so kann ein Firewall-Fehler ihn nicht exponieren.

systemctl daemon-reload && systemctl enable --now minio
ss -ltn | grep 9000

2. Anwendungshost: Pakete

Auf der Ryzen-Instanz:

apt update && apt install -y nginx postgresql redis-server unzip \
  php8.4-fpm php8.4-pgsql php8.4-gd php8.4-curl php8.4-zip php8.4-xml \
  php8.4-mbstring php8.4-intl php8.4-bcmath php8.4-gmp php8.4-imagick \
  php8.4-redis php8.4-apcu

3. Datenbank

sudo -u postgres psql -c "CREATE USER nextcloud WITH PASSWORD '<a long password>';"
sudo -u postgres psql -c "CREATE DATABASE nextcloud OWNER nextcloud TEMPLATE template0 ENCODING UTF8;"

Die Standard-Postgres-Einstellungen sind in dieser Größenordnung ausreichend. Falls dieses Nextcloud ein paar hundert Personen bedient, gilt die Postgres-Tuning-Anleitung, und die Zahlen dort sind eine Stunde wert.

4. Nextcloud, vor der Installation konfiguriert

cd /var/www
wget https://download.nextcloud.com/server/releases/latest.zip
unzip -q latest.zip && rm latest.zip
chown -R www-data:www-data /var/www/nextcloud

Jetzt der Teil, der vor dem Installationsprogramm passieren muss, denn ein primärer Objekt-Store kann nicht zu einer bestehenden Instanz hinzugefügt werden, ohne jede Datei von Hand zu verschieben. Erstellen Sie /var/www/nextcloud/config/config.php:

<?php
$CONFIG = array(
  "objectstore" => array(
    "class" => "\\OC\\Files\\ObjectStore\\S3",
    "arguments" => array(
      "bucket" => "nextcloud",
      "autocreate" => true,
      "key" => "nextcloud",
      "secret" => "<the same forty characters>",
      "hostname" => "10.9.0.2",
      "port" => 9000,
      "use_ssl" => false,
      "use_path_style" => true,
      "region" => "local",
    ),
  ),
);

use_ssl ist absichtlich falsch. Der Datenverkehr läuft durch einen WireGuard-Tunnel, der bereits authentifiziert und verschlüsselt ist; TLS ein zweites Mal auf dem Storage-Knoten zu terminieren, bringt nichts außer CPU-Zyklen und einem Zertifikat, das man vergessen kann zu erneuern. Sollten die beiden Maschinen jemals in verschiedenen Städten stehen, schalten Sie es ein.

chown www-data:www-data /var/www/nextcloud/config/config.php
chmod 640 /var/www/nextcloud/config/config.php
cd /var/www/nextcloud
sudo -u www-data php occ maintenance:install --database pgsql \
  --database-name nextcloud --database-user nextcloud \
  --database-pass "<a long password>" \
  --admin-user admin --admin-pass "<another long password>"

5. Cache, Cron und die letzten Einstellungen

sudo -u www-data php occ config:system:set memcache.local --value "\\OC\\Memcache\\APCu"
sudo -u www-data php occ config:system:set memcache.distributed --value "\\OC\\Memcache\\Redis"
sudo -u www-data php occ config:system:set memcache.locking --value "\\OC\\Memcache\\Redis"
sudo -u www-data php occ config:system:set redis host --value /run/redis/redis-server.sock
sudo -u www-data php occ config:system:set redis port --value 0
sudo -u www-data php occ config:system:set trusted_domains 0 --value cloud.example.com
sudo -u www-data php occ config:system:set overwrite.cli.url --value https://cloud.example.com
sudo -u www-data php occ background:cron

Redis über einen Unix-Socket statt TCP spart einen Syscall-Roundtrip bei jedem Sperrvorgang ein, und Nextcloud nimmt viele Sperren. Fügen Sie www-data zur Redis-Gruppe hinzu, damit es den Socket erreichen kann, und verbinden Sie den Hintergrundjob mit einem Timer:

usermod -aG redis www-data
systemctl restart php8.4-fpm redis-server
echo "*/5 * * * * php -f /var/www/nextcloud/cron.php" | crontab -u www-data -

Die PHP-FPM-Standardeinstellungen gehen von einer gemeinsam genutzten Maschine aus. Ihre ist nicht gemeinsam genutzt, also setzen Sie in /etc/php/8.4/fpm/pool.d/www.conf pm = dynamic, pm.max_children = 60, pm.start_servers = 12, pm.min_spare_servers = 8 und pm.max_spare_servers = 20 und erhöhen Sie memory_limit auf 1G in /etc/php/8.4/fpm/php.ini. Uploads wollen upload_max_filesize und post_max_size auf 16G, es sei denn, Sie erklären gern, warum ein Video bei neunzig Prozent fehlschlug.

Verifizieren Sie es

Der Sinn dieser Konstruktion ist, dass Benutzerdateien nicht auf dem Anwendungsserver liegen. Beweisen Sie das mit drei Befehlen.

sudo -u www-data php occ status
du -sh /var/www/nextcloud/data

Der Status sollte installiert und nicht im Wartungsmodus melden. Das Datenverzeichnis sollte ein paar Megabyte groß sein und für immer so bleiben; es enthält Protokolle und sonst nichts, sobald der Objekt-Store primär ist.

Laden Sie jetzt etwas hoch und beobachten Sie, wo es landet:

head -c 50M /dev/urandom > /tmp/test.bin
curl -u admin -T /tmp/test.bin https://cloud.example.com/remote.php/dav/files/admin/test.bin
du -sh /var/www/nextcloud/data

Der Upload gelingt, und das Datenverzeichnis ist nicht um fünfzig Megabyte gewachsen. Auf dem Storage-Knoten ist das Objekt direkt sichtbar:

ls -R /srv/objects/nextcloud | tail -5
du -sh /srv/objects/nextcloud

Objekte heißen urn:oid: gefolgt von der Datei-ID, weshalb der Bucket keine durchsuchbare Kopie der Benutzerordner ist und weshalb Sie Datenbank und Bucket zusammen sichern oder gar nicht erst sichern.

Danach

Nehmen Sie das Backup-Add-on außerhalb des Knotens für den Postgres-Dump und den Bucket oder führen Sie Ihr eigenes zu einem dritten Standort aus. Ein Bucket ohne seine Datenbank ist ein Stapel nummerierter Blobs, und eine Datenbank ohne ihren Bucket ist ein Index von Dateien, die nicht mehr existieren. Wenn Sie eines verlieren, verlieren Sie beides.

Bereit, wenn Sie es sind

Wählen Sie eine Stadt. Wählen Sie eine Größe. Bezahlen Sie in Coins.

Keine Formulare darüber, wer Sie sind, kein Warten auf einen Menschen, der Sie genehmigt, kein Anruf zur Verifizierung. Die Rechnung wird beglichen, und die Zugangsdaten landen in Ihrem Posteingang.