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`,
|
Weil der Container-Name die Adresse ist: aendert sich `container_name`,
|
||||||
zeigt der Proxy-Host ins Leere.
|
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
|
## Erstes Deployment einer App
|
||||||
|
|
||||||
1. Repo in Gitea anlegen, pushen.
|
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.
|
`__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
|
## Nur die Konventionen
|
||||||
|
|
||||||
Wenn ein Projekt schon eingerichtet ist und du nur die aktuelle Fassung der
|
Wenn ein Projekt schon eingerichtet ist und du nur die aktuelle Fassung der
|
||||||
|
|||||||
Reference in New Issue
Block a user