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]>
59 lines
2.7 KiB
Docker
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"]
|