diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cef64db..fb1d03bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **Docker base image now uses a multi-stage build.** The builder stage installs compilers and dev headers (`gcc`, `g++`, `gfortran`, `build-essential`, `libopenblas-dev`, `libpcre3-dev`, `python3.13-dev`, `ninja-build`) to create the virtual environment and compile the legacy NumPy wheel (cpu-baseline=none for old hardware). The final runtime image starts from the same ffmpeg base, copies only the prebuilt venv and wheel from the builder, and installs only the runtime libraries needed at runtime - eliminating compiler binaries from production containers and reducing final image size. — Thanks [@kensac](https://github.com/kensac) +- **`libpq-dev` removed from both build and runtime stages.** The project uses `psycopg2-binary`, which bundles its own statically linked copy of libpq inside the wheel. No system libpq headers or shared library are needed at build or runtime. + ### Performance - **Reduced Redis round-trips on the Stats page channel status endpoint.** `get_basic_channel_info` was making up to 6 individual `HGET` calls per connected client plus a redundant `HGET` for `TOTAL_BYTES` (already present in the preceding `HGETALL` result). Client metadata is now fetched with a single `HMGET` per client, and `TOTAL_BYTES` is read from the already-fetched hash. Under load with many active streams this significantly reduces the time each uWSGI worker holds the GIL servicing the stats endpoint, reducing the chance of concurrent requests from other pages timing out with a 503. The same `HGET`-to-`HMGET` consolidation was applied to `stream_ts` and `get_user_active_connections`. The Stats page frontend was also fixed to fire the initial fetch only once on mount (previously two `useEffect` hooks both triggered an immediate fetch on load). — Thanks [@JCBird1012](https://github.com/JCBird1012) diff --git a/docker/DispatcharrBase b/docker/DispatcharrBase index 3040e189..bc2e9b4d 100644 --- a/docker/DispatcharrBase +++ b/docker/DispatcharrBase @@ -1,4 +1,10 @@ -FROM lscr.io/linuxserver/ffmpeg:latest +# ============================================================================== +# Stage 1: builder +# Installs the Python toolchain + compilers, creates the virtual environment and +# builds the legacy (CPU-baseline=none) NumPy wheel. None of these compilers end +# up in the final image — only the artifacts below are copied forward. +# ============================================================================== +FROM lscr.io/linuxserver/ffmpeg:latest AS builder ENV DEBIAN_FRONTEND=noninteractive ENV UV_PROJECT_ENVIRONMENT=/dispatcharrpy @@ -15,10 +21,9 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ && apt-get update \ && apt-get install --no-install-recommends -y \ python3.13 python3.13-dev python3.13-venv libpython3.13 \ - libpcre3 libpcre3-dev libpq-dev procps pciutils \ - nginx comskip \ - vlc-bin vlc-plugin-base \ - build-essential gcc g++ gfortran libopenblas-dev libopenblas0 ninja-build + libpcre3 libpcre3-dev \ + build-essential gcc g++ gfortran libopenblas-dev libopenblas0 ninja-build \ + && rm -rf /var/lib/apt/lists/* # --- Install UV --- COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ @@ -28,11 +33,12 @@ WORKDIR /tmp/build COPY pyproject.toml /tmp/build/ COPY version.py /tmp/build/ COPY README.md /tmp/build/ -RUN uv sync --python 3.13 --no-cache --no-install-project --no-dev && \ - rm -rf /tmp/build +RUN uv sync --python 3.13 --no-cache --no-install-project --no-dev WORKDIR / # --- Build legacy NumPy wheel for old hardware (store for runtime switching) --- +# build/pip are installed into the venv only long enough to produce the wheel, +# then uninstalled so they are not carried into the final image's venv. RUN uv pip install --python $UV_PROJECT_ENVIRONMENT/bin/python --no-cache build pip && \ cd /tmp && \ $UV_PROJECT_ENVIRONMENT/bin/python -m pip download --no-binary numpy --no-deps numpy && \ @@ -40,14 +46,44 @@ RUN uv pip install --python $UV_PROJECT_ENVIRONMENT/bin/python --no-cache build cd numpy-*/ && \ $UV_PROJECT_ENVIRONMENT/bin/python -m build --wheel -Csetup-args=-Dcpu-baseline="none" -Csetup-args=-Dcpu-dispatch="none" && \ mv dist/*.whl /opt/ && \ - cd / && rm -rf /tmp/numpy-* /tmp/*.tar.gz && \ + cd / && rm -rf /tmp/numpy-* /tmp/*.tar.gz /tmp/build && \ uv pip uninstall --python $UV_PROJECT_ENVIRONMENT/bin/python build pip -# --- Clean up build dependencies to reduce image size --- -RUN apt-get remove -y build-essential gcc g++ gfortran libopenblas-dev libpcre3-dev python3.13-dev ninja-build && \ - apt-get autoremove -y --purge && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /root/.cache /tmp/* +# ============================================================================== +# Stage 2: final runtime image +# Same ffmpeg base, but installs only the runtime system libraries (no compilers) +# and copies the prebuilt virtual environment + NumPy wheel from the builder. +# ============================================================================== +FROM lscr.io/linuxserver/ffmpeg:latest AS final + +ENV DEBIAN_FRONTEND=noninteractive +ENV UV_PROJECT_ENVIRONMENT=/dispatcharrpy +ENV VIRTUAL_ENV=/dispatcharrpy +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +ENV UV_COMPILE_BYTECODE=1 +ENV UV_LINK_MODE=copy + +# --- Install runtime system dependencies (no build toolchain) --- +# python3.13 + libpython3.13 back the copied venv; libpcre3 backs uWSGI; +# libopenblas0 backs the legacy NumPy wheel; ca-certificates/gnupg2/curl/wget +# and software-properties-common are kept for TLS and the AIO runtime apt step. +RUN apt-get update && apt-get install --no-install-recommends -y \ + ca-certificates software-properties-common gnupg2 curl wget \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update \ + && apt-get install --no-install-recommends -y \ + python3.13 python3.13-venv libpython3.13 \ + libpcre3 libopenblas0 procps pciutils \ + nginx comskip \ + vlc-bin vlc-plugin-base \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# --- Install UV (used at runtime to swap in the legacy NumPy wheel) --- +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +# --- Copy prebuilt virtual environment and legacy NumPy wheel from the builder --- +COPY --from=builder /dispatcharrpy /dispatcharrpy +COPY --from=builder /opt/numpy-*.whl /opt/ # --- Set up Redis 7.x --- RUN curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg && \