From 5b7418138e669eaa33f9689d0e92b97f758d2502 Mon Sep 17 00:00:00 2001 From: Lucas Orth Date: Thu, 20 Aug 2026 19:23:21 +0200 Subject: [PATCH] Anleitung zum Umstellen bestehender Container Fuer Apps, die heute per SCP deployt werden. Schwerpunkt liegt auf den beiden Aenderungen, die nicht rueckwirkungsfrei sind: der feste Projektname benennt die Volumes um, der feste Container-Name kollidiert mit dem noch laufenden alten Stack. Enthaelt das Rezept zum Umkopieren der Volumes und einen Rueckweg, solange das alte Volume noch existiert. Co-Authored-By: Claude Opus 5 --- DEPLOY.md | 6 ++ MIGRATION.md | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 12 ++++ 3 files changed, 180 insertions(+) create mode 100644 MIGRATION.md diff --git a/DEPLOY.md b/DEPLOY.md index 2e43c1c..6da1ceb 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -159,6 +159,12 @@ Danach Let's-Encrypt-Zertifikat ausstellen und "Force SSL" aktivieren. Weil der Container-Name die Adresse ist: aendert sich `container_name`, zeigt der Proxy-Host ins Leere. +## Bestehende App umstellen + +Laeuft die App heute schon manuell auf dem Server, gilt statt des folgenden +Abschnitts [MIGRATION.md](MIGRATION.md) - dort steht insbesondere, wie die +Volumes den Wechsel des Projektnamens ueberleben. + ## Erstes Deployment einer App 1. Repo in Gitea anlegen, pushen. diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..230d8dc --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,162 @@ +# Bestehenden Container umstellen + +Anleitung fuer Apps, die heute manuell deployt werden (Dateien per SCP auf +den Server, dort `docker compose up -d --build`) und auf Tag-Deployment +umziehen sollen. + +Die Zielkonventionen stehen in [DEPLOY.md](DEPLOY.md). Hier steht nur, was +beim **Umstieg** zu tun ist und wo dabei Daten verloren gehen koennen. + +## Der gefaehrliche Teil zuerst + +Zwei Aenderungen sind nicht rueckwirkungsfrei: + +1. **Fester Projektname** (`name:` im Compose). Compose leitet den + Projektnamen sonst aus dem Verzeichnisnamen ab. Aendert er sich, heissen + auch die **Volumes anders** - die App startet mit leerem Datenbestand. +2. **Fester Container-Name.** Der alte Container belegt ihn noch. Ohne + vorheriges `down` schlaegt der erste Deploy fehl. + +Beides ist beherrschbar, aber nicht durch Ausprobieren. Schritt 5 und 6 +gehoeren zusammen und in dieser Reihenfolge ausgefuehrt. + +## Schritt 0: Bestand aufnehmen + +Auf dem Server, im Verzeichnis der App: + +```bash +docker compose config --format json | head -40 +docker compose ps +docker volume ls +docker inspect -f '{{range $n,$v := .NetworkSettings.Networks}}{{$n}} {{end}}' +``` + +Notieren: + +- aktueller **Projektname** (= Verzeichnisname, falls `name:` fehlt) +- aktuelle **Volume-Namen** (Praefix ist der Projektname) +- ob der Container schon im Netz `nginx-proxy-manager_default` haengt +- ob `ports:` verwendet wird und worauf der NPM-Host zeigt +- Inhalt der `.env` + +## Schritt 1: Repo anlegen + +Code lokal in ein Git-Repo, in Gitea pushen. Noch keinen Tag setzen. + +Falls der Code bisher nur auf dem Server lag: per SCP herunterholen, **ohne** +`.env`, `node_modules`, `dist` und Datenverzeichnisse. + +## Schritt 2: docker-compose.yml umbauen + +| Vorher | Nachher | Warum | +|---|---|---| +| kein `name:` | `name: ` | CI-Job hat ein anderes Arbeitsverzeichnis als der Host | +| `build: .` | `image: :${IMAGE_TAG:-latest}` **plus** `build: .` | Versionstag ermoeglicht Rollback ohne Rebuild | +| kein/wechselnder `container_name` | `container_name: ` | Das ist die Adresse, auf die der Proxy zeigt | +| `environment:` mit Klartextwerten | `env_file: .env` | Wird im Workflow aus dem Secret erzeugt | +| `volumes: [foo:/data]` | zusaetzlich `name: -data` im `volumes`-Block | Loest das Volume vom Projektnamen | +| `ports: ["8080:8080"]` | streichen | Der Proxy erreicht den Container ueber das gemeinsame Netz | +| kein `healthcheck:` | einen ergaenzen | Der Deploy nutzt `--wait` und macht ihn zum Gate | +| relative Bind-Mounts `./x:/x` | named volume | Compose laeuft im Job-Container, der Pfad zeigt ins Leere | + +Vorlage: [template/docker-compose.yml](template/docker-compose.yml). + +## Schritt 3: Workflow anlegen + +[template/.gitea/workflows/deploy.yml](template/.gitea/workflows/deploy.yml) +uebernehmen, Build- und Test-Schritte des Projekts einsetzen. Tests gehoeren +**vor** den Deploy-Schritt. + +In `.dockerignore` `.gitea` ergaenzen. + +## Schritt 4: Secret setzen + +Inhalt der `.env` vom Server als Repo-Secret `DOTENV` anlegen +(Repo -> Einstellungen -> Actions -> Secrets), mehrzeilig am Stueck. + +Vorher eine Kopie in den Passwortmanager - Gitea gibt den Wert nicht mehr +heraus. + +## Schritt 5: Volumes migrieren + +Nur noetig, wenn die App persistente Daten hat, die **nicht** neu erzeugt +werden koennen. Bei einem regenerierbaren Cache: ueberspringen. + +Alte Volumes finden: + +```bash +docker volume ls --filter name= +``` + +Container stoppen (kein Kopieren aus einem laufenden Datenbank-Volume): + +```bash +docker compose stop +``` + +Kopieren: + +```bash +docker volume create -data +docker run --rm -v _:/from -v -data:/to \ + alpine sh -c 'cd /from && cp -a . /to' +``` + +Pruefen, dass etwas angekommen ist: + +```bash +docker run --rm -v -data:/d alpine ls -la /d +``` + +Das alte Volume **nicht** loeschen, bis der erste Deploy laeuft. Es ist der +Rueckweg. + +## Schritt 6: Cutover + +```bash +docker compose down +``` + +Damit ist der Container-Name frei. Der Dienst ist ab hier bis zum Ende des +ersten Deploys offline. + +Dann lokal: + +```bash +git tag v1.0.0 && git push origin v1.0.0 +``` + +## Schritt 7: Pruefen + +```bash +docker ps --filter name= --format '{{.Names}}\t{{.Status}}' +``` + +`Status` muss `healthy` zeigen, nicht `Restarting`. Der Workflow faengt das +zwar durch `--wait` ab, aber nachsehen kostet nichts. + +Danach die App ueber ihre Domain aufrufen. Bei 502 im NPM: Container haengt +nicht im Netz `nginx-proxy-manager_default`, oder der Proxy-Host zeigt noch +auf `IP:Port` statt auf den Container-Namen. + +## Wenn es schiefgeht + +Der alte Zustand ist noch da, solange du das alte Volume nicht geloescht +hast: + +```bash +docker compose -f /docker-compose.yml up -d +``` + +Der Workflow selbst faellt in den Schritten 1-7 folgenlos aus - erst der +Deploy-Schritt fasst den Server an. + +## Fallstricke + +| Symptom | Ursache | +|---|---| +| `Conflict. The container name "/x" is already in use` | Schritt 6 uebersprungen, alter Stack laeuft noch | +| App startet mit leerem Datenbestand | Schritt 5 uebersprungen, Volume heisst jetzt anders | +| Deploy gruen, App antwortet nicht | `ports:` entfernt, aber NPM-Host zeigt noch auf `IP:Port` | +| `network nginx-proxy-manager_default not found` | Netz im Compose nicht als `external: true` deklariert | +| Container startet endlos neu | Wert in `DOTENV` fehlt; `docker logs ` zeigt welcher | diff --git a/README.md b/README.md index 2f29302..fcf8454 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,18 @@ Das Skript legt an, was fehlt, und laesst Vorhandenes unangetastet: `__APP__` und `__PORT__` werden dabei ersetzt. +## Bestehende App umstellen + +Fuer Apps, die heute per SCP deployt werden, gibt es eine eigene Anleitung - +inklusive Volume-Migration, damit der Wechsel des Projektnamens keine Daten +kostet: + +```bash +curl -fsSL -o MIGRATION.md https://gitea.lucas-orth.de/lucas.orth/deploy-kit/raw/branch/main/MIGRATION.md +``` + +Dann Claude bitten, die Schritte darin auf das Projekt anzuwenden. + ## Nur die Konventionen Wenn ein Projekt schon eingerichtet ist und du nur die aktuelle Fassung der