feat: launch desfoto.de as a standalone photography site and retire the old redirect
This commit is contained in:
112
docs/deployment.md
Normal file
112
docs/deployment.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Deployment — desfoto.de
|
||||
|
||||
## Zielumgebung
|
||||
|
||||
| Baustein | Wert |
|
||||
| --- | --- |
|
||||
| Produktionshost | `prod-main` = `opencode-prod@87.106.24.138` (NOPASSWD sudo) |
|
||||
| Live-Verzeichnis | `/home/denny/stacks/desfoto/` (Owner `denny:denny`) |
|
||||
| Release-Snapshots | `/home/denny/stacks/desfoto-releases/<stamp>-<sha>/` |
|
||||
| Compose-Dateien | `compose.yml` + `compose.vps.yml`, Projektname `desfoto` |
|
||||
| Container | `desfoto-web-1` (`nginx:1.28-alpine`), gebunden an `127.0.0.1:18430` |
|
||||
| Netzwerk | externes Docker-Netzwerk `web` (Traefik-Docker-Provider) |
|
||||
| Reverse Proxy | Traefik v3 im Projekt `stack` (`/srv/stack/docker-compose.yml`) |
|
||||
| Entrypoints | `http` und `https`, ACME-Resolver `le`, HTTP-01-Challenge |
|
||||
| Zertifikate | `/srv/traefik/acme.json` |
|
||||
| Domains | `desfoto.de` (kanonisch) und `www.desfoto.de` → 301 auf Apex |
|
||||
|
||||
Der Proxy wird **niemals** neu gestartet oder neu erzeugt. Die Anbindung erfolgt
|
||||
ausschließlich über Labels in `compose.vps.yml`.
|
||||
|
||||
## Ablauf einer Veröffentlichung
|
||||
|
||||
```bash
|
||||
/home/king/bin/oc-release /home/king/projects/desfoto \
|
||||
"feat: ..." <geänderte Dateien...>
|
||||
```
|
||||
|
||||
`oc-release` committet und pusht die genannten Dateien und ruft danach die
|
||||
getrackten Hooks auf:
|
||||
|
||||
1. `.ocauto/deploy <sha>`
|
||||
- prüft, dass `HEAD` dem Release entspricht und `site/`, `nginx.conf`,
|
||||
`compose.yml`, `compose.vps.yml` keine uncommitteten Änderungen haben;
|
||||
- packt diese Pfade in ein Archiv und lädt es nach `prod-main:/tmp`;
|
||||
- legt auf dem Server zuerst einen Snapshot der aktuell laufenden Version an
|
||||
(`/home/denny/stacks/desfoto-releases/<stamp>-<sha>/`, zusätzlich als
|
||||
`previous` verlinkt);
|
||||
- entpackt die neue Version nach `/home/denny/stacks/desfoto/`, setzt den
|
||||
Owner auf `denny:denny` und schreibt die Release-Kennung nach `RELEASE`;
|
||||
- `docker compose -f compose.yml -f compose.vps.yml up -d --remove-orphans`
|
||||
und wartet auf `healthy`;
|
||||
- entfernt einmalig die veralteten `desfoto.de`-Weiterleitungs-Labels aus
|
||||
`/srv/stack/docker-compose.yml`: Es werden ausschließlich Traefik-Label-Zeilen
|
||||
mit `desfoto`-Bezug sowie der zugehörige Kommentar entfernt, das Ergebnis wird
|
||||
zuerst als Compose-Projekt validiert (`config --quiet`) und danach per atomarem
|
||||
`mv` an die Stelle der Live-Datei gesetzt. Eine Vorher-Fassung liegt im
|
||||
Snapshot (`docker-compose.yml.stack-backup`) und daneben als
|
||||
`docker-compose.yml.bak.<stamp>`. Danach wird ausschließlich der
|
||||
`landing`-Container mit `--no-deps` neu erzeugt, damit `dennyapp.de` und
|
||||
`dennyschulz.de` unverändert weiterlaufen. Bleibt eine `desfoto`-Referenz
|
||||
übrig (z. B. ein eigener Service-Block), wird sie als WARNUNG ausgegeben.
|
||||
|
||||
2. `.ocauto/verify <sha>`
|
||||
- vergleicht den Dateibaum unter `/home/denny/stacks/desfoto/site` mit dem
|
||||
committeten `site/` (SHA-256 über alle Dateien außer den datierten
|
||||
`sitemap.xml`/`security.txt`);
|
||||
- prüft alle 13 Routen, die 404-Seite, `robots.txt`, `sitemap.xml`,
|
||||
`.well-known/security.txt`, Bilder und Schriften;
|
||||
- prüft die Weiterleitungen `http → https` und `www → Apex`;
|
||||
- prüft die Sicherheits-Header, dass kein `Set-Cookie` gesetzt wird und dass
|
||||
das Zertifikat noch mindestens sieben Tage gültig ist;
|
||||
- prüft, dass `www.dennyschulz.de` und `dennyapp.de` weiterhin erreichbar sind.
|
||||
|
||||
## Routing
|
||||
|
||||
`desfoto.de` wird vor dem Aufräumen bereits vom neuen Container bedient, weil die
|
||||
Routers in `compose.vps.yml` eine explizite Traefik-Priorität `200` tragen und die
|
||||
alten Weiterleitungsrouters keine Priorität setzen (damit gilt dort die
|
||||
Regel-Länge). Der Container wird also zuerst gesund geprüft, und erst danach werden
|
||||
die alten Labels entfernt — der Übergang hat damit kein Fenster ohne Antwort.
|
||||
|
||||
`www.desfoto.de` wird über die Middleware `desfoto-canonical` dauerhaft auf
|
||||
`https://desfoto.de/...` umgeschrieben. Für `http` greift die globale
|
||||
Entrypoint-Weiterleitung von Traefik auf `https`; die ACME-HTTP-01-Challenge
|
||||
beantwortet Traefik selbst, bevor diese Weiterleitung greift.
|
||||
|
||||
## Rollback
|
||||
|
||||
```bash
|
||||
ROLLBACK_TO=/home/denny/stacks/desfoto-releases/<stamp>-<sha> \
|
||||
/home/king/projects/desfoto/.ocauto/deploy rollback
|
||||
```
|
||||
|
||||
Der Befehl synchronisiert `site/`, `nginx.conf` und beide Compose-Dateien aus dem
|
||||
Snapshot zurück nach `/home/denny/stacks/desfoto/` und startet den Stack neu. Die
|
||||
frühere Version bleibt so lange verfügbar, bis sie bewusst gelöscht wird.
|
||||
|
||||
Jeder Release legt den Snapshot an, auch der allererste. Enthält der Snapshot noch
|
||||
kein `site/` (weil vorher nichts ausgeliefert wurde), entfernt der Rollback den
|
||||
desfoto-Stack wieder und stellt den Zustand vor der Veröffentlichung her.
|
||||
|
||||
Wurde `/srv/stack/docker-compose.yml` verändert, liegt die Vorher-Fassung sowohl im
|
||||
Snapshot als `docker-compose.yml.stack-backup` als auch daneben unter
|
||||
`docker-compose.yml.bak.<stamp>`. Beide werden vom Rollback automatisch
|
||||
zurückgespielt, gefolgt von `docker compose up -d --no-deps landing`; das Backup
|
||||
`.bak.<stamp>` bleibt zusätzlich für einen manuellen Eingriff liegen.
|
||||
|
||||
## Betrieb
|
||||
|
||||
```bash
|
||||
ssh prod-main
|
||||
sudo docker compose -f /home/denny/stacks/desfoto/compose.yml \
|
||||
-f /home/denny/stacks/desfoto/compose.vps.yml ps
|
||||
sudo docker logs --tail 50 desfoto-web-1
|
||||
sudo docker inspect --format '{{.State.Health.Status}}' desfoto-web-1
|
||||
```
|
||||
|
||||
Access-Logs gibt es bewusst nicht (`access_log off` in `nginx.conf`). Auch Fehler
|
||||
schreibt nginx nicht weg: `error_log /dev/null crit;` verwirft sie vollständig,
|
||||
statt sie in eine Datei zu schreiben. Der JSON-Log-Treiber des Containers ist
|
||||
zusätzlich auf 5 MB × 2 Dateien begrenzt. Das entspricht der Zusage in der
|
||||
Datenschutzerklärung.
|
||||
Reference in New Issue
Block a user