Dockerkonsolidierung
Im Laufe der Zeit haben sich in meinem Homelab ein paar Anwendungen, die auf Docker basieren, eingefunden und sie tummeln sich auf mehreren verschiedenen Servern herum. Teilweise werden dabei Container auch redundant betrieben. So habe bzw. hatte ich für verschiedene Paperless-NGX-Instanzen und für Docmost mehrere Redis, Tika und Postgresql-Container in Betrieb. Es ist also Zeit, aufzuräumen.
Vorbereitungen
Ich wähle einen virtuellen Server, der ausreichend Kapazitäten hat. Er bekommt von mir 8 GB RAM, 4 CPU-Kerne und 200 GB Plattenplatz. Der RAM ist hier noch am ehesten der limitierende Faktor. Ich installiere darauf Docker und Docker-Compose (s. hier: docker). Dann erstelle ich das Netzwerk mit
docker create network master1
sudo mkdir /srv/docker
sudo chown -R 1000:1000 /srv/docker
mkdir /srv/docker/apps
mkdir /srv/docker/data
Umzug der Dienste Redis, Gotenberg und Tika
Für diese Dienste lege ich eine eigene Compose-Datei an. Ich erstelle zunächst einen Ordner in dem Verzeichnis /srv/docker/apps. Ich nenne das App-Verzeichnis dienste und wechsele in selbiges
mkdir /srv/docker/apps/dienste && cd /srv/docker/apps/dienste
services:
broker:
container_name: broker
image: docker.io/library/redis:latest
restart: unless-stopped
networks:
- master1
volumes:
- redisdata:/data
gotenberg:
image: docker.io/gotenberg/gotenberg:latest
container_name: gotenberg
networks:
- master1
restart: unless-stopped
# Beim Konvertieren von .eml-Files mit Gotenberg soll
# externer content wie z.B: tracking pixels oder javascript
# nicht mit konvertiert werden
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
tika:
image: docker.io/apache/tika:latest
container_name: tika
networks:
- master1
restart: unless-stopped
volumes:
redisdata:
networks:
master1:
external: true
Mit
docker compose up -d
Umzug der Docker-Anwendungen
Dockhand
Dockhand besteht nur aus einem Container und einem persistenten Volume. Ich erstelle zunächst wieder ein Verzeichnis im App-Ordner, das ich diesmal dockhand nenne und darin erstelle ich eine Datei namens docker-compose.yml mit folgendem Inhalt:
services:
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /srv/docker/data/dockhand/dockhand_data:/app/data
networks:
- master1
volumes:
dockhand_data:
networks:
master1:
external: true
Mit
mkdir /srv/docker/data/dockhand
docker compose up -d
Caddy
Damit ich die Dienste in den Containern aufrufen kann, nutze ich den Reverse-Proxy Caddy. Installation und Nutzung von Caddy habe ich hier beschrieben: Caddy. Ich erstelle wieder ein passendes Verzeichnis und wechsele in selbiges.
mkdir /srv/docker/apps/caddy
mkdir /srv/docker/data/caddy
cd /srv/docker/apps/caddy
services:
caddy:
build:
context: .
dockerfile_inline: |
FROM caddy:builder AS builder
# Caddy mit de Plugins bauen
RUN xcaddy build \
--with github.com/caddy-dns/all-inkl
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
container_name: caddy
restart: unless-stopped
dns:
- 1.1.1.1
- 8.8.8.8
ports:
- '80:80
- '443:443'
- '443:443/udp' # Wichtig für HTTP/3 Support
volumes:
- /srv/docker/data/caddy/Caddyfile:/etc/caddy/Caddyfile
- /srv/docker/data/caddy/data:/data
- /srv/docker/data/caddy//config:/config
environment:
- KAS_USERNAME=***********
- KAS_PASSWORD=******************
networks:
- master1
networks:
master1:
external: true
Dann erstelle ich das Caddyfile namens Caddyfile im Data-Ordner mit folgendem Inhalt:
{
# Optionale globale Einstellungen
email mail@sechzig-veedel.de
}
# Block fuer die Wildcard-Domain
web-cooperation.de, *.web-cooperation.de {
tls {
dns allinkl {
kas_username {$KAS_USERNAME}
kas_password {$KAS_PASSWORD}
}
propagation_delay 120s
propagation_timeout -1
}
@dockhand host dockhand.web-cooperation.de
handle @dockhand {
reverse_proxy dockhand:3000
}
@docmost host docmost.web-cooperation.de
handle @docmost {
reverse_proxy docmost:3000
}
@duplicati host duplicati.web-cooperation.de
handle @duplicati {
reverse_proxy duplicati:8200
}
@beszel host beszel.web-cooperation.de
handle @beszel {
reverse_proxy beszel:8090
}
@paperless host paperless.web-cooperation.de
handle @paperless {
reverse_proxy webserver:8000
}
@paper2 host paper2.web-cooperation.de
handle @paper2 {
reverse_proxy paper2:8000
}
@n8n host n8n.web-cooperation.de
handle @n8n {
reverse_proxy n8n:5678
}
@uptime host uptime.web-cooperation.de
handle @uptime {
reverse_proxy uptime-kuma:3001
}
@authentic host authentic.web-cooperation.de
handle @authentic {
reverse_proxy server:9000
}
@dms host dms.web-cooperation.de
handle @dms {
reverse_proxy dms:8000
}
# Fallback innerhalb dieses Blocks
handle {
abort
}
Dann starte ich den Container mit
docker compose up -d
docker exec -w /etc/caddy caddy caddy fmt --overwrite
docker exec -w /etc/caddy caddy caddy reload
Docmost
Für die Migration von Docmost muss ich ein persistentes Volume übertragen und vor allem die Migration von der Postgresql-DB, die bisher als eigener Docker-Container in der Compose-Datei definiert ist, auf einen abgesetzten Datenbankserver durchführen. Als erstes erstelle ich wieder ein entsprechendes Unterverzeichnis, welches ich diesmal docmost nenne.
Übertragung der Dateien
Dateien, wie z.B. Grafiken speichert Docmost intern im Verzeichnis /app/data/storage. Bei Einrichtung von Docmost mittels Docker Compose habe ich dafür ein persistentes Volume im Verzeichnis von Docmost angelegt (s. Docmost). Dieses Verzeichnis übertrage ich per rsync auf den neuen Server. Wenn der neue Server entsprechend meinen Standard abgesichert ist (s. Server absichern), dann muss ich zunächst den öffentlichen SSH-Schlüssel von dem Quellserver auf den Zielserver übertragen. Dann kann ich mit
rsync -avE -e ssh storage [username@ip-des-zielservers]:/home/[username]/docmost
Datenbank migrieren
Zunächst muss ich einen Datenbankdump erzeugen. Dazu brauche ich ein persistentes Volume, in dem ich den Export dann außerhalb des Dockercontainers auch finde. Dafür nutze ich das Verzeichnis db_data, das in der Datei docker-compose.yml des laufenden Containers unter /var/lib/postgresql/data gemountet ist. Für die Erstellung des Dump nutze ich die Shell des laufenden alten Docmost-Containers. Das kann ich komfortabel mit Dockhand über die Web-GUI machen. Manuell geht das aber auch von der Konsole aus mit
docker exec -t [containername] pg_dump -U docmost docmost > /mnt/export/docmost.sql
rsync -avE -e ssh db_data/docmost.sql [username@ip-des-zielservers]:/home/
psql -h pg1.lan -U postgres
CREATE USER docmost WITH PASSWORD 'SEHR_SICHERES_PASSWORT';
CREATE DATABASE docmost OWNER docmost;
GRANT ALL PRIVILEGES ON DATABASE docmost TO docmost;
\q
pg_restore -h pg1.lan -U docmost -d docmost docmost.sql
Angepasste Compose-Datei
Die Datei docker-compose.yml auf dem Altsystem enthält die Definition des DB-Containers. Diese muss durch die Zugriffs-Parameter für die abgesetzte Datenbank ersetzt werden. Außerdem muss der Redis-Container gelöscht werden und der docmost-Container entsprechend angepasst werden. Die neue docker-compose.yml ist damit überschaubarer. Zunächst erstelle ich wieder die beiden Ordner unter /srv/docker
mkdir /srv/docker/apps/docmost
mkdir /srv/docker/data/docmost
services:
docmost:
image: docmost/docmost:latest
container_name: docmost
environment:
APP_URL: 'https://docmost.web-cooperation.de'
APP_SECRET: '*****************************************************'
DATABASE_URL: 'postgresql://docmost:[PASSWORT]@pg1.lan:5432/docmost?schema=public'
REDIS_URL: 'redis://broker:6379'
MAIL_DRIVER: 'smtp'
SMTP_HOST: 'name.smtpserver.com'
SMTP_PORT: '587'
SMTP_USERNAME: 'name'
SMTP_PASSWORD: '****'
MAIL_FROM_ADDRESS: 'docmost@web-cooperation.de'
MAIL_FROM_NAME: 'Docmost'
restart: unless-stopped
volumes:
- /srv/docker/data/docmost/storage:/app/data/storage
networks:
- master1
volumes:
docmost:
networks:
master1:
external: true
docker compose up -d --remove-orphans
Paperless NGX
Die Migration von Paperless NGX ist nun kein Hexenwerk mahr. Die Verzeichnisse data und media werden zunächst per rsync übertragen. Dann erstelle ich eine angepasste Version der Datei docker-compose.env, bei der ich den URL und die Adresse beim Parameter PAPERLESS_ALLOWED_HOSTS anpasse. In der docker-Compose.yml muss ich die Container für Redis, Gotenberg und Tika entfernen und durch die Nennung der entsprechenden bereits laufenden Container ersetzen. Sie sieht bei mir so aus:
services:
dms:
container_name: dms
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
volumes:
- /src/docker/data/paperless-ngx/data:/usr/src/paperless/data
- /srv/docker/data/paperless-ngx/media:/usr/src/paperless/media
- /srv/docker/data/paperless-ngx/export:/usr/src/paperless/export
- /srv/docker/data/paperless-nxg/consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_DBHOST: pg1.lan
PAPERLESS_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: *********
PAPERLESS_DBPORT: 5432
networks:
- master1
networks:
master1:
external: true
docker compose up -d --remove-orphans
Fazit
Auf diese Weise ziehe ich alle meine Dockeranwendungen auf einem größeren Server zusammen. Aktuell sind das bei mir 15 Container, wobei ich unnötige Redundanzen vermeide. Die Auslastung des Servers liegt bei 50 % des RAM während sich die CPU-Kerne langweilen.