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 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
162
MIGRATION.md
Normal file
162
MIGRATION.md
Normal file
@@ -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}}' <container>
|
||||
```
|
||||
|
||||
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: <app>` | CI-Job hat ein anderes Arbeitsverzeichnis als der Host |
|
||||
| `build: .` | `image: <app>:${IMAGE_TAG:-latest}` **plus** `build: .` | Versionstag ermoeglicht Rollback ohne Rebuild |
|
||||
| kein/wechselnder `container_name` | `container_name: <app>` | 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: <app>-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=<alter-projektname>
|
||||
```
|
||||
|
||||
Container stoppen (kein Kopieren aus einem laufenden Datenbank-Volume):
|
||||
|
||||
```bash
|
||||
docker compose stop
|
||||
```
|
||||
|
||||
Kopieren:
|
||||
|
||||
```bash
|
||||
docker volume create <app>-data
|
||||
docker run --rm -v <alter-projektname>_<volume>:/from -v <app>-data:/to \
|
||||
alpine sh -c 'cd /from && cp -a . /to'
|
||||
```
|
||||
|
||||
Pruefen, dass etwas angekommen ist:
|
||||
|
||||
```bash
|
||||
docker run --rm -v <app>-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=<app> --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 <altes-verzeichnis>/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 <app>` zeigt welcher |
|
||||
12
README.md
12
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
|
||||
|
||||
Reference in New Issue
Block a user