taskmanager/REGISTRY.md
Weapie 238dc11848
All checks were successful
check / verify (push) Successful in 3m8s
check / publish (push) Has been skipped
deploy: run administrator seed after access and upload migrations
2026-10-08 14:48:49 +02:00

10 KiB

Docker-Registry und Releases

Registry: git.jfritzsche.de, Eigentümer: jf.

Verwendung Image Tags eines Releases v1.0.0
Webanwendung git.jfritzsche.de/jf/taskmanager 1.0.0, latest
Datenmigration und Worker git.jfritzsche.de/jf/taskmanager-operations 1.0.0, latest

Beide Images sind erforderlich. Das kleine Web-Image enthält absichtlich keine Migrationswerkzeuge. Veröffentlichte Architektur: linux/amd64. ARM64 ist derzeit kein veröffentlichtes Ziel.

Automatische Veröffentlichung

.github/workflows/check.yml wird vom vorhandenen Gitea-Runner verarbeitet. Bei Push und Pull Request laufen Typecheck, Lint, Runtime-Audit, Sicherheits-/Integrationstests, die Migration vom Altschema, Produktionsbuild und Browsertests. Prüfjob und PostgreSQL-Services teilen ein Container-Netz; Verbindung über postgres:5432 statt Container-Loopback. Die Upgrade-Probe verwendet einen getrennten Datenbank-Service, ohne Docker-Zugriff aus dem Prüfjob.

Nur ein gepushter stabiler Git-Tag vX.Y.Z, der exakt zur Version in package.json passt, startet nach erfolgreichen Prüfungen den Veröffentlichungsjob. Branches, Pull Requests und Vorabversionen aktualisieren latest nicht. Bereits vorhandene Versions-Tags werden vor dem Build geprüft und nicht bewusst überschrieben. Veröffentlichungen nacheinander ausführen; alte Release-Tags nicht erneut veröffentlichen. latest bezeichnet das zuletzt erfolgreich veröffentlichte stabile Release, keinen Entwicklungsbranch.

Zuerst werden beide Versionsimages veröffentlicht. Erst wenn beide Pushes erfolgreich waren, werden die latest-Tags gesetzt. Registry-Updates zweier Images sind nicht atomar; bei einem Abbruch während dieses letzten Schritts können die Aliase kurz unterschiedliche Versionen bezeichnen. Produktion deshalb mit gleicher expliziter Versionsnummer für alle drei Dienste betreiben.

Registry-Anmeldung für Actions

Der Veröffentlichungsjob braucht einen Runner mit Docker/Buildx und HTTPS-Zugriff auf die Registry. Für Gitea 1.22.3 einen eigenen Access-Token des Benutzers jf mit write:package verwenden und im Repository unter Einstellungen → Actions → Secrets speichern:

  • REGISTRY_TOKEN: eingeschränkter Paket-Token, kein Administratorpasswort.
  • REGISTRY_USERNAME: optional, Standard ist der Repository-Eigentümer jf.

Ein bereits vorhandenes Secret bleibt unverändert. Der Workflow kann alternativ den eingebauten GITEA_TOKEN verwenden, sofern die eingesetzte Gitea-Version dessen Paketveröffentlichung unterstützt. permissions: packages: write allein rüstet diese Fähigkeit älterer Server nicht nach. Secrets nie in Git, Build-Argumenten oder Logs speichern. Das operations-Image erhält durch .dockerignore weder lokale .env noch Backups oder Uploads.

Gitea-Pakete gehören einem Benutzer/einer Organisation. Sichtbarkeit nach dem ersten Publish prüfen und Pakete bei Bedarf mit jf/taskmanager verknüpfen. Private Images erfordern beim Deployment einen separaten Token mit read:package. Dokumentation: Gitea Container Registry.

Gitea hinter einem HTTPS-Proxy

Gitea muss seine externe URL kennen. In app.ini:

[server]
ROOT_URL = https://git.jfritzsche.de/

