docs(quick-260914-ku1): Betriebshandbuch — Zwei Kanäle Live und Beta, Freigabe, Hotfix ohne Datenbankänderung, neuer Live-Server; ci-cd-setup auf gemessenen Stand
- anleitung-betrieb.md: neues Kapitel 9 (Kanal, IMAGE_TAG je Server, Freigabe, Hotfix-Ablauf mit Regel "Keine Datenbankaenderung als Hotfix", drei Kontrollwege, Einrichtung des Live-Servers, Erstfreigabe v1.0.0); Inhaltsverzeichnis, Tabelle in Kapitel 1, IMAGE_TAG in Kapitel 3, Etiketten in Kapitel 4, Startzeile in Kapitel 7 - ci-cd-setup.md: REGISTRY_TOKEN und Push ueber localhost:3002, Trigger main/live/v*, Jobs quality -> test -> publish, Etiketten- und Build-Arg-Tabellen, D-13 ueberholt, Tag-/Branch-Schutz-Empfehlung (T-KU1-04), Fehlerbehebung fuer den Stempel Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
+184
-9
@@ -19,6 +19,7 @@ Betrieb der bereits laufenden Installation, nicht deren automatisierten Build.
|
||||
6. [Sicherung und Wiederherstellung](#6-sicherung-und-wiederherstellung)
|
||||
7. [Protokolle und Fehlersuche](#7-protokolle-und-fehlersuche)
|
||||
8. [Abgrenzung zur CI/CD-Pipeline](#8-abgrenzung-zur-cicd-pipeline)
|
||||
9. [Zwei Kanäle: Live und Beta](#9-zwei-kanäle-live-und-beta)
|
||||
|
||||
---
|
||||
|
||||
@@ -29,8 +30,8 @@ Tessera besteht aus drei Containern, definiert in `docker-compose.yml` (Basis) u
|
||||
|
||||
| Dienst | Image / Build | Host-Port | Zweck |
|
||||
|--------|---------------|-----------|-------|
|
||||
| `web` | `apps/web/Dockerfile` (Basis) bzw. `git.vicolab.de/schalli/tessera-ctl/web:latest` (Prod) | 3000 | Next.js-Frontend (Portal, Dashboard) |
|
||||
| `api` | `apps/api/Dockerfile` (Basis) bzw. `git.vicolab.de/schalli/tessera-ctl/api:latest` (Prod) | 3001 | NestJS-Backend (REST-API, Prisma/PostgreSQL-Zugriff) |
|
||||
| `web` | `apps/web/Dockerfile` (Basis) bzw. `git.vicolab.de/schalli/tessera-ctl/web:${IMAGE_TAG}` (Prod; `beta` oder `live`, siehe Kapitel 9) | 3000 | Next.js-Frontend (Portal, Dashboard) |
|
||||
| `api` | `apps/api/Dockerfile` (Basis) bzw. `git.vicolab.de/schalli/tessera-ctl/api:${IMAGE_TAG}` (Prod; `beta` oder `live`, siehe Kapitel 9) | 3001 | NestJS-Backend (REST-API, Prisma/PostgreSQL-Zugriff) |
|
||||
| `db` | `postgres:16-alpine` | kein Host-Port | PostgreSQL-Datenbank |
|
||||
|
||||
Zusätzlich existiert `docker-compose.dev.yml` (Bind-Mounts für Live-Reload,
|
||||
@@ -159,6 +160,7 @@ Zugangsdaten.
|
||||
| `TESSERA_SMTP_HOST` / `_PORT` / `_SECURE` / `_USER` / `_PASSWORD` / `_FROM` | nein (aber ohne Host kein Mailversand) | Host leer, Port `587`, `_SECURE=false` | SMTP-Relay für ausgehende Mails (Passwort-Reset, Benachrichtigungen). |
|
||||
| `APP_URL` | empfohlen | `http://localhost:3001` (für `NEXT_PUBLIC_API_URL`) / `http://localhost:3000` (für `TESSERA_APP_URL`) | Öffentliche Basis-URL der Web-Oberfläche. Wird serverseitig u. a. für in E-Mails generierte Links verwendet (`TESSERA_APP_URL`). |
|
||||
| `API_INTERNAL_URL` | fest verdrahtet | `http://api:3001` | Adresse, unter der `web` die API **innerhalb** des Docker-Netzes erreicht; dorthin schreibt Next.js die `/api-proxy/*`-Rewrites um. In der Regel nicht ändern. |
|
||||
| `IMAGE_TAG` | empfohlen | `beta` | Welcher Kanal auf diesem Server läuft: `beta` (alle Neuerungen, alpha) oder `live` (nur freigegebene Versionen, tessera.ctl.de). Siehe Kapitel 9. |
|
||||
|
||||
Hinweis zu `NEXT_PUBLIC_API_URL`: Diese Variable wird beim Image-Build bereits fest
|
||||
auf `/api-proxy` gesetzt (`ENV NEXT_PUBLIC_API_URL=/api-proxy` in
|
||||
@@ -191,7 +193,7 @@ docker compose -f docker-compose.prod.yml up -d --force-recreate api web
|
||||
**Verifizierte Falle:** `docker compose up -d` **ohne** `--force-recreate` ersetzt
|
||||
einen bereits laufenden Container **nicht**, wenn Compose der Meinung ist, an der
|
||||
Service-Definition habe sich nichts geändert – auch wenn `pull` gerade ein neues
|
||||
Image unter demselben Tag (`:latest`) heruntergeladen hat. Der alte Container läuft
|
||||
Image unter demselben Etikett (`beta` bzw. `live`) heruntergeladen hat. Der alte Container läuft
|
||||
dann unverändert mit dem alten Code weiter, ohne Fehlermeldung. Das Deployment
|
||||
wirkt erfolgreich, ist es aber nicht. Dieser Fehler ist dem Team schon mehrfach
|
||||
passiert. Deshalb: nach jedem `pull` **immer** `--force-recreate` verwenden (nur
|
||||
@@ -204,12 +206,15 @@ Erstellungszeitpunkt des zugehörigen Images vergleichen. Der Container muss
|
||||
|
||||
```bash
|
||||
docker inspect -f '{{.State.StartedAt}}' tessera-api-1
|
||||
docker image inspect -f '{{.Created}}' git.vicolab.de/schalli/tessera-ctl/api:latest
|
||||
docker image inspect -f '{{.Created}}' git.vicolab.de/schalli/tessera-ctl/api:beta
|
||||
|
||||
docker inspect -f '{{.State.StartedAt}}' tessera-web-1
|
||||
docker image inspect -f '{{.Created}}' git.vicolab.de/schalli/tessera-ctl/web:latest
|
||||
docker image inspect -f '{{.Created}}' git.vicolab.de/schalli/tessera-ctl/web:beta
|
||||
```
|
||||
|
||||
(Auf dem Live-Server statt `:beta` jeweils `:live` einsetzen – das Etikett, das in
|
||||
der `.env` als `IMAGE_TAG` steht, siehe Kapitel 9.)
|
||||
|
||||
(Container-Namen mit `docker compose -f docker-compose.prod.yml ps` prüfen, falls
|
||||
sie auf dem Server abweichen.) Liegt `StartedAt` **vor** `Created` des Images, läuft
|
||||
noch die alte Version – dann `--force-recreate` nachholen.
|
||||
@@ -317,8 +322,10 @@ docker compose -f docker-compose.prod.yml logs -f db
|
||||
|
||||
**Gesunder Start sieht so aus:** `db` wird `healthy`, danach startet `api` und
|
||||
protokolliert die durchgelaufenen Prisma-Migrationen sowie zuletzt
|
||||
`Tessera API running on port 3001` (aus `apps/api/src/main.ts`). Erst danach startet
|
||||
`web`, weil `depends_on: api: condition: service_healthy` das erzwingt.
|
||||
`Tessera API running on port 3001` und direkt darunter eine Zeile wie
|
||||
`Tessera API v1.0.0 (live) abc1234` (beides aus `apps/api/src/main.ts`) – Version,
|
||||
Kanal und Kurzkennung des Standes, der gerade läuft (siehe Kapitel 9). Erst danach
|
||||
startet `web`, weil `depends_on: api: condition: service_healthy` das erzwingt.
|
||||
|
||||
| Symptom | Wahrscheinliche Ursache | Prüfen / Beheben |
|
||||
|---|---|---|
|
||||
@@ -342,5 +349,173 @@ Berechtigungen des `act_runner`-Docker-Socket-Mounts) aufgesetzt wird, ist bewus
|
||||
nicht Teil dieses Dokuments – das steht vollständig in
|
||||
[`docs/ci-cd-setup.md`](./ci-cd-setup.md). Die Grenze zwischen beiden Dokumenten:
|
||||
Sobald ein neues Image lokal vorliegt oder in einer Registry verfügbar ist, beginnt
|
||||
dieses Betriebshandbuch (Kapitel 4); alles davor – wie das Image entsteht – gehört
|
||||
in das CI/CD-Runbook.
|
||||
dieses Betriebshandbuch (Kapitel 4 und 9); alles davor – wie das Image entsteht –
|
||||
gehört in das CI/CD-Runbook.
|
||||
|
||||
## 9. Zwei Kanäle: Live und Beta
|
||||
|
||||
Seit September 2026 gibt es Tessera in zwei Ausgaben, die getrennt voneinander
|
||||
laufen. Dieses Kapitel erklärt, was das bedeutet, welche Zeile auf welchem Server
|
||||
stehen muss, wie eine Version freigegeben wird, wie ein dringender Fehler auf Live
|
||||
behoben wird, und wie Sie jederzeit sehen, welche Fassung gerade läuft.
|
||||
|
||||
### Was ein Kanal ist
|
||||
|
||||
Ein Kanal ist eine Ausgabe von Tessera, die auf einem bestimmten Server läuft und
|
||||
nach eigenen Regeln neue Stände bekommt. Es gibt zwei:
|
||||
|
||||
- **Beta** – alles Neue, sofort nach jeder Änderung. Läuft unter
|
||||
`alpha.tessera.ctl.de`. Das Etikett (die Kennzeichnung des Docker-Images in der
|
||||
Registry) heißt `beta`. Das ältere Etikett `latest` ist nur ein zweiter Name für
|
||||
genau dasselbe Beta-Image; es bleibt vorerst bestehen, damit nichts kaputtgeht,
|
||||
und kann später wegfallen.
|
||||
- **Live** – nur freigegebene Versionen mit einer Nummer. Läuft unter
|
||||
`tessera.ctl.de` auf dem neuen Server. Das Etikett heißt `live`; zusätzlich trägt
|
||||
jede freigegebene Version ihre Nummer als eigenes Etikett (`v1.0.0`, `v1.0.1`, …),
|
||||
damit man jederzeit auch einen älteren Stand gezielt holen kann.
|
||||
|
||||
Die Versionsnummer kommt aus der Freigabe (in Git heißt das „Tag“ – eine Markierung
|
||||
an einem bestimmten Stand), nicht aus einer Datei im Code. Zwischen zwei Freigaben
|
||||
zeigt die Beta eine Kennung wie `v1.0.0-12-gabc1234`: das bedeutet „12 Änderungen
|
||||
nach Version 1.0.0, Stand abc1234“. Vor der allerersten Freigabe steht dort nur die
|
||||
Kurzkennung des Standes (sieben Zeichen, z. B. `abc1234`).
|
||||
|
||||
### Die eine Zeile je Server
|
||||
|
||||
Welchen Kanal ein Server bekommt, entscheidet **eine einzige Zeile** in der Datei
|
||||
`/opt/tessera/.env`:
|
||||
|
||||
- auf **alpha** (Beta): `IMAGE_TAG=beta`
|
||||
- auf dem **neuen Live-Server**: `IMAGE_TAG=live`
|
||||
|
||||
Fehlt die Zeile ganz, nimmt die Compose-Datei von selbst `beta`. Für den Live-Server
|
||||
ist die Zeile also Pflicht, sonst zieht er die Beta.
|
||||
|
||||
Weil `/opt/tessera` keine Arbeitskopie des Repositorys ist (siehe Kapitel 3,
|
||||
„Konfigurationsdrift“), muss die Compose-Datei auf dem Server einmal von Hand
|
||||
angepasst werden. Auf alpha ist das die Datei `/opt/tessera/docker-compose.prod.yml`
|
||||
(die `.env` dort verweist mit `COMPOSE_FILE` auf sie). Zuerst eine Sicherung:
|
||||
|
||||
```bash
|
||||
cd /opt/tessera
|
||||
cp docker-compose.prod.yml docker-compose.prod.yml.bak.$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
Dann die zwei `image:`-Zeilen (eine beim Dienst `web`, eine beim Dienst `api`) auf
|
||||
diese Form bringen – der einzige Unterschied zu heute ist das Ende der Zeile:
|
||||
|
||||
```yaml
|
||||
image: git.vicolab.de/schalli/tessera-ctl/web:${IMAGE_TAG:-beta}
|
||||
```
|
||||
|
||||
```yaml
|
||||
image: git.vicolab.de/schalli/tessera-ctl/api:${IMAGE_TAG:-beta}
|
||||
```
|
||||
|
||||
Danach wie in Kapitel 4:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d --force-recreate api web
|
||||
```
|
||||
|
||||
Hinweis: Die Vorlage `.env.prod.example` im Repository enthält die Zeile
|
||||
`IMAGE_TAG` noch nicht. Wer eine neue `.env` aus der Vorlage anlegt, ergänzt die
|
||||
Zeile von Hand.
|
||||
|
||||
### Eine Version freigeben
|
||||
|
||||
Das Freigeben erledigt Claude; Sie sagen nur „Version X freigeben“. Zur Einordnung,
|
||||
was dabei passiert:
|
||||
|
||||
```bash
|
||||
git checkout live
|
||||
git merge --ff-only main
|
||||
git tag -a vX.Y.Z -m "Tessera X.Y.Z"
|
||||
git push origin live vX.Y.Z
|
||||
```
|
||||
|
||||
Der zweite Befehl übernimmt den Stand der Beta in den Live-Zweig. Wenn er sich
|
||||
weigert, ist eine frühere Korrektur (siehe Hotfix, Schritt 5) noch nicht zurück in
|
||||
`main` – dann wird erst das nachgeholt. Der Push löst die Pipeline zweimal aus: der
|
||||
Zweig `live` wird nur geprüft, der Tag `vX.Y.Z` wird gebaut und als `live` und
|
||||
`vX.Y.Z` abgelegt. Das dauert etwa vier bis sechs Minuten.
|
||||
|
||||
Danach spielen Sie die Version auf dem Live-Server ein – Kapitel 4 gilt unverändert:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d --force-recreate api web
|
||||
```
|
||||
|
||||
**Erstfreigabe v1.0.0:** Den Zweig `live` gibt es noch nicht. Er entsteht beim
|
||||
ersten Mal aus `main` (`git checkout -b live main`), bekommt den Tag `v1.0.0` und
|
||||
wird zusammen mit dem Tag gepusht. Das erfolgt, sobald der Knopf „Fehler melden“
|
||||
eingebaut ist – nicht in diesem Durchlauf.
|
||||
|
||||
### Einen Fehler auf Live beheben (Hotfix)
|
||||
|
||||
Ein Hotfix ist eine kleine Korrektur, die auf Live landen muss, **ohne** die
|
||||
Neuerungen der Beta mitzunehmen. Ablauf (Claude führt die Git-Schritte aus, Sie
|
||||
spielen ein):
|
||||
|
||||
1. Den Live-Stand holen: `git checkout live && git pull`.
|
||||
2. Einen Korrekturzweig `hotfix/<kurzer-name>` von `live` anlegen.
|
||||
3. Die Korrektur machen und die Tests laufen lassen.
|
||||
4. Nach `live` mergen, die nächste Nummer vergeben (`vX.Y.(Z+1)`, also z. B.
|
||||
`v1.0.1` nach `v1.0.0`) und beides pushen: `git push origin live vX.Y.(Z+1)`.
|
||||
Danach spielen Sie die Version auf dem Live-Server ein (Befehle wie oben).
|
||||
5. Die Korrektur in die Beta übernehmen: `git checkout main && git merge live`.
|
||||
Vorher wird geprüft, ob die Korrektur dort noch zusammenpasst (Konflikte, Tests),
|
||||
dann `git push` – die Beta bekommt sie mit dem nächsten Pipeline-Lauf.
|
||||
|
||||
**Keine Datenbankänderung als Hotfix.** Der Grund in Alltagssprache:
|
||||
Datenbankänderungen (Migrationen) tragen einen Zeitstempel im Namen und werden in
|
||||
dieser Reihenfolge ausgeführt. Die Beta hat womöglich schon neuere Änderungen
|
||||
eingespielt. Eine Hotfix-Änderung mit noch späterem Zeitstempel landet beim
|
||||
Übernehmen in die Beta hinter Änderungen, die sie eigentlich nicht kennt – das ist
|
||||
der eine Fall, der beim Zusammenführen still kaputtgehen kann. Braucht eine Korrektur
|
||||
eine Datenbankänderung, wird sie als reguläre Version über `main` freigegeben.
|
||||
|
||||
### Woran Sie erkennen, welche Version läuft
|
||||
|
||||
Drei Wege, vom einfachsten zum genauesten:
|
||||
|
||||
1. **In der Oberfläche:** Unten in der Seitenleiste steht `v1.0.0 · Live` bzw.
|
||||
`v1.0.0-12-gabc1234 · Beta`. Wenn Sie die Maus darüber halten, erscheinen die
|
||||
Kurzkennung des Standes und die Version, die der Server meldet. Weichen
|
||||
Oberfläche und Server voneinander ab, wurde nur einer der beiden Container neu
|
||||
erstellt – dann Kapitel 4 anwenden (`--force-recreate api web`).
|
||||
2. **Auf dem Server per Abfrage:**
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:3001/health/version
|
||||
```
|
||||
|
||||
Die Antwort enthält die Felder `version`, `channel` (`beta` oder `live`),
|
||||
`commit` (Kurzkennung) und `buildTime` (wann das Image gebaut wurde).
|
||||
3. **Im Protokoll:**
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml logs api | grep "Tessera API"
|
||||
```
|
||||
|
||||
Zeigt die Startzeile `Tessera API v1.0.0 (live) abc1234` (siehe Kapitel 7).
|
||||
|
||||
### Den neuen Live-Server einrichten
|
||||
|
||||
Kapitel 2 gilt vollständig. Die Abweichungen gegenüber alpha:
|
||||
|
||||
- In der `.env` steht `IMAGE_TAG=live`.
|
||||
- Eigene, neu erzeugte Geheimnisse: `JWT_SECRET`, `TESSERA_ENCRYPTION_KEY`,
|
||||
`DB_PASSWORD` und das Admin-Passwort. Nichts davon von alpha übernehmen.
|
||||
- Eine eigene, leere Datenbank. Die API legt beim ersten Start den ersten Admin an
|
||||
(Kapitel 2, Schritt 4). Die alpha-Datenbank wird **nicht** kopiert – es sei denn,
|
||||
das wird ausdrücklich gewünscht. In diesem Fall gilt Kapitel 6 (Wiederherstellung)
|
||||
**und** der Live-Server muss denselben `TESSERA_ENCRYPTION_KEY` wie alpha
|
||||
bekommen, sonst sind alle gespeicherten Zugangsdaten unbrauchbar.
|
||||
- `APP_URL=https://tessera.ctl.de`.
|
||||
- Der erste `pull` holt das Etikett `live`. Vor der Erstfreigabe v1.0.0 gibt es
|
||||
dieses Etikett noch nicht – deshalb erst freigeben, dann installieren. Für einen
|
||||
Probelauf davor kann vorübergehend `IMAGE_TAG=beta` stehen; danach auf `live`
|
||||
umstellen und `pull` + `up -d --force-recreate api web` wiederholen.
|
||||
|
||||
Reference in New Issue
Block a user