refactor(deploy): remove old multi-resource code, document single docker-compose workflow

- Remove POSTGRES_COMPOSE, REDIS_COMPOSE templates (unused)
- Remove create_service(), create_api_application(), generate_worker_compose()
- Remove deploy_worker(), deploy_worker_only(), verify_worker_service()
- Remove resolve_worker_uuid() and all worker_uuid references
- Remove get_worker_envs(), get_api_envs(), get_postgres_envs(), get_redis_envs()
- Remove set_service_envs(), set_application_envs() (dead code)
- Remove _extract_deploy_uuid(), _wait_service_healthy() (only used by deploy_worker)
- Remove seed_admin_user() (only used by old deploy_full)
- Remove DB_HOST, REDIS_HOST, WORKER_UUID, WORKER_NAME config vars
- Remove --worker-only CLI arg
- Replace old deploy_full() with simple redeploy via /api/v1/deploy
- Update run_verification() to remove worker_uuid param
- Add KI workflow comment at top of deploy.py
- Update DEPLOY.md: single docker-compose stack workflow
- Update COOLIFY_SETUP.md: single docker-compose stack, remove 3-resource setup
- Update docs/INSTALL.md: automated --initial workflow

deploy.py: 1370 → 893 lines (-477 lines, -35%)
This commit is contained in:
Agent Zero
2026-08-06 01:22:09 +02:00
parent 85fcb90b32
commit 5b7d93cd0e
4 changed files with 400 additions and 877 deletions
+108 -64
View File
@@ -1,39 +1,86 @@
# LeoCRM Deployment
## Architektur
LeoCRM läuft als **einzelner docker-compose Stack** in Coolify. Alle 4 Container
(PostgreSQL, Redis, API, Worker) werden aus der `docker-compose.yaml` im Git-Repo
gestartet und teilen sich ein Docker-Netzwerk.
```
┌─────────────────────────────────────────────┐
│ Coolify Application (docker-compose) │
│ ┌──────────┐ ┌──────────┐ │
│ │ postgres │ │ redis │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ │
│ │ crm_app │ │crm_worker│ │
│ │ (API) │ │ (ARQ) │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
```
Keine separaten Coolify Services für DB/Redis/Worker. Alles in einem Stack.
## Quick Start
### Option A: Coolify (empfohlen für Produktion)
### Redeploy (bestehende Anwendung)
```bash
# Einmalig: Umgebungsvariablen setzen
# Umgebungsvariablen setzen
export COOLIFY_API_TOKEN="dein-token"
export COOLIFY_APP_UUID="dx4pqdziu4uj6x9fxs1u5z0x" # oder via APP_DOMAIN auto-resolved
export APP_DOMAIN="https://crm.media-on.de"
export COOLIFY_APP_UUID="dx4pqdziu4uj6x9fxs1u5z0x" # optional, wird via APP_NAME gesucht
# Deploy
# Redeploy via Coolify API
python scripts/deploy.py
# Redeploy (ohne Neubuild)
python scripts/deploy.py --skip-build
# Verifikation nur
python scripts/deploy.py --verify-only
```
Das Script macht automatisch:
1. Coolify Build & Deploy triggern
2. Persistent Volume in Coolify DB konfigurieren (automatisch, portabel)
3. Auf healthy Container warten
4. RLS auf allen Tenant-Tabellen sicherstellen
5. DB-Migrationen verifizieren
6. Worker-Container starten
7. App-Health verifizieren
8. Domain-Erreichbarkeit prüfen
1. Coolify Application auflösen (via UUID oder Name)
2. Deploy via `/api/v1/deploy` triggern
3. Auf Deployment-Erfolg warten
4. HTTP Health-Check verifizieren
5. Login-Test (optional, wenn LOGIN_EMAIL/LOGIN_PASSWORD gesetzt)
6. Alembic-Migration-Head prüfen (via SSH)
7. RLS-Tabellen zählen (via SSH)
**Funktioniert auf jeder Coolify-Instanz. Bei mehreren Apps. Bei Erst-Deploy und Redeploy.**
### Initial Deployment (neue Anwendung)
```bash
# Alle Umgebungsvariablen setzen
export COOLIFY_API_TOKEN="dein-token"
export APP_DOMAIN="https://crm.media-on.de"
export APP_NAME="leocrm" # Coolify Application Name
export DB_PASSWORD="..."
export REDIS_PASSWORD="..."
export SECRET_KEY="..."
export COOLIFY_PROJECT_UUID="..."
export COOLIFY_SERVER_UUID="..."
export COOLIFY_PRIVATE_KEY_UUID="..."
export COOLIFY_ENVIRONMENT="production" # optional, default: production
# Initial deployment
python scripts/deploy.py --initial
```
Das Script macht automatisch:
1. Coolify Application via `private-deploy-key` erstellen
2. Build Pack auf `dockercompose` setzen (liest docker-compose.yaml aus Git)
3. Environment-Variablen setzen (Secrets, Domain, Admin-Credentials)
4. Erster Deploy (ohne Domain — Coolify muss docker-compose.yaml lesen)
5. `docker_compose_domains` setzen + Redeploy (mit Traefik-Labels)
6. Verifikation (HTTP, Login, Alembic, RLS)
### Option B: Docker Compose (lokal / ohne Coolify)
```bash
# .env.docker erstellen
cp .env.docker.example .env.docker
$EDITOR .env.docker # SECRET_KEY, POSTGRES_PASSWORD, etc. ausfüllen
$EDITOR .env.docker # SECRET_KEY, DB_PASSWORD, REDIS_PASSWORD etc. ausfüllen
# Starten (alle 4 Container: Postgres, Redis, App, Worker)
docker compose --env-file .env.docker up --build -d
@@ -46,19 +93,20 @@ docker compose down
```
**Container:**
- `crm-postgres` — PostgreSQL 16 mit pgvector
- `crm-redis` — Redis 7
- `crm-app` — FastAPI API Server
- `crm-worker` — ARQ Background Worker
- `postgres` — PostgreSQL 16 mit pgvector
- `redis` — Redis 7
- `crm_app` — FastAPI API Server
- `crm_worker` — ARQ Background Worker
Alle mit persistenten Volumes. Kein Datenverlust bei Redeploy.
## Voraussetzungen
- Python 3.12+
- `httpx` Python package
- Docker & Docker Compose (für Option B)
- Coolify v4+ (für Option A)
- SSH-Zugang zum Server (für Option A)
- SSH-Zugang zum Server (für Verifikation, Option A)
## Umgebungsvariablen
@@ -66,13 +114,26 @@ Siehe `.env.example` für alle Variablen. Wichtigste:
| Variable | Pflicht | Default | Beschreibung |
|---|---|---|---|
| `DATABASE_URL` | Ja | — | PostgreSQL Connection String |
| `REDIS_URL` | Ja | — | Redis Connection String |
| `SECRET_KEY` | Ja | — | Mindestens 32 Zeichen |
| `ENVIRONMENT` | Nein | `development` | `production` oder `development` |
| `SESSION_COOKIE_SECURE` | Nein | `true` | In Production muss `true` |
| `STORAGE_PATH` | Nein | `/data/storage` | Datei-Upload-Pfad |
| `STORAGE_BACKEND` | Nein | `local` | `local` oder `s3` |
| `COOLIFY_API_TOKEN` | Ja | — | Coolify API Token |
| `APP_DOMAIN` | Ja | — | App-Domain (z.B. https://crm.media-on.de) |
| `COOLIFY_APP_UUID` | Nein | — | Application UUID (auto-resolved via APP_NAME) |
| `APP_NAME` | Nein | abgeleitet aus APP_DOMAIN | Coolify Application Name |
| `SSH_KEY` | Nein | `/a0/usr/workdir/.ssh/coolify-01-root` | SSH Key für Verifikation |
| `SERVER_IP` | Nein | `46.225.91.159` | Server IP für SSH |
| `LOGIN_EMAIL` | Nein | — | Login-Test Email (optional) |
| `LOGIN_PASSWORD` | Nein | — | Login-Test Passwort (optional) |
### Nur für `--initial`:
| Variable | Pflicht | Beschreibung |
|---|---|---|
| `DB_PASSWORD` | Ja | PostgreSQL Passwort |
| `REDIS_PASSWORD` | Ja | Redis Passwort |
| `SECRET_KEY` | Ja | Application Secret Key (min. 32 Zeichen) |
| `COOLIFY_PROJECT_UUID` | Ja | Coolify Project UUID |
| `COOLIFY_SERVER_UUID` | Ja | Coolify Server UUID |
| `COOLIFY_PRIVATE_KEY_UUID` | Ja | Coolify Private Deploy Key UUID |
| `COOLIFY_ENVIRONMENT` | Nein | Coolify Environment (default: production) |
## S3 Storage (optional)
@@ -85,19 +146,19 @@ S3_ACCESS_KEY=...
S3_SECRET_KEY=...
```
## Test- vs. Produktionsumgebung
## Mehrere Instanzen
**Test:**
Mehrere LeoCRM-Instanzen auf demselben Coolify-Server:
```bash
python scripts/deploy.py --environment test
```
Eigene Coolify-App, eigene DB, eigene Domain (`crm-test.media-on.de`).
# Test-Instanz
APP_NAME=leocrm-test APP_DOMAIN=https://crm-test.media-on.de python scripts/deploy.py --initial
**Produktion:**
```bash
python scripts/deploy.py --environment production
# Produktions-Instanz
APP_NAME=leocrm APP_DOMAIN=https://crm.media-on.de python scripts/deploy.py --initial
```
Jede Instanz hat eigene DB, Redis, Container und Domain.
## Troubleshooting
**Container nicht healthy:**
@@ -110,15 +171,10 @@ docker logs <container-name> --tail 50
docker exec <container> alembic upgrade head
```
**RLS nicht aktiv:**
```bash
python scripts/deploy.py --migrate-only
```
**Worker nicht gestartet:**
```bash
python scripts/deploy.py --skip-build # startet Worker automatisch
```
**503 Fehler (Traefik):**
- Domain ohne `:443` in `docker_compose_domains` setzen
- Service-Namen mit Unterstrichen in docker-compose.yaml (crm_app, nicht crm-app)
- `docker-compose.yaml` (nicht `.yml`) als Dateiname
## Backup & Restore
@@ -126,46 +182,34 @@ python scripts/deploy.py --skip-build # startet Worker automatisch
```bash
# Full DB backup (run on the host or via docker exec)
docker exec crm-postgres pg_dump -U crm_user -Fc crm_db > backup_$(date +%Y%m%d_%H%M%S).dump
# Backup mit Custom-Format (komprimiert, parallel restore-fähig)
docker exec crm-postgres pg_dump -U crm_user -Fc -Z 9 crm_db > backup_$(date +%Y%m%d).dump
docker exec <postgres-container> pg_dump -U crm_user -Fc crm_db > backup_$(date +%Y%m%d_%H%M%S).dump
```
### Backup (Redis — Sessions/Queues)
```bash
# Redis RDB Snapshot
docker exec crm-redis redis-cli -a "$REDIS_PASSWORD" SAVE
docker cp crm-redis:/data/dump.rdb redis_backup_$(date +%Y%m%d).rdb
```
### Backup (File Storage)
```bash
# Local storage volume
docker run --rm -v leocrm-fix_storage:/data -v $(pwd):/backup alpine \
tar czf /backup/storage_$(date +%Y%m%d).tar.gz /data
docker exec <redis-container> redis-cli -a "$REDIS_PASSWORD" SAVE
docker cp <redis-container>:/data/dump.rdb redis_backup_$(date +%Y%m%d).rdb
```
### Restore (PostgreSQL)
```bash
# Stop app containers
docker compose stop crm-app crm-worker
docker compose stop crm_app crm_worker
# Restore DB
docker exec -i crm-postgres pg_restore -U crm_user -d crm_db --clean < backup_20260726.dump
docker exec -i <postgres-container> pg_restore -U crm_user -d crm_db --clean < backup_20260726.dump
# Restart app
docker compose start crm-app crm-worker
docker compose start crm_app crm_worker
```
### Automatisierte Backups (Cron)
```bash
# /etc/cron.d/leocrm-backup
0 2 * * * root docker exec crm-postgres pg_dump -U crm_user -Fc crm_db > /backups/leocrm_$(date +\%Y\%m\%d).dump
0 2 * * * root docker exec <postgres-container> pg_dump -U crm_user -Fc crm_db > /backups/leocrm_$(date +\%Y\%m\%d).dump
0 3 * * * root find /backups -name 'leocrm_*.dump' -mtime +30 -delete
```