5.6 KiB · text History d9ed3b6
# Built with `podman build` / `podman compose build` locally and Kaniko in
# CI. Stays Kaniko-clean: no `# syntax=docker/dockerfile:…` directive (Kaniko
# ignores it) and no BuildKit cache mounts (Kaniko unmounts them between
# RUN steps, so subsequent `cp` from the cached target/ would miss).

# ---------------------------------------------------------------------------
# Stage 1 — assets: compile Tailwind CSS, bundle JS, and stage FontAwesome webfonts.
# ---------------------------------------------------------------------------
FROM node:20-alpine AS assets
WORKDIR /app
# Install JS deps first so this layer caches across source changes.
COPY assets/package.json assets/package-lock.json ./assets/
RUN cd assets && npm ci --no-audit --no-fund

# Tailwind scans templates/ — bring it in too.
COPY assets/f ./assets/f
COPY templates ./templates

RUN cd assets \
 && ./node_modules/.bin/tailwindcss -i f/css/style.css -o f/css/app.css \
 && ./node_modules/.bin/esbuild f/js/src.js --bundle --minify --outfile=f/js/app.js \
 && mkdir -p f/webfonts \
 && cp node_modules/@fortawesome/fontawesome-free/webfonts/*.woff2 f/webfonts/ \
 && cp node_modules/@fortawesome/fontawesome-free/webfonts/*.ttf  f/webfonts/

# ---------------------------------------------------------------------------
# Stage 2 — rust-builder: build all three binaries in release mode.
#
# Kept Kaniko-friendly: no BuildKit cache mounts (Kaniko unmounts them
# before subsequent steps, so cp-ing from a cached target/ would silently
# miss the binaries). Layer caching from the COPY ordering below
# (Cargo.toml/Cargo.lock first, then src) still helps local rebuilds.
# ---------------------------------------------------------------------------
FROM rust:1.92-bookworm AS rust-builder
WORKDIR /app

RUN apt-get update \
 && apt-get install -y --no-install-recommends pkg-config libssl-dev ca-certificates \
 && rm -rf /var/lib/apt/lists/*

COPY Cargo.toml Cargo.lock build.rs ./
COPY migration/Cargo.toml migration/
COPY migration/src migration/src
COPY src src
COPY templates templates
# Tailwind output from stage 1 lives under assets/f — copy it in so build.rs
# (which embeds templates) and ServeDir at runtime have what they need.
COPY --from=assets /app/assets/f ./assets/f

RUN cargo build --release \
        --bin webservice \
        --bin queue-worker \
        --bin registration-token \
        --bin user-admin \
 && mkdir -p /out \
 && cp target/release/webservice /out/ \
 && cp target/release/queue-worker /out/ \
 && cp target/release/registration-token /out/ \
 && cp target/release/user-admin /out/ \
 && rm -rf target

# ---------------------------------------------------------------------------
# Stage 3 — ocr-models: pre-fetch the ocrs detection + recognition .rten
# models so the OCR worker has them baked into the image. Stage is separate
# so the (slow) downloads cache independently from the cargo build.
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim AS ocr-models
RUN apt-get update \
 && apt-get install -y --no-install-recommends ca-certificates curl \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /models
RUN curl -fL --retry 3 -o text-detection.rten   https://ocrs-models.s3-accelerate.amazonaws.com/text-detection.rten \
 && curl -fL --retry 3 -o text-recognition.rten https://ocrs-models.s3-accelerate.amazonaws.com/text-recognition.rten

# ---------------------------------------------------------------------------
# Stage 4 — runtime: minimal Debian slim with the binaries + assets + templates.
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim AS runtime
# ffmpeg is here for the `poster` worker, which lifts a still frame out of a
# video so the image-only categorizers have something to read. It is the
# heaviest thing in this layer; drop it only if video uploads go away.
RUN apt-get update \
 && apt-get install -y --no-install-recommends ca-certificates libssl3 ffmpeg \
 && rm -rf /var/lib/apt/lists/* \
 && groupadd --system --gid 10001 dms \
 && useradd  --system --uid 10001 --gid dms --home /app --shell /usr/sbin/nologin dms \
 && mkdir -p /app /data/storage /data/models \
 && chown -R dms:dms /app /data

WORKDIR /app
COPY --from=rust-builder /out/webservice          /usr/local/bin/dms-webservice
COPY --from=rust-builder /out/queue-worker        /usr/local/bin/dms-queue-worker
COPY --from=rust-builder /out/registration-token  /usr/local/bin/dms-registration-token
COPY --from=rust-builder /out/user-admin          /usr/local/bin/dms-user-admin
COPY --from=assets       /app/assets/f            /app/assets/f
COPY --chown=dms:dms     templates                /app/templates
# OCR models bundled at a fixed image path — read-only and tied to the app
# version. Override via env if you swap to bigger/smaller models later.
COPY --from=ocr-models   /models/text-detection.rten   /opt/dms/models/text-detection.rten
COPY --from=ocr-models   /models/text-recognition.rten /opt/dms/models/text-recognition.rten

USER dms

# Sensible defaults — override via `docker run -e ...` or compose.
ENV BIND_ADDRESS=0.0.0.0:3007 \
    STORAGE_ROOT=/data/storage \
    AUTO_MIGRATE=true \
    REGISTRATION_ENABLED=false \
    OCRS_DETECTION_MODEL=/opt/dms/models/text-detection.rten \
    OCRS_RECOGNITION_MODEL=/opt/dms/models/text-recognition.rten

EXPOSE 3007
VOLUME ["/data"]

# `docker run … dms-webservice`           → web (default)
# `docker run … dms-queue-worker --kind ocr` → categorizer
# `docker run … dms-registration-token …` → CLI
# `docker run … dms-user-admin …`         → user management CLI
CMD ["dms-webservice"]