Files
ApiCore/ApiCore-Whisper/Dockerfile
T
Nick SzaunigandClaude Opus 5.5 c53810b8a6
Backend Image / build-and-push (push) Successful in 5m49s
Frontend Image / build-and-push (push) Successful in 13m3s
Manifests / validate (push) Successful in 17s
Whisper Image / build-and-push (push) Successful in 1m56s
feat: Abschreiben im Cluster mit Whisper-Image und Voice-CronJob (T7 L3 Task 13)
ApiCore-Whisper: transcribe.py liest das Manifest im RAM-Arbeitsordner,
dekodiert mit festem Demuxer je Endung, misst die Länge vor dem
Abschreiben (TOO_LONG > 240 s, DECODE_FAILED bei Länge 0), sperrt das
Netz vor dem Modell-Laden und schreibt nur Zähler auf stdout. Image:
python:3.12-slim per Digest, Nutzer 1000, alle Pakete mit Hash
(faster-whisper 1.2.1, av 18.1.0 wegen Inkompatibilität mit av 19),
Modell faster-whisper-small auf fester Revision mit SHA-256 je Datei.
CI-Workflow nur bei Änderungen unter ApiCore-Whisper/, Actions per SHA.

deploy/k8s/components/voice: CronJob alle 2 Minuten mit fetch, whisper,
ingest (gleiche UID, getrennte /tmp-Volumes, damit der Decoder dem
Container mit den Secrets nichts unterschieben kann; whisper ohne
Secrets und Umgebungsvariablen außer WHISPER_CPU_THREADS), Overlay mit
Platzhalter-Digest, ConfigMap-Werte für Sprache, Policy-Tests (55).
Die Anbindung des Anthropic-Schlüssels bleibt offen (Ruling 9).

Refs #27

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 11:12:59 +02:00

59 lines
2.7 KiB
Docker

# syntax=docker/dockerfile:1
# Abschreib-Image fuer den CronJob apicore-job-voice-transcribe (Spec T7 6.3,
# 11.1). Zur Laufzeit ohne Netz, ohne Schluessel, ohne Datenbank: das Image
# enthaelt Python, faster-whisper, PyAV und das Modell `small`, sonst nichts.
#
# Base-Image per Digest gepinnt (python:3.12-slim, Multi-Arch-Index vom
# 2026-10-16, Python 3.12.15). Ein Update ist ein bewusster Commit mit neuem
# Digest, ermittelt per
# docker buildx imagetools inspect python:3.12-slim
ARG BASE_IMAGE=python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1
# --- Modell: feste Revision, jede Datei gegen ihre SHA-256 geprueft ---------
FROM ${BASE_IMAGE} AS model
WORKDIR /build
COPY fetch_model.py ./
RUN python fetch_model.py /models/small
# --- Abhaengigkeiten: nur Wheels, nur mit Hash (requirements.txt) -----------
FROM ${BASE_IMAGE} AS deps
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 PIP_NO_CACHE_DIR=1
RUN python -m venv /opt/venv
COPY requirements.txt /tmp/requirements.txt
RUN /opt/venv/bin/pip install --require-hashes --only-binary=:all: -r /tmp/requirements.txt
# --- Tests: ohne bestandene Tests entsteht kein Image -----------------------
FROM deps AS test
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 PIP_NO_CACHE_DIR=1 PYTHONDONTWRITEBYTECODE=1
COPY requirements.txt requirements-test.txt /tmp/
RUN /opt/venv/bin/pip install --require-hashes --only-binary=:all: -r /tmp/requirements.txt -r /tmp/requirements-test.txt
WORKDIR /app
COPY transcribe.py fetch_model.py vocabulary.txt ./
COPY tests ./tests
RUN /opt/venv/bin/python -m pytest -q -p no:cacheprovider tests && touch /app/.tests-passed
# --- Laufzeit ---------------------------------------------------------------
# Kein PYTHONUNBUFFERED/PYTHONDONTWRITEBYTECODE: `python -I` ignoriert PYTHON*-
# Variablen; die Ausgabe sind wenige Zaehlerzeilen am Ende des Laufs.
FROM ${BASE_IMAGE} AS runtime
# uid/gid 1000 wie die Backend-Container: fetch, whisper und ingest teilen sich
# das Arbeitsverzeichnis (Modus 0700/0600) und brauchen dieselbe Kennung.
RUN groupadd --gid 1000 whisper \
&& useradd --uid 1000 --gid 1000 --no-create-home --home-dir /tmp --shell /usr/sbin/nologin whisper
ENV PATH=/opt/venv/bin:$PATH \
HOME=/tmp \
HF_HOME=/tmp/hf \
HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
WHISPER_MODEL_DIR=/models/small
COPY --from=deps /opt/venv /opt/venv
COPY --from=model /models /models
WORKDIR /app
COPY transcribe.py vocabulary.txt ./
# Haengt die Laufzeit an die Test-Stufe (BuildKit baut sonst nur, was gebraucht
# wird), ohne dass etwas davon im Image landet.
RUN --mount=type=bind,from=test,source=/app/.tests-passed,target=/mnt/tests-passed true
USER 1000:1000
ENTRYPOINT ["python", "-I", "/app/transcribe.py"]