# VLog Remote Transcoding Worker (CPU-only) # Containerized worker for distributed video transcoding # # Build: docker build -f Dockerfile.worker-cpu-only -t vlog-worker --build-arg CODE_VERSION=$(git rev-parse --short HEAD) . # Run: docker run -e VLOG_WORKER_API_KEY= -e VLOG_WORKER_API_URL=http://host:9002 vlog-worker # Pin base image version for reproducible builds # python:3.12-slim-trixie is Debian Trixie (13) based ARG PYTHON_VERSION=3.12-slim-trixie # Code version for worker compatibility checking # Set this during build: docker build --build-arg CODE_VERSION=$(git rev-parse --short HEAD) ... ARG CODE_VERSION=dev ARG BUILD_TIMESTAMP="" # Build the signed upstream FFmpeg release: Debian's FFmpeg is behind security fixes. FROM python:${PYTHON_VERSION} AS ffmpeg-builder ARG FFMPEG_VERSION=9.0.1 RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ build-essential pkg-config nasm curl gnupg libx264-dev libx265-dev libsvtav1enc-dev libdav1d-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /build/ffmpeg RUN curl -fsSLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz \ && curl -fsSLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz.asc \ && curl -fsSLo release-key.asc https://ffmpeg.org/ffmpeg-devel.asc \ && gpg --batch --import release-key.asc \ && gpg --batch --status-fd 1 --verify ffmpeg-${FFMPEG_VERSION}.tar.xz.asc ffmpeg-${FFMPEG_VERSION}.tar.xz \ > signature.status \ && grep '^\[GNUPG:\] VALIDSIG FCF986EA15E6E293A5644F10B4322F04D67658D8 ' signature.status \ && tar xf ffmpeg-${FFMPEG_VERSION}.tar.xz --strip-components=1 # Files are downloaded by the worker API client; FFmpeg needs no network protocols. RUN ./configure --prefix=/opt/ffmpeg --disable-shared --enable-static \ --disable-autodetect --enable-gpl --enable-libx264 --enable-libx265 --enable-libsvtav1 --enable-libdav1d \ --disable-doc --disable-ffplay --disable-debug --disable-network --disable-devices \ && make -j4 && make install \ && mkdir -p /opt/ffmpeg/licenses && cp COPYING.* /opt/ffmpeg/licenses/ # ============================================================================= # Stage 1: Builder - Install Python dependencies # ============================================================================= FROM python:${PYTHON_VERSION} AS builder # Install build dependencies for packages with C extensions (argon2-cffi) # These are only needed in the builder stage, not runtime RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ gcc \ libffi-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /build # Install the same dependency manifest used by the APIs and CI. COPY pyproject.toml requirements.lock README.md ./ RUN pip3 install --no-cache-dir --prefix=/install --require-hashes -r requirements.lock # Copy source code and install the package COPY config.py ./ COPY code_version.py ./ COPY api/ api/ COPY worker/ worker/ COPY cli/ cli/ COPY backup/ backup/ RUN pip install --no-cache-dir --no-deps --prefix=/install . # ============================================================================= # Stage 2: Runtime - Minimal production image # ============================================================================= FROM python:${PYTHON_VERSION} AS runtime-files # Runtime assembly keeps only required binaries/libraries, not the package tools. # PostgreSQL clients support the application's backup and restore endpoints. RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ libx264-164 libx265-dev libsvtav1enc-dev libdav1d7 postgresql-client-17 \ && rm -rf /var/lib/apt/lists/* \ && ln -s /usr/lib/postgresql/17/bin/pg_dump /usr/local/bin/pg_dump \ && ln -s /usr/lib/postgresql/17/bin/pg_restore /usr/local/bin/pg_restore COPY --from=ffmpeg-builder /opt/ffmpeg/bin/ /usr/local/bin/ COPY --from=ffmpeg-builder /opt/ffmpeg/licenses/ /usr/share/doc/vlog-ffmpeg/ # Create non-root user for security RUN useradd -m -u 1000 -s /bin/bash vlog WORKDIR /app # Copy installed Python packages from builder COPY --from=builder /install /usr/local # Copy source code COPY --chown=vlog:vlog pyproject.toml ./ COPY --chown=vlog:vlog config.py ./ COPY --chown=vlog:vlog code_version.py ./ COPY --chown=vlog:vlog api/ api/ COPY --chown=vlog:vlog worker/ worker/ COPY --chown=vlog:vlog cli/ cli/ COPY --chown=vlog:vlog backup/ backup/ # Re-declare build args for use in this stage (args don't persist across stages) ARG CODE_VERSION=dev ARG BUILD_TIMESTAMP="" # Switch to non-root user USER vlog # Create work directory RUN mkdir -p /tmp/vlog-worker # Assemble only the Python/media runtime and its shared-library closure. Keep # Debian package provenance and licenses so scanners still inspect shipped code. USER root COPY scripts/assemble-worker-runtime.py /build/assemble-worker-runtime.py RUN python /build/assemble-worker-runtime.py FROM scratch AS runtime COPY --from=runtime-files /runtime/ / COPY --from=runtime-files --chown=1000:1000 /app/ /app/ COPY --from=ffmpeg-builder /opt/ffmpeg/licenses/ /usr/share/doc/vlog-ffmpeg/ ARG CODE_VERSION=dev ARG BUILD_TIMESTAMP="" WORKDIR /app USER 1000:1000 ENV PATH=/usr/local/bin \ LD_LIBRARY_PATH=/usr/local/lib \ SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ LANG=C.UTF-8 # Environment variables with defaults # VLOG_CODE_VERSION and VLOG_BUILD_TIMESTAMP are used for worker compatibility checking ENV VLOG_WORKER_API_URL=http://vlog-worker-api:9002 \ VLOG_WORKER_WORK_DIR=/tmp/vlog-worker \ VLOG_WORKER_HEARTBEAT_INTERVAL=30 \ VLOG_WORKER_POLL_INTERVAL=10 \ VLOG_CODE_VERSION=${CODE_VERSION} \ VLOG_BUILD_TIMESTAMP=${BUILD_TIMESTAMP} \ PYTHONUNBUFFERED=1 # Health check - verify worker's health server is responding # The worker exposes /health on port 8080 which verifies: # - Worker process is alive # - API connection is working # - FFmpeg is available HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health', timeout=5)"] # Run the remote transcoder CMD ["python", "-m", "worker.remote_transcoder"]