Alternativ im Gitea-Container GITEA__server__ROOT_URL=https://git.jfritzsche.de/ setzen und Gitea neu starten. Der interne Listener darf weiterhin HTTP auf Port 3000 verwenden. HTTPS am Proxy allein korrigiert nicht automatisch die Registry-Token-URL.

Bei einer geänderten Compose-Umgebungsvariable reicht docker restart nicht: den Gitea-Dienst aus dessen eigenem Compose-Projekt neu erstellen (docker compose up -d --force-recreate DIENSTNAME). Das ist nicht der Taskmanager-Dienst. In der Gitea-Administrationsansicht die tatsächlich geladene Serverkonfiguration prüfen; eine Umgebungsvariable kann eine manuell bearbeitete app.ini beim Start wieder überschreiben. Keine Secrets oder vollständigen Umgebungsvariablen in Fehlerlogs veröffentlichen.

Neue Release-Workflows prüfen den HTTPS-Realm bereits vor Login und Image-Build mit node scripts/check-registry.mjs. Ein erneuter Lauf des alten Tags v1.0.0 verwendet weiterhin den Workflow dieses Tags; die Serverkorrektur ist auch dafür erforderlich.

Gitea 1.22.3: Proxy-Header haben Vorrang. Der Registry-Code verwendet GuessCurrentHostURL; ein vom Proxy angeliefertes X-Forwarded-Proto: http übersteuert eine korrekt gesetzte HTTPS-ROOT_URL. Danach berücksichtigt Gitea auch X-Forwarded-Protocol, X-Url-Scheme, Front-End-Https und X-Forwarded-Ssl. Siehe Registry-Code und URL-Ermittlung.

Wenn Umgebungsvariable und app.ini bereits HTTPS enthalten, nicht wiederholt ROOT_URL ändern. Im Gitea-Container ohne Proxy prüfen:

wget -S -O /dev/null http://127.0.0.1:3000/v2/ 2>&1

HTTP 401 ist dabei erwartbar. Liefert der direkte Aufruf einen HTTPS-Realm, der öffentliche Aufruf aber HTTP, liegt der Unterschied in der Proxy-Kette. Der vertrauenswürdige Proxy muss für externe HTTPS-Aufrufe das ursprüngliche Protokoll korrekt an Gitea weiterreichen. Bei Cloudflare auch SSL/TLS-Modus und die Verbindung zum Origin prüfen; Full (strict) benötigt ein gültiges Origin-Zertifikat. Keine ungeprüften Client-Header pauschal vertrauen. Die konkrete Korrektur hängt von Nginx, Traefik, Caddy bzw. Cloudflare Tunnel ab.

curl -sS -D - -o /dev/null https://git.jfritzsche.de/v2/

Ohne Anmeldung ist HTTP 401 normal. Der WWW-Authenticate-Header muss als Bearer-Realm https://git.jfritzsche.de/v2/token nennen. Ein HTTP-Realm kann den Upload mit authorization server did not include a token in the response scheitern lassen, obwohl docker login erfolgreich erschien. Keine HTTP-/TLS-Ausnahmen als Ersatz einrichten. Nach Korrektur den fehlgeschlagenen publish-Job erneut starten; dafür weder einen Release-Tag verschieben noch die Anwendung neu versionieren.

Upload scheitert mit HTTP 413 von Cloudflare

Enthält die Fehlerantwort beim Push einer Image-Schicht 413 Payload Too Large und den HTML-Absender cloudflare, blockiert Cloudflare den Upload. Eine funktionierende Registry-Anmeldung und ein korrekter HTTPS-Realm schließen diesen Fehler nicht aus. Cloudflare begrenzt einzelne Upload-Anfragen je Tarif, bei Free/Pro auf 100 MB; kleinere konfigurierte Grenzwerte sind ebenfalls möglich. Siehe Cloudflare: Error 413.

Für große Registry-Uploads kann git.jfritzsche.de als DNS only (graue Wolke) betrieben werden. Vor der Umstellung muss Dokploy/Traefik die Domain selbst auf Port 443 mit einem öffentlich vertrauenswürdigen Zertifikat, etwa von Let's Encrypt, bedienen. Ein reiner HTTP-Router auf web reicht nicht. Ein nur von Cloudflare vertrautes Origin-CA-Zertifikat reicht für direkte Docker-Clients ebenfalls nicht. Die direkte Erreichbarkeit des Origins muss zur Firewall-Konfiguration passen. Die Umstellung betrifft auch die Gitea-Weboberfläche auf derselben Domain.

