25. Deployment
Das Deployment bringt das System von der lokalen Entwicklung auf einen Linux-Server.
Ziel:
Projekt liegt stabil auf dem Server.
.env ist produktiv gesetzt.
Playwright-Browser sind installiert.
Sessions, Screenshots und Logs haben passende Rechte.
Cron startet den Scheduler.
Systemd kann Smoke-Tests oder Worker ausführen.
Rollback ist vorbereitet.
Für dieses Handbuch wird Debian/Ubuntu angenommen.
25.1 Verzeichnis auf Server
Empfohlenes Zielverzeichnis:
/opt/social-publisher
Anlegen:
sudo mkdir -p /opt/social-publisher
sudo chown -R "$USER":"$USER" /opt/social-publisher
Projekt kopieren:
rsync -av --exclude ".venv" --exclude ".env" ./ /opt/social-publisher/
Auf dem Server:
cd /opt/social-publisher
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/pip install -r requirements.txt
Erwartete Struktur:
/opt/social-publisher/
├── config.yaml
├── requirements.txt
├── data/
├── prompts/
├── src/
├── storage/
│ ├── sessions/
│ ├── screenshots/
│ ├── images/
│ └── logs/
└── tests/
25.2 .env produktiv setzen
Datei erstellen:
nano /opt/social-publisher/.env
Beispiel:
OPENAI_API_KEY=sk-...
APP_ENV=prod
APP_TIMEZONE=Europe/Berlin
DRY_RUN=true
Rechte setzen:
chmod 600 /opt/social-publisher/.env
Die .env wird nicht versioniert.
.gitignore:
.env
.venv/
storage/sessions/
storage/screenshots/
storage/logs/
__pycache__/
.pytest_cache/
Produktiv zunächst mit DRY_RUN=true starten. Live-Betrieb erst nach erfolgreichem Smoke-Test und geprüfter Session.
25.3 Playwright-Browser installieren
Playwright-Browser installieren:
cd /opt/social-publisher
sudo .venv/bin/python -m playwright install --with-deps chromium
Prüfen:
.venv/bin/python -m playwright install --list
Minimaler Test:
.venv/bin/python -m src.browser_session check linkedin --headed
Auf einem Headless-Server ohne Desktop:
.venv/bin/python -m src.browser_session check linkedin
Falls noch keine Session vorhanden ist, den manuellen Login auf einer Umgebung mit sichtbarem Browser durchführen und storage/sessions/linkedin.json anschließend sicher auf den Server kopieren.
25.4 Rechte setzen
Verzeichnisse anlegen:
sudo mkdir -p /opt/social-publisher/storage/sessions
sudo mkdir -p /opt/social-publisher/storage/screenshots
sudo mkdir -p /opt/social-publisher/storage/images
sudo mkdir -p /opt/social-publisher/storage/logs
sudo mkdir -p /opt/social-publisher/data
Eigentümer setzen:
sudo chown -R "$USER":"$USER" /opt/social-publisher
Rechte für sensible Sessiondaten:
chmod 700 /opt/social-publisher/storage/sessions
chmod 600 /opt/social-publisher/storage/sessions/*.json 2>/dev/null || true
Rechte für Logs und Screenshots:
chmod 755 /opt/social-publisher/storage
chmod 755 /opt/social-publisher/storage/screenshots
chmod 755 /opt/social-publisher/storage/logs
Wenn ein eigener Systemnutzer verwendet wird:
sudo useradd --system --home /opt/social-publisher --shell /usr/sbin/nologin socialpublisher
sudo chown -R socialpublisher:socialpublisher /opt/social-publisher
Dann müssen Cron und Systemd ebenfalls unter diesem Nutzer laufen.
25.5 Systemd-Unit
Für das MVP reicht Cron. Eine Systemd-Unit ist dennoch sinnvoll für manuelle Läufe, Smoke-Tests oder spätere Worker.
Service-Datei:
sudo nano /etc/systemd/system/social-publisher-smoke.service
Inhalt:
[Unit]
Description=Social Publisher Smoke Test
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
WorkingDirectory=/opt/social-publisher
EnvironmentFile=/opt/social-publisher/.env
ExecStart=/opt/social-publisher/.venv/bin/python -m src.main logs events --limit 1
User=socialpublisher
Group=socialpublisher
[Install]
WantedBy=multi-user.target
Falls kein eigener Nutzer verwendet wird, User und Group entsprechend anpassen oder entfernen.
Systemd neu laden:
sudo systemctl daemon-reload
Test:
sudo systemctl start social-publisher-smoke.service
sudo systemctl status social-publisher-smoke.service
Logs:
journalctl -u social-publisher-smoke.service -n 100 --no-pager
Optionale Scheduler-Unit:
sudo nano /etc/systemd/system/social-publisher-scheduler.service
[Unit]
Description=Social Publisher Scheduler
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
WorkingDirectory=/opt/social-publisher
EnvironmentFile=/opt/social-publisher/.env
ExecStart=/opt/social-publisher/.venv/bin/python -m src.scheduler run
User=socialpublisher
Group=socialpublisher
Live-Variante erst später:
ExecStart=/opt/social-publisher/.venv/bin/python -m src.scheduler run --live
25.6 Cron-Konfiguration
Crontab für den Systemnutzer öffnen:
sudo crontab -u socialpublisher -e
Dry-Run-Betrieb:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 8 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.generate_posts >> storage/logs/cron.log 2>&1
10 8 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.review_queue auto-approve >> storage/logs/cron.log 2>&1
*/15 8-18 * * 1-5 flock -n /tmp/social-publisher.lock bash -lc 'cd /opt/social-publisher && .venv/bin/python -m src.scheduler run' >> /opt/social-publisher/storage/logs/cron.log 2>&1
0 18 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.report daily --date $(date +\%F) >> storage/logs/reports.log 2>&1
Live-Betrieb:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 8 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.generate_posts >> storage/logs/cron.log 2>&1
10 8 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.review_queue auto-approve >> storage/logs/cron.log 2>&1
*/15 8-18 * * 1-5 flock -n /tmp/social-publisher.lock bash -lc 'cd /opt/social-publisher && .venv/bin/python -m src.scheduler run --live' >> /opt/social-publisher/storage/logs/cron.log 2>&1
0 18 * * 1-5 cd /opt/social-publisher && .venv/bin/python -m src.report daily --date $(date +\%F) >> storage/logs/reports.log 2>&1
Cron prüfen:
sudo crontab -u socialpublisher -l
Log prüfen:
tail -n 100 /opt/social-publisher/storage/logs/cron.log
25.7 Smoke-Test produktiv
Reihenfolge:
cd /opt/social-publisher
Python-Import prüfen:
.venv/bin/python -m src.main logs events --limit 1
Konfiguration prüfen:
.venv/bin/python -m src.scheduler run
OpenAI-Test:
.venv/bin/python -m src.main generate --platform linkedin --type workshop
Queue prüfen:
.venv/bin/python -m src.main queue list --status draft
Freigabe im Dry-Run:
.venv/bin/python -m src.main queue auto-approve
.venv/bin/python -m src.main publish-next
Screenshot prüfen:
ls -lah storage/screenshots/linkedin
Logs prüfen:
tail -n 50 storage/logs/events.jsonl
tail -n 50 storage/logs/errors.jsonl
Live-Test mit einem einzelnen freigegebenen Beitrag:
.venv/bin/python -m src.main publish-next --live
Direkt danach prüfen:
.venv/bin/python -m src.main queue status
tail -n 50 storage/logs/events.jsonl
tail -n 50 storage/logs/errors.jsonl
25.8 Rollback
Vor jeder Änderung Backup anlegen:
cd /opt
sudo tar -czf social-publisher-backup-$(date +%Y%m%d-%H%M%S).tar.gz social-publisher
Nur Code zurückrollen, Daten behalten:
cd /opt/social-publisher
git status
git log --oneline -n 5
Dann gezielt auf eine bekannte Version wechseln. Keine destruktiven Git-Befehle ohne vorherige Sicherung.
Sicherer Rollback über neues Verzeichnis:
sudo mkdir -p /opt/social-publisher-rollback
sudo chown -R "$USER":"$USER" /opt/social-publisher-rollback
Backup entpacken:
cd /opt
sudo tar -xzf social-publisher-backup-YYYYMMDD-HHMMSS.tar.gz -C /opt/social-publisher-rollback --strip-components=1
Cron temporär deaktivieren:
sudo crontab -u socialpublisher -e
Alle aktiven Social-Publisher-Zeilen auskommentieren.
Smoke-Test im Rollback-Verzeichnis:
cd /opt/social-publisher-rollback
.venv/bin/python -m src.main queue status
.venv/bin/python -m src.scheduler run
Umschalten erst nach Prüfung:
sudo mv /opt/social-publisher /opt/social-publisher-broken-$(date +%Y%m%d-%H%M%S)
sudo mv /opt/social-publisher-rollback /opt/social-publisher
sudo chown -R socialpublisher:socialpublisher /opt/social-publisher
Cron wieder aktivieren:
sudo crontab -u socialpublisher -e
Ergebnis dieses Kapitels
Das Deployment umfasst jetzt:
Serververzeichnis unter /opt
produktive .env
installierte Playwright-Browser
gesetzte Rechte für Sessions, Logs und Screenshots
optionale Systemd-Units
Cron-Automatisierung mit Lockfile
produktiver Smoke-Test
Rollback über Backup-Verzeichnis
Damit ist das System serverfähig, bleibt aber kontrolliert: Dry-Run vor Live-Betrieb, Logs vor Vertrauen, Rollback vor Änderungen.
0 Kommentare