diff --git a/README.md b/README.md index bbc56d7..2086e8c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Future upstream updates: [Upgrade-Info (Deutsch)](docs/UPGRADE_INFO.md). Prebuilt-image installation: [Container Registry](docs/CONTAINER_REGISTRY.md) (published image for linux/amd64; no local build required). +**Vollständige manuelle Anleitung (Deutsch): [Docker Pull, Labels, Volumes, Ports und Start](docs/DOCKER_INSTALLATION_DE.md).** + ## Debian / Docker deployment Prerequisites: Docker Engine + Compose; a separately configured LTX Desktop backend; diff --git a/docs/CONTAINER_REGISTRY.md b/docs/CONTAINER_REGISTRY.md index aa06e1c..4a11fe8 100644 --- a/docs/CONTAINER_REGISTRY.md +++ b/docs/CONTAINER_REGISTRY.md @@ -25,6 +25,9 @@ Direkt herunterladen: docker pull git.casaderoll.de/michael/ltx-deskweb:0.1.0-9ff96db ``` +Vollständige Einrichtung einschließlich `docker run`, Labels, Volumes, Rechten +und Ports: [Docker-Installationsanleitung](DOCKER_INSTALLATION_DE.md). + ## Manuelle Installation ohne lokalen Image-Build Repository klonen und `.env`, Secrets sowie gemeinsame Medienverzeichnisse wie diff --git a/docs/DOCKER_INSTALLATION_DE.md b/docs/DOCKER_INSTALLATION_DE.md new file mode 100644 index 0000000..1597f7a --- /dev/null +++ b/docs/DOCKER_INSTALLATION_DE.md @@ -0,0 +1,280 @@ +# LTX DeskWEB manuell mit Docker installieren + +Diese Anleitung installiert das fertige **Web-Frontend** auf Debian/Linux amd64. +Auf dem Zielserver wird nichts kompiliert. Athena Deck muss die GUI nicht selbst +installieren: Die zwei unten genannten Labels machen sie in „Weitere Dienste“ +sichtbar. + +## Voraussetzungen und Aufbau + +- Docker Engine ist installiert und läuft. Für die optionale Compose-Variante + zusätzlich ein aktuelles Docker-Compose-Plugin verwenden. +- Ein eingerichtetes LTX-Desktop-Backend ist separat vorhanden. Bei Verwendung + von Athena Deck stellt dessen API-Port im Videomodus die native LTX-API bereit. +- GUI und LTX-Backend sehen dieselben Eingabe- und Ausgabedateien. Nur eine + API-Adresse genügt nicht, da die native LTX-API Server-Dateipfade verwendet. +- Der gewünschte GUI-Port ist frei. Das Beispiel verwendet Port **8118**. + +Die GUI braucht keine GPU-Freigabe, NVIDIA-Runtime oder Docker-Socket. Das Image +enthält den Webdienst und ffmpeg für Medienvorschauen, keine Modellgewichte. +Die folgenden Serverbefehle als root ausführen. Sie entsprechen Athenas aktueller +LTX-Pfadstruktur; auf einem anderen Server die Pfade und Benutzergruppen anpassen. + +## 1. Fertiges Image herunterladen + +Falls das Paket einen Login verlangt: + +```sh +docker login git.casaderoll.de --username michael +``` + +Passwort bzw. Token interaktiv eingeben. Für den Download genügen Paket-Leserechte. +Dann: + +```sh +docker pull git.casaderoll.de/michael/ltx-deskweb:0.1.0-9ff96db +``` + +Das Image enthält die Korrekturen für Upload-Rechte und Browser-Höhe. +Version und geprüfter Digest stehen in [CONTAINER_REGISTRY.md](CONTAINER_REGISTRY.md). + +## 2. Volumes und Zugriffsrechte vorbereiten + +| Hostpfad im Athena-Beispiel | Pfad im GUI-Container | Zugriff / Zweck | +| --- | --- | --- | +| `/data/video/ltx-desktop/LTXDesktop/remote-inputs/deskweb` | `/data/inputs` | Lesen/Schreiben: Uploads, importierte Dateien, Vorschaubilder | +| `/data/video/ltx-desktop/LTXDesktop/outputs` | `/data/outputs` | Nur Lesen: Ergebnisse des LTX-Backends | +| `/opt/ltx-deskweb/secrets/web-password` | `/run/secrets/web_password` | Nur Lesen: Kennwort für die Web-GUI | +| `/opt/ltx-deskweb/secrets/ltx-token` | `/run/secrets/ltx_token` | Nur Lesen: API-Token des angesprochenen Backends/Deck-Endpunkts | + +Die unten beschriebenen Pfade unter `/opt/ltx-deskweb/secrets` sind ein Beispiel für +**neue manuelle Installationen**. Die bereits ausgerollte Athena-Instanz verwendet +`/opt/ltx-deskweb/source/secrets`; deren Betriebsanleitung steht in +[ATHENA_DEPLOYMENT.md](ATHENA_DEPLOYMENT.md). Keine zweite Instanz mit demselben +Container-Namen oder Port starten. + +Nur für die neue Einrichtung: + +```sh +install -d -m 700 /opt/ltx-deskweb/secrets +install -d -o 1000 -g 0 -m 2750 \ + /data/video/ltx-desktop/LTXDesktop/remote-inputs/deskweb +test -d /data/video/ltx-desktop/LTXDesktop/outputs +``` + +Der letzte Befehl muss erfolgreich sein. Andernfalls zuerst die tatsächliche +Ausgabeablage des LTX-Backends klären, nicht einfach einen leeren Ersatzordner +anlegen. Vorhandene Modell-/Medienverzeichnisse nicht rekursiv umberechtigen. + +**Warum UID 1000 / GID 0?** Die GUI läuft ohne root-UID als Benutzer 1000. Athenas +LTX-Backend läuft als UID/GID 0, aber ohne Linux-Capabilities. Es kann deshalb +private Dateien eines anderen Benutzers nicht automatisch lesen. Der neue +Input-Ordner ist gruppenlesbar und hat das Setgid-Bit (`2750`); neue Dateien erben +GID 0 und erhalten `0640`. So können beide Dienste dieselben Uploads lesen. +Bei anderer Backend-Identität eine passende gemeinsame Gruppe verwenden. +Auch LTX-Ausgaben müssen für die GUI lesbar sein; der Mount bleibt read-only. + +Zuordnung zum **LTX-Container**, nicht zur GUI: + +| GUI-Pfad | Derselbe Inhalt aus Sicht des LTX-Backends | +| --- | --- | +| `/data/inputs` | `/data/LTXDesktop/remote-inputs/deskweb` | +| `/data/outputs` | `/data/LTXDesktop/outputs` | + +Diese zweite Spalte wird mit `BACKEND_INPUT_DIR` und `BACKEND_OUTPUT_DIR` gesetzt. +Bei Athena bindet LTX `/data/video/ltx-desktop` vom Host als `/data` ein. Bei anderen +Mounts müssen die Backend-Pfade entsprechend angepasst werden. + +## 3. Kennwort und API-Token hinterlegen + +Zwei getrennte Dateien verwenden: + +- `web-password`: eigenes GUI-Kennwort, mindestens 16 Zeichen. +- `ltx-token`: vorhandener API-Token von **Athena Deck**, wenn die URL auf Deck + zeigt. Beim direkten LTX-Zugriff stattdessen dessen Token. Nur bei einem Backend + ohne Authentifizierung darf diese Datei leer sein. + +Das folgende interaktive Beispiel schreibt keine Secrets in die Shell-Historie +und überschreibt keine vorhandenen Dateien: + +```sh +python3 - <<'PY' +import getpass, os +from pathlib import Path +root = Path('/opt/ltx-deskweb/secrets') +if any((root / name).exists() for name in ('web-password', 'ltx-token')): + raise SystemExit('Secret-Dateien existieren bereits; nichts überschrieben.') +password = getpass.getpass('Neues GUI-Kennwort (mindestens 16 Zeichen): ') +if len(password) < 16 or password != getpass.getpass('GUI-Kennwort wiederholen: '): + raise SystemExit('Kennwort zu kurz oder Wiederholung stimmt nicht.') +token = getpass.getpass('Vorhandener Deck-/LTX-API-Token: ') +for name, value in [('web-password', password), ('ltx-token', token)]: + fd = os.open(root / name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o400) + with os.fdopen(fd, 'w') as stream: + stream.write(value + '\n') + os.chown(root / name, 1000, 0) +PY +``` + +Secrets nicht ins Repository einchecken. Sie werden nur in den Container gemountet +und bleiben außerhalb des Images. + +## 4. Labels und Ports + +Für die Anzeige unter **Athena Deck → Weitere Dienste** sind beide Labels nötig: + +```text +io.athena-deck.managed=true +io.athena-deck.role=application +``` + +Zusätzliche beschreibende Labels dieses Images: + +```text +org.ltx-deskweb.role=frontend +org.ltx-deskweb.backend=ltx-desktop +``` + +Labels werden beim Erstellen des Containers gesetzt. Sie registrieren kein Modell +und laden keine GPU-Laufzeit. Die GUI erscheint nur in der Docker-Instanz, die Deck +abfragt; ein Container auf einem anderen Server wird dadurch nicht automatisch +entdeckt. + +| Verbindung | Port / Bedeutung | +| --- | --- | +| Browser → Web-GUI | `8118` im Beispiel; frei wählbar | +| Web-GUI → Athena Deck | `8120` im bestehenden Aufbau; die konfigurierte API-Adresse verwenden | +| Web-GUI → LTX direkt (optional) | Tatsächlicher nativer LTX-Port, auf Athena derzeit `41955`; kein zusätzlicher GUI-Port | +| Mac → Athena (SSH-Tunnel) | SSH-Port `22` auf Athena | + +Die folgende Variante verwendet **Host-Networking nur für diesen GUI-Container**. +Damit erreicht sie Decks `127.0.0.1:8120`, ohne Decks Netzwerk zu verändern. +`HOST=127.0.0.1` beschränkt den GUI-Zugriff auf Loopback. Es gibt bei Host-Networking +keine `-p`-Portabbildung: `PORT` ist der tatsächlich auf dem Host belegte Port. + +## 5. Container starten — ohne Build und ohne Git-Checkout + +Vorher z. B. mit `ss -ltn` prüfen, dass `8118` frei ist. + +```sh +docker run -d \ + --name ltx-deskweb \ + --restart unless-stopped \ + --init \ + --network host \ + --user 1000:0 \ + --read-only \ + --cap-drop ALL \ + --security-opt no-new-privileges:true \ + --cpus 2 --memory 1g \ + --tmpfs /tmp:size=128m,mode=1777 \ + --label io.athena-deck.managed=true \ + --label io.athena-deck.role=application \ + --label org.ltx-deskweb.role=frontend \ + --label org.ltx-deskweb.backend=ltx-desktop \ + --env HOST=127.0.0.1 \ + --env PORT=8118 \ + --env PUBLIC_ORIGIN=http://127.0.0.1:8118 \ + --env LTX_BACKEND_URL=http://127.0.0.1:8120 \ + --env WEB_PASSWORD_FILE=/run/secrets/web_password \ + --env LTX_TOKEN_FILE=/run/secrets/ltx_token \ + --env LOCAL_INPUT_DIR=/data/inputs \ + --env BACKEND_INPUT_DIR=/data/LTXDesktop/remote-inputs/deskweb \ + --env LOCAL_OUTPUT_DIR=/data/outputs \ + --env BACKEND_OUTPUT_DIR=/data/LTXDesktop/outputs \ + --mount type=bind,src=/data/video/ltx-desktop/LTXDesktop/remote-inputs/deskweb,dst=/data/inputs \ + --mount type=bind,src=/data/video/ltx-desktop/LTXDesktop/outputs,dst=/data/outputs,readonly \ + --mount type=bind,src=/opt/ltx-deskweb/secrets/web-password,dst=/run/secrets/web_password,readonly \ + --mount type=bind,src=/opt/ltx-deskweb/secrets/ltx-token,dst=/run/secrets/ltx_token,readonly \ + git.casaderoll.de/michael/ltx-deskweb:0.1.0-9ff96db +``` + +Für Portainer/andere Container-Verwaltungen dieselben Labels, Umgebungsvariablen, +Bind-Mounts, Benutzer- und Netzwerkeinstellungen übernehmen. + +### Alternative: vorhandene Compose-Dateien verwenden + +Im geklonten Repository `.env` und `secrets/` gemäß README vorbereiten. Hier liegen +Secrets relativ zum Repository, nicht im Pfad des obigen `docker run`-Beispiels. +Die Werte für den dedizierten Input-Ordner inklusive `/deskweb` verwenden. + +```sh +docker compose -p ltx-deskweb -f compose.yaml -f compose.registry.yaml \ + -f deploy/compose.athena.yaml pull ltx-deskweb +docker compose -p ltx-deskweb -f compose.yaml -f compose.registry.yaml \ + -f deploy/compose.athena.yaml up -d --no-build --no-deps ltx-deskweb +``` + +`compose.registry.yaml` entfernt den Build-Schritt; die Labels kommen aus +`compose.yaml`. Nicht zusätzlich zur `docker run`-Variante starten. + +### Alternative: Bridge-Netzwerk oder anderer Container-Stack + +Ohne `--network host` gilt: `HOST=0.0.0.0` im Container und beispielsweise +`-p 127.0.0.1:8118:8118` für den Host. `LTX_BACKEND_URL` muss vom Container aus +wirklich erreichbar sein: `127.0.0.1` bezeichnet dann die GUI selbst. +`host.docker.internal` allein macht einen ausschließlich auf Host-Loopback +gebundenen Backend-Port nicht erreichbar. Netzwerkzugang bewusst planen, nicht +pauschal bestehende Backend-Bindings oder Firewallregeln ändern. +Ein anderer Rechner benötigt zusätzlich Zugriff auf dieselben Medien, etwa über +geeignet eingebundenen gemeinsamen Speicher. + +## 6. Im Browser öffnen + +Auf dem Mac den Tunnel starten und das Terminal offen lassen: + +```sh +ssh -i /Users/mike_i386/.ssh/athena_key -o BatchMode=yes \ + -o ExitOnForwardFailure=yes -N \ + -L 8118:127.0.0.1:8118 root@192.168.1.212 +``` + +Dann [http://127.0.0.1:8118](http://127.0.0.1:8118) öffnen und das GUI-Kennwort +verwenden. Die Anwendung läuft auf Athena; der Mac ist Browser und Tunnel-Client. +Ist der Tunnel bereits offen, nicht noch einmal denselben Port belegen. + +`PUBLIC_ORIGIN` muss exakt zur verwendeten Browser-Adresse passen, einschließlich +Schema und Port. Bei einer Domain mit HTTPS dort die tatsächliche HTTPS-Origin +setzen und einen passenden Reverse Proxy verwenden. Die Anleitung richtet kein +öffentliches Routing oder WireGuard ein. + +Unter Deck „Weitere Dienste“ aktualisieren: `ltx-deskweb` sollte erscheinen. +Für Generierung **Video in Deck aktivieren**. Der GUI-Start selbst schaltet keine +Modelle um und verändert den LTX-Dienst nicht. + +## 7. Prüfen, stoppen, aktualisieren + +```sh +docker ps --filter name=ltx-deskweb +curl --fail --output /dev/null http://127.0.0.1:8118/ +docker ps -a --filter label=io.athena-deck.managed=true \ + --filter label=io.athena-deck.role=application + +# Nur die GUI stoppen / wieder starten: +docker stop ltx-deskweb +docker start ltx-deskweb +``` + +Für Updates erst die neue geprüfte Image-Version herunterladen und dann nur den +GUI-Container ersetzen. Vorher laufende Generierungen und die GUI-Verbindung +berücksichtigen; ein Neustart beendet die Web-Sitzungen. Compose-Nutzer starten +mit den gleichen drei Dateien erneut `up -d --no-build --no-deps ltx-deskweb`. +Bei `docker run` muss der alte GUI-Container nach dem Stoppen entfernt und mit dem +neuen Image neu angelegt werden. Die Bind-Mount-Daten bleiben erhalten. +Details: [UPGRADE_INFO.md](UPGRADE_INFO.md). + +Projekte liegen in dieser Version im Browser. JSON-Backup in „Connection & +information“ verwenden; ein Container-Backup allein sichert keine Browserprojekte. +Medien und Secrets separat sichern. Ein Image enthält keine Nutzerdaten. + +## Häufige Fehler + +| Meldung / Verhalten | Prüfen | +| --- | --- | +| `Image file not found` | Backend-Pfad, gleicher Hostordner in beiden Containern, Ordnerdurchquerung und Gruppen-Leserechte (`2750` / `0640`) | +| GUI läuft, LTX nicht bereit | Deck im Videomodus? API-Port und Token korrekt? Backend erreichbar? | +| `Cross-origin request rejected` | `PUBLIC_ORIGIN` stimmt exakt mit der Browser-URL überein | +| Container fehlt in Deck | Beide `io.athena-deck.*`-Labels gesetzt und derselbe Docker-Host? | +| Registry `unauthorized` | Login auf dem Rechner ausführen, der das Image pullt; Paket-Leserechte prüfen | +| Port belegt | Freien `PORT` wählen; Tunnel und `PUBLIC_ORIGIN` ebenfalls anpassen. Bei Host-Networking hilft eine Änderung nur an `-p` nicht. | +| Vorschau/Download nicht lesbar | Output-Mount und Dateirechte für UID 1000 / gemeinsame Gruppe prüfen |