112 lines
4.9 KiB
Docker
112 lines
4.9 KiB
Docker
# syntax=docker/dockerfile:1
|
||
|
||
# SwyxWeb als ein Container: Das React-Frontend wird gebaut, in die statischen
|
||
# Ressourcen des Backends gelegt und zusammen mit ihm in ein Jar gepackt.
|
||
# Ausgeliefert wird am Ende alles über den einen Port des Backends (8080).
|
||
#
|
||
# Der eingebaute SwyxTray-Mock (/ws und /api/mock/**) läuft NICHT mit: seine
|
||
# Bohnen hängen am Spring-Profil "mock", und das setzt dieses Image nicht.
|
||
#
|
||
# Die beiden Build-Stufen laufen bewusst auf $BUILDPLATFORM, also nativ auf dem
|
||
# bauenden Rechner: JS-Bundle und Jar sind architekturunabhängig. Nur die
|
||
# Laufzeitstufe nutzt die Zielarchitektur.
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 1. Frontend bauen (Vite -> frontend/dist)
|
||
# ---------------------------------------------------------------------------
|
||
FROM --platform=$BUILDPLATFORM node:22-alpine AS frontend
|
||
WORKDIR /build
|
||
|
||
# Erst die Abhängigkeiten, damit diese Schicht im Cache bleibt, solange sich
|
||
# package.json/package-lock.json nicht ändern.
|
||
COPY frontend/package.json frontend/package-lock.json ./
|
||
RUN npm ci
|
||
|
||
COPY frontend/ ./
|
||
# VITE_WS_URL nur setzen, wenn die Adresse fest ins Bundle soll; im Normalfall
|
||
# kommt sie zur Laufzeit aus /api/config (siehe README).
|
||
ARG VITE_WS_URL
|
||
RUN npm run build
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 2. Backend bauen (Spring Boot 4.1, Java 21) inkl. Frontend als static/
|
||
# ---------------------------------------------------------------------------
|
||
FROM --platform=$BUILDPLATFORM maven:3.9-eclipse-temurin-21 AS backend
|
||
WORKDIR /build
|
||
|
||
# Abhängigkeiten vorab auflösen – eigene Schicht, unabhängig vom Quelltext.
|
||
COPY backend/pom.xml ./
|
||
RUN --mount=type=cache,target=/root/.m2 \
|
||
mvn -B -q dependency:go-offline
|
||
|
||
COPY backend/src ./src
|
||
COPY --from=frontend /build/dist/ ./src/main/resources/static/
|
||
|
||
RUN --mount=type=cache,target=/root/.m2 \
|
||
mvn -B -DskipTests clean package \
|
||
&& cp target/*.jar /build/app.jar
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 3. Whisper-Modell einmalig ins Image laden, unabhängig von der Zielarchitektur.
|
||
# ---------------------------------------------------------------------------
|
||
FROM --platform=$BUILDPLATFORM python:3.12-slim-bookworm AS whisper-model
|
||
ENV HF_HUB_DISABLE_TELEMETRY=1
|
||
RUN pip install --no-cache-dir huggingface-hub==2.3.0
|
||
ARG WHISPER_MODEL_REPO=mobiuslabsgmbh/faster-whisper-large-v3-turbo
|
||
ARG WHISPER_MODEL_REVISION=0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf
|
||
RUN python -c 'import os; from huggingface_hub import snapshot_download; snapshot_download(os.environ["WHISPER_MODEL_REPO"], revision=os.environ["WHISPER_MODEL_REVISION"], local_dir="/models/whisper", allow_patterns=["*.json", "model.bin", "vocabulary.*", "README.md"])' \
|
||
&& rm -rf /models/whisper/.cache
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4. Java + lokaler Whisper-Worker (glibc für CTranslate2/PyAV statt Alpine).
|
||
# ---------------------------------------------------------------------------
|
||
FROM eclipse-temurin:21-jre-noble AS runtime
|
||
|
||
RUN apt-get update \
|
||
&& apt-get install -y --no-install-recommends python3 python3-venv libgomp1 curl tini \
|
||
&& rm -rf /var/lib/apt/lists/* \
|
||
&& python3 -m venv /opt/whisper
|
||
ENV PATH="/opt/whisper/bin:$PATH"
|
||
COPY transcription/requirements.txt /app/transcription/requirements.txt
|
||
RUN pip install --no-cache-dir -r /app/transcription/requirements.txt && pip check
|
||
|
||
# Nicht als root laufen.
|
||
RUN groupadd --gid 10001 swyx \
|
||
&& useradd --uid 10001 --gid swyx --create-home swyx \
|
||
&& mkdir -p /data/transcriptions \
|
||
&& chown -R swyx:swyx /data
|
||
WORKDIR /app
|
||
|
||
COPY --from=backend --chown=swyx:swyx /build/app.jar ./app.jar
|
||
COPY --from=whisper-model /models/ ./models/
|
||
COPY transcription/service.py transcription/summary.py ./transcription/
|
||
COPY --chmod=755 docker/entrypoint.sh ./entrypoint.sh
|
||
|
||
USER swyx
|
||
EXPOSE 8080
|
||
VOLUME ["/data/transcriptions"]
|
||
|
||
# Adresse der SwyxTray-App zur Laufzeit überschreibbar (Spring Relaxed Binding):
|
||
# -e APP_WEBSOCKET_HOST=127.0.0.1 -e APP_WEBSOCKET_PORT=17654
|
||
# -e APP_WEBSOCKET_PATH=/ws -e APP_WEBSOCKET_SECURE=false
|
||
#
|
||
# SPRING_PROFILES_ACTIVE bleibt bewusst leer – der SwyxTray-Mock bliebe sonst
|
||
# beim Ausrollen erreichbar. Für eine Vorführung ohne Telefonanlage lässt er
|
||
# sich einzeln einschalten: -e SPRING_PROFILES_ACTIVE=mock
|
||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=25.0" \
|
||
APP_TRANSCRIPTION_BASE_URL=http://127.0.0.1:8765 \
|
||
WHISPER_MODEL_PATH=/app/models/whisper \
|
||
WHISPER_DATA_DIR=/data/transcriptions \
|
||
WHISPER_THREADS=4 \
|
||
PYTHONDONTWRITEBYTECODE=1 \
|
||
PYTHONUNBUFFERED=1 \
|
||
HF_HUB_OFFLINE=1 \
|
||
HF_HUB_DISABLE_TELEMETRY=1 \
|
||
TOKENIZERS_PARALLELISM=false
|
||
|
||
HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --retries=3 \
|
||
CMD curl -fsS --max-time 2 http://127.0.0.1:8080/api/config >/dev/null \
|
||
&& curl -fsS --max-time 2 http://127.0.0.1:8765/health >/dev/null || exit 1
|
||
|
||
ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint.sh"]
|