Vorher von außerhalb des Servers mit dessen tatsächlicher öffentlicher IP prüfen:

curl --resolve git.jfritzsche.de:443:SERVER_IP -sS -D - -o /dev/null https://git.jfritzsche.de/v2/

Erwartet: erfolgreiche TLS-Prüfung ohne -k, HTTP 401 und HTTPS-Bearer-Realm. Erst danach den DNS-Proxy abschalten, die DNS-Auflösung abwarten und den fehlgeschlagenen Publish-Job erneut starten. Keine Änderung von ROOT_URL, Tokens oder Release-Tags erforderlich. Falls der Cloudflare-Schutz für die Domain erhalten bleiben soll, benötigt der Publisher stattdessen einen gezielt eingerichteten direkten HTTPS-Zugang zum Origin; auch der BuildKit-Container muss diesen verwenden. Ein erneuter Lauf über dieselbe begrenzte Verbindung behebt HTTP 413 nicht.

Neues Release erstellen

# Geprüften Release-Stand auschecken, keine uncommitteten Änderungen.
npm version 1.0.1 --no-git-tag-version
git add package.json package-lock.json
git commit -m "Release 1.0.1"
git push origin HEAD
git tag -a v1.0.1 -m "Release 1.0.1"
git push origin v1.0.1

Workflow-Erfolg und beide Registry-Images prüfen. Tags nicht verschieben. Bei teilweise veröffentlichtem Release die fehlenden Schritte gezielt reparieren oder eine neue Version verwenden; kein Force-Push eines anderen Images auf eine bestehende Version.

Deployment ohne lokalen Image-Build

Docker Compose mindestens 2.24.4 wegen !reset. Checkout/Compose-Dateien müssen zum Image-Release passen. Die Upgrade- und Backup-Anleitung gilt unverändert: bei vorhandenen Daten zuerst UPGRADE.md, insbesondere keine Neuinitialisierung.

docker login git.jfritzsche.de --username jf
# Passwort-Prompt: Token mit read:package, nicht im Befehl ausschreiben.
export TASKMANAGER_VERSION=1.0.0
docker compose -p taskmanager -f docker-compose.yml -f docker-compose.registry.yml pull
docker compose -p taskmanager -f docker-compose.yml -f docker-compose.registry.yml up -d --wait db
docker compose -p taskmanager -f docker-compose.yml -f docker-compose.registry.yml run --rm migrate
# Bei Neuinstallation vorher Bootstrap-Werte gemäß README setzen;
# der Migrationsdienst führt den Seed mit aus.
docker compose -p taskmanager -f docker-compose.yml -f docker-compose.registry.yml up -d --wait app worker

docker-compose.registry.yml ersetzt alle Build-Definitionen durch Registry-Images. Volumes, private Anhänge, Healthchecks, Sicherheitsoptionen und Migrationsreihenfolge aus dem Basis-Compose bleiben erhalten. Den tatsächlichen Projektnamen verwenden; ein anderer Name kann leere neue Volumes erzeugen. Auch bei Backup-/Restore-Befehlen dieselben Compose-Dateien und denselben Projektnamen verwenden.

Zum Nachschlagen verfügbar, für geplante Upgrades aber die konkrete Version bevorzugen:

docker pull git.jfritzsche.de/jf/taskmanager:latest
docker pull git.jfritzsche.de/jf/taskmanager-operations:latest
docker buildx imagetools inspect git.jfritzsche.de/jf/taskmanager:1.0.0
docker buildx imagetools inspect git.jfritzsche.de/jf/taskmanager-operations:1.0.0

Kein automatischer Austausch der laufenden Produktivinstallation: Veröffentlichung und produktives Upgrade sind getrennte Schritte. Ein älteres Image allein macht Datenbankmigrationen nicht rückgängig.