mirror of
https://github.com/Heretek-AI/Strata.git
synced 2026-11-07 10:05:58 -10:00
135 lines
7.9 KiB
Docker
135 lines
7.9 KiB
Docker
# Strata container image, built from the engine COMPONENT PACKAGE: nothing is compiled on the user's side.
|
|
#
|
|
# one image = the Terminal (bundled Python 3.12 + strata_cli / strata_control / serve, built exactly as the release archive)
|
|
# + one engine package (CUDA 13, CUDA 12, HIP gfx120X, HIP gfx110X or SYCL) + the entrypoint
|
|
#
|
|
# Build it with docker/build.sh (it assembles the build context: the Terminal sources and the package folder):
|
|
#
|
|
# docker/build.sh --flavor cuda --engine /path/to/0.1.45.3-linux-x86_64-cuda-cuda13 --tag strata:0.2.7-cuda13
|
|
#
|
|
# Plain `docker build` also works when the context folder has this layout (build.sh makes it):
|
|
# terminal-src/ the files package_terminal.py needs (packaging/, strata_cli/, strata_control/, serve/, tools/, data/, ...)
|
|
# engine/ one folder per engine package, exactly as `package_engine.py` wrote it (engine-component.json inside)
|
|
# docker/ entrypoint.sh, strata-wrapper.sh, healthcheck.sh
|
|
# docker build --build-arg FLAVOR=cuda -t strata:0.2.7-cuda13 -f Dockerfile <context>
|
|
#
|
|
# FLAVOR picks the small set of system libraries the runtime image adds and its base:
|
|
# cuda NVIDIA: the engine package carries cudart/cublas; the driver's libcuda comes from nvidia-container-toolkit
|
|
# hip AMD: the engine package carries the ROCm runtime and kernels; the host's amdgpu driver and /dev/kfd are used
|
|
# sycl Intel: the engine package carries oneAPI's SYCL runtime; this image adds the Level Zero loader and Intel's compute runtime
|
|
# (Ubuntu 26.04 base: its compute runtime knows Arc A- and B-series; the 24.04 one does not know Battlemage)
|
|
#
|
|
# Only the legacy-builder-safe Dockerfile features are used (no heredocs, no --mount), so it builds with `docker build` as shipped
|
|
# in docker.io as well as with BuildKit.
|
|
|
|
ARG BASE_IMAGE=ubuntu:24.04
|
|
|
|
# ---- 1. the Terminal: the release archive, built here (pinned CPython 3.12 + hash-locked wheels, installed at build time only)
|
|
FROM ${BASE_IMAGE} AS terminal
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends python3 ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
COPY terminal-src/ /src/
|
|
# the sources have no .git here (and the image has no git): build.sh passes the commit they are from, package_terminal.py writes it to COMMIT
|
|
ARG STRATA_COMMIT=
|
|
ENV STRATA_BUILD_COMMIT=${STRATA_COMMIT}
|
|
RUN python3 /src/packaging/package_terminal.py --out /out --cache /cache \
|
|
&& mkdir -p /opt/strata \
|
|
&& tar -xzf /out/strata-terminal-*-linux-x86_64.tar.gz -C /opt/strata \
|
|
&& mv /opt/strata/strata-terminal-* /opt/strata/terminal \
|
|
&& /opt/strata/terminal/bin/strata --version
|
|
# llama.cpp at the commit setup.py pins: fetched here, so the first start does not depend on GitHub. Only gguf-py (the model preparation
|
|
# tools) and the file setup.py looks for (ggml/CMakeLists.txt) are kept: 170 MB become a few
|
|
RUN cd /opt/strata/terminal && STRATA_CONTROL_INSTALL=1 STRATA_INSTALL_DIR=/opt/strata/seed PYTHONNOUSERSITE=1 \
|
|
/opt/strata/terminal/python/bin/python3 -c "import setup; print(setup.get_llama_cpp())" \
|
|
&& rm -rf /opt/strata/seed/third_party/*.zip* \
|
|
&& cd /opt/strata/seed/third_party/llama.cpp \
|
|
&& find . -mindepth 1 -maxdepth 1 ! -name gguf-py ! -name ggml ! -name 'LICENSE*' -exec rm -rf {} + \
|
|
&& find ggml -mindepth 1 -depth ! -type d ! -name CMakeLists.txt -delete && find ggml -mindepth 1 -type d -empty -delete \
|
|
&& du -sh /opt/strata/seed
|
|
|
|
# ---- 2. the engine package, checked before it is allowed into the image: every file's hash, and every library it imports is
|
|
# bundled or belongs to the OS / GPU driver (packaging/binary_deps.py, the check the packager runs)
|
|
FROM ${BASE_IMAGE} AS verify
|
|
COPY --from=terminal /opt/strata/terminal /opt/strata/terminal
|
|
COPY terminal-src/packaging/package_engine.py terminal-src/packaging/binary_deps.py /opt/strata/terminal/packaging/
|
|
COPY engine/ /engine/
|
|
RUN set -e; n=0; for d in /engine/*/; do \
|
|
echo "== $d"; \
|
|
/opt/strata/terminal/python/bin/python3 /opt/strata/terminal/packaging/package_engine.py --verify "$d"; \
|
|
/opt/strata/terminal/python/bin/python3 /opt/strata/terminal/packaging/binary_deps.py "$d"; \
|
|
n=$((n+1)); \
|
|
done; [ "$n" -ge 1 ] || { echo "no engine package in the build context (engine/<package>/engine-component.json)"; exit 1; }
|
|
|
|
# ---- 3. the runtime image
|
|
FROM ${BASE_IMAGE}
|
|
ARG FLAVOR=cuda
|
|
ARG STRATA_VERSION=dev
|
|
ARG ENGINE_LABEL=
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# system libraries the engine packages leave to the OS (SYSTEM-LIBS.txt), plus tini (PID 1: forwards docker stop, reaps children)
|
|
RUN set -e; \
|
|
pkgs="tini ca-certificates libgomp1 libatomic1 libnuma1 zlib1g"; \
|
|
case "$FLAVOR" in \
|
|
cuda) ;; \
|
|
hip) pkgs="$pkgs libdrm2 libdrm-amdgpu1 libelf1t64 libtinfo6 libpci3" ;; \
|
|
sycl) pkgs="$pkgs libze1 libze-intel-gpu1 intel-opencl-icd" ;; \
|
|
*) echo "FLAVOR must be cuda, hip or sycl"; exit 1 ;; \
|
|
esac; \
|
|
apt-get update; apt-get install -y --no-install-recommends $pkgs; \
|
|
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
|
|
|
COPY --from=terminal /opt/strata/terminal /opt/strata/terminal
|
|
COPY --from=terminal /opt/strata/seed /opt/strata/seed
|
|
COPY --from=verify /engine/ /opt/strata/components/engine/
|
|
COPY docker/entrypoint.sh docker/healthcheck.sh /opt/strata/
|
|
COPY docker/strata-wrapper.sh /usr/local/bin/strata
|
|
RUN chmod 0755 /opt/strata/entrypoint.sh /opt/strata/healthcheck.sh /usr/local/bin/strata
|
|
|
|
# Does the dynamic linker find every library of the package inside this image? (the GPU driver's libcuda / libnvidia-* come from the host)
|
|
RUN set -e; \
|
|
for d in /opt/strata/components/engine/*/; do \
|
|
miss=$(find "$d" -type f \( -name 'strata*' -o -name '*.so' -o -name '*.so.*' \) ! -name '*.json' ! -name '*.txt' | while read -r f; do \
|
|
head -c4 "$f" | grep -q ELF || continue; \
|
|
LD_LIBRARY_PATH="$d" ldd "$f" 2>/dev/null | grep 'not found' | grep -v -E 'libcuda\.so|libnvidia|libnvcuvid|libze_intel_gpu|libhsa-runtime64|libdrm_amdgpu' | sed "s|^|$(basename "$f"): |"; \
|
|
done | sort -u); \
|
|
if [ -n "$miss" ]; then echo "MISSING LIBRARIES in $d"; echo "$miss"; exit 1; fi; \
|
|
echo "libraries of $d resolve inside this image"; \
|
|
done
|
|
|
|
# Everything that is yours lives in /data: model files, the prepared pack, presets, tuning results, the API key, logs.
|
|
# The engine and the Terminal are in the image; updating the image never touches /data.
|
|
ENV STRATA_COMPONENTS_DIR=/opt/strata/components \
|
|
STRATA_INSTALL_DIR=/data/install \
|
|
XDG_STATE_HOME=/data/state \
|
|
XDG_CONFIG_HOME=/data/config \
|
|
HOME=/data/home \
|
|
PORT=8080 \
|
|
LANG=C.UTF-8 PYTHONUNBUFFERED=1 PYTHONNOUSERSITE=1 \
|
|
STRATA_IN_DOCKER=1 \
|
|
NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
|
VOLUME ["/data"]
|
|
EXPOSE 8080
|
|
|
|
LABEL org.opencontainers.image.title="Strata" \
|
|
org.opencontainers.image.source="https://github.com/Niko1221/Strata" \
|
|
org.opencontainers.image.url="https://github.com/Niko1221/Strata" \
|
|
org.opencontainers.image.documentation="https://github.com/Niko1221/Strata/blob/main/docs/DOCKER.md" \
|
|
org.opencontainers.image.description="Strata local LLM server (Terminal + engine package), flavor ${FLAVOR}" \
|
|
org.opencontainers.image.version="${STRATA_VERSION}" \
|
|
org.opencontainers.image.licenses="see LICENSE" \
|
|
com.strata.flavor="${FLAVOR}" \
|
|
com.strata.engine="${ENGINE_LABEL}"
|
|
|
|
# The port only opens after the model is downloaded, prepared and loaded (minutes; hours for a first 70 GB download), so the
|
|
# start period is long: "starting" is not "unhealthy".
|
|
HEALTHCHECK --interval=30s --timeout=8s --start-period=2h --retries=3 CMD ["/opt/strata/healthcheck.sh"]
|
|
|
|
# `docker stop` sends SIGTERM to tini, which passes it to the entrypoint; the entrypoint runs `strata launch --stop` (drain, unload,
|
|
# free the GPU memory). Give it time: docker run --stop-timeout 120 (compose: stop_grace_period: 2m).
|
|
STOPSIGNAL SIGTERM
|
|
ENTRYPOINT ["/usr/bin/tini", "--", "/opt/strata/entrypoint.sh"]
|
|
CMD ["run"]
|