Paperless NGX im Loadbalancing
Einleitung
Paperless-NGX ist an und für sich schon eine recht performante Lösung. Wenn aber viele Anwenderinnen und Anwender gleichzeitig damit arbeiten sollen, dann kann der Bedarf nach Skalierung der Maschinenressourcen entstehen. Ich wollte es wissen und habe mir eine Lösung überlegt, die aus drei Paperless-NGX-Webservern mit jeweils den Komponenten Redis, Gotenberg und Tika sowie traefik als Loadbalancer besteht. Damit diese drei Server eine gemeinsame Datenbasis nutzen, liegen die Dokumente auf einem gemeinsam genutzten NFS-Share und die Daten in einer abgesetzten Datenbank. Das System besteht also aus sechs Servern:
- dem Loadbalancer
- dem Datenbankserver
- dem NFS-Server
- und den drei Webservern
Loadbalancer und NFS-Server können aber auch auf einer Maschine laufen. Das Ganze habe ich auch in einem Youtube-Video vorgestellt.
Der Datenbankserver
Paperless-NGX kann mit Postgresql und MariaDB betrieben werden. Empfohlen wird Postgresql, also habe ich hier PostgreSQL verwendet. Ich nutze Ubuntu Linux Server in der Version 24.04 LTS. Mit anderen Linux-Servern z.B. unter Debian sollte das genauso funktionieren. Ich melde mich auf der Konsole des Servers an, um die Befehle auf der Kommandozeile abzusetzen. Zunächst stelle ich sicher, dass das System auf dem aktuellen Stand ist mit
sudo apt update && sudo apt upgrade -y
sudo apt install postgresql
listen_addresses = 'localhost'
listen_addresses = ''
Dann muss ich noch in der Datei "/etc/postgresql/{PostgreSQL-Version}/main/pg_hba.conf" die Zeile
hostssl paperless paperless 192.168.0.0/24 scram-sha-256
sudo systemctl restart postgresql
sudo -u postgres psql
CREATE USER paperless with password '123456';
CREATE DATABASE paperless;
\c paperless
GRANT ALL PRIVILEGES ON DATABASE paperless TO paperless;
EXIT;
Danach kann ich mich von dieser Konsole und auch von dem Server wieder abmelden.
Der Loadbalancer
Als Loadbalancer wird trafik unter Docker eingesetzt. Auf dem Server, auf dem traefik laufen soll, habe ich dazu Docker und Docker Compose entsprechend meiner Anleitung installiert (s. auch hier: Tutorial-Beitrag zu Docker). Ich melde mich auf der Konsole des Servers an. Dann erstelle ich zunächst ein Verzeichnis traefik im Homeshare und wechsele in selbiges
mkdir traefik && cd traefik
nano riles.yml
http:
routers:
paperless:
rule: "Host(`paperless.local`)"
service: paperless
entryPoints:
- "web"
services:
paperless:
loadBalancer:
sticky:
Cookie:
name: paperless_sticky
servers:
- url: "http://192.168.0.113:8000"
- url: "http://192.168.0.59:8000"
- url: "http://192.168.0.149:8000"
healthCheck:
path: /api/health/
interval: 10s
timeout: 5s
scheme: http
port: 8000
nano docker-compose.yml
services:
traefik:
image: traefik:v2.10
container_name: traefik
command:
- --api.insecure=true
- --providers.docker=false # Docker Provider nicht noetig,
# da Services auf anderen Servern laufen
- --providers.file=true
- --providers.file.filename=/etc/traefik/rules.yml
- --entrypoints.web.address=:80
ports:
- "80:80"
- "8080:8080" # Dashboard (optional)
volumes:
- ./rules.yml:/etc/traefik/rules.yml
networks:
- traefik_net
networks:
traefik_net:
driver: bridge
Auch die Datei docker-compose.yml ist eigentlich recht überschaubar. Zunächst wird der Service traefik definiert. Hier wähle ich Version 2.10, weil ich damit gute Erfahrungen gemacht habe. Dann erlaube ich Zugriffe ohne Zertifikate. Für den Zugriff auf die Webserver brauche ich hier nicht den lokalen Docker Provider, weil die Webserver ja auf separaten Maschinen laufen. Der Rest ist selbsterklärend. Das kann ich dann schon einmal mit
docker compose up -d
docker compose up -d
docker logs traefik
Der NFS-Server und die Freigaben
Damit das Gesamtsystem später auch eine einheitliche Dokumentenbasis verwendet, müssen die Verzeichnisse "data" und "media" auf einem gemeinsam genutztem Share liegen. In einer reinen Linux-Umgebung bietet sich hierfür NFS an. Ich nehme also einen frischen Linux-Server mit ausreichend Massenspeicher und melde mich an der Konsole dieses Servers an. Hierauf installiere ich nfs-server mit
sudo apt install nfs-server
sudo mkdir /mnt/storage
sudo mkdir /mnt/storage/data
sudo mkdir /mnt/storage/media
sudo chown -R 1000:1000 /mnt/storage
sudo nano /etc/exports
/mnt/storage/data 192.168.0.59(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
/mnt/storage/media 192.168.0.59(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
/mnt/storage/data 192.168.0.113(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
/mnt/storage/media 192.168.0.113(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
/mnt/storage/data 192.168.0.149(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
/mnt/storage/media 192.168.0.149(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
sudo exportfs -ra
Die Webserver
Die Bezeichnung Webserver trifft es nicht ganz, denn auf diesen Servern laufen neben den Webservern noch jeweils die Dienste Gotenberg und Tika für die Verarbeitung der Dokumente und die In-Memory-Datenbank redis, die die DB-Zugriffe auf die Postgresql-Datenbank puffert. Ich habe also drei Linux-Server, auf denen bereits Docker und Docker-Compose installiert ist (s.o.). Hier muss ich zunächst die NFS-Freigaben mounten. Dazu muss ich zuerst den NFS-Client mit
sudo apt install nfs-common
sudo nano /etc/fstab
192.168.0.140:/mnt/storage/data /home/andreas/paperless-ngx/data nfs rw 0 0
192.168.0.140:/mnt/storage/media /home/andreas/paperless-ngx/media nfs rw 0 0
mkdir paperless-ngx
cd paperless-nxg
mkdir data
mkdir media
sudo systemctl daemon-reload
sudo mount -a
df -h
192.168.0.140:/mnt/storage/data XG XM XG X% /home/andreas/paperless-ngx/data
192.168.0.140:/mnt/storage/media XG XM XG X% /home/andreas/paperless-ngx/media
sudo mount -anano docker-compose.yml
services:
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on:
- broker
- gotenberg
- tika
ports:
- "8000:8000"
volumes:
- ./data:/usr/src/paperless/data
- ./media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
environment:
PAPERLESS_DBHOST: 192.168.0.139
PAPERLESS_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: 123456
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
broker:
image: docker.io/library/redis:7
restart: unless-stopped
volumes:
- redisdata:/data
gotenberg:
image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even javascript.
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
tika:
image: docker.io/apache/tika:latest
restart: unless-stopped
volumes:
redisdata:
Dann werden im Environment-Teil die Services Redis, Gotenberg und Tika verbunden, die im darauf folgenden Teil definiert werden. Das ist schon alles. Ich starte de Container mit
docker compose up -d
docker logs paperless-ngx-webserver-1
sudo nano /etc/hosts
192.168.0.139 paperless.local
Ergebnis/Fazit
Paperless-NGX ist nun mit verteilten Kapazitäten verfügbar. Die Last wird auf drei Webserver und einen Datenbankserver aufgeteilt. Die Zahl der Webserver lässt sich steigern. Dabei wendet der Loadbalancer ein sogenanntes Round-Robin-Prinzip an, das heißt, bei jeder Anmeldung wird der Reihe nach der jeweils nächste Server gewählt. Dass das funktioniert, kann man ausprobieren, indem man sich zum Beispiel mit zwei Browsern anmeldet, jeweils Dokumente hochlädt und in den Protokollen der Webserver nachsieht, wer das gerade verarbeitet hat.
Die hier gezeigte Lösung ist geeignet, Umgebungen für eine große Anzahl an Anwenderinnen und Anwender performant bereit zu stellen. Eine Umgebung mit wenigen Anwenderinnen und Anwendern profitiert davon nicht. Das ist angesichts der guten Performancewerte von Paperless-NGX auch nicht notwendig. Die Lösung stellt auch einen einfachen Failover für den Ausfall einzelner Webserver dar.