refactor(docker): optimize multi-stage build with cargo-chef and layer caching

Improves Docker build performance and security through better layer caching,
dependency pre-building, and a minimal non-root runtime container.

- Add cargo-chef for Rust dependency caching across demo and server builds
- Separate planner and builder stages for optimal layer reuse
- Use pnpm with frozen lockfile for reproducible frontend builds
- Switch to debian:12-slim runtime with non-root user (uid 1000)
- Add health check endpoint monitoring
- Strip release binaries to reduce image size
- Pre-compress frontend assets during build
This commit is contained in:
2025-12-11 12:18:57 -06:00
parent 006055cb7f
commit 702205e181
+112 -39
View File
@@ -1,60 +1,133 @@
# Build the demo application
FROM rust:latest AS builder-demo
# syntax=docker/dockerfile:1
ARG RUST_VERSION=1.86.0
WORKDIR /build/demo
RUN apt update && apt install -y g++-mingw-w64-x86-64
ARG RAILWAY_PUBLIC_DOMAIN
# --- Chef Base Stage ---
FROM lukemathwalker/cargo-chef:latest-rust-${RUST_VERSION} AS chef
WORKDIR /app
RUN rustup target add x86_64-pc-windows-gnu
RUN rustup target add x86_64-unknown-linux-gnu
# TODO: Add support for macOS
# RUN rustup target add x86_64-apple-darwin
# --- Demo Planner Stage ---
FROM chef AS demo-planner
COPY demo/Cargo.toml demo/Cargo.lock* demo/build.rs ./
COPY demo/src ./src
RUN cargo chef prepare --recipe-path recipe.json
COPY ./demo ./
# --- Demo Builder Stage ---
FROM chef AS demo-builder
RUN cargo build --release --target x86_64-pc-windows-gnu
RUN cargo build --release --target x86_64-unknown-linux-gnu
# RUN cargo build --release --target x86_64-apple-darwin
# Install cross-compilation toolchain for Windows
RUN apt-get update && apt-get install -y \
g++-mingw-w64-x86-64 \
&& rm -rf /var/lib/apt/lists/*
# Build the server application
FROM rust:alpine AS builder-server
# Add cross-compilation targets
RUN rustup target add x86_64-pc-windows-gnu x86_64-unknown-linux-gnu
RUN apk update && apk add musl-dev
WORKDIR /build/server
# Copy recipe and cook dependencies
COPY --from=demo-planner /app/recipe.json recipe.json
RUN cargo chef cook --release --target x86_64-unknown-linux-gnu --recipe-path recipe.json
RUN cargo chef cook --release --target x86_64-pc-windows-gnu --recipe-path recipe.json
COPY ./src ./src
COPY ./Cargo.toml ./Cargo.lock ./
RUN cargo build --release
# Build the Astro frontend
FROM node:lts AS builder-astro
WORKDIR /build/astro
COPY ./frontend/ ./
# Copy source and build
COPY demo/Cargo.toml demo/Cargo.lock* demo/build.rs ./
COPY demo/src ./src
ARG RAILWAY_PUBLIC_DOMAIN
ENV RAILWAY_PUBLIC_DOMAIN=${RAILWAY_PUBLIC_DOMAIN}
RUN npm install pnpm -g
RUN pnpm install
RUN pnpm build
RUN ./compress.sh
RUN cargo build --release --target x86_64-unknown-linux-gnu
RUN cargo build --release --target x86_64-pc-windows-gnu
# Run the server application
FROM alpine:latest
# Strip binaries
RUN strip target/x86_64-unknown-linux-gnu/release/demo
# --- Server Planner Stage ---
FROM chef AS server-planner
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo chef prepare --recipe-path recipe.json
# --- Server Builder Stage ---
FROM chef AS server-builder
# Copy recipe and cook dependencies
COPY --from=server-planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
# Copy source and build
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
# Strip binary
RUN strip target/release/dynamic-preauth
# --- Frontend Builder Stage ---
FROM node:22-slim AS frontend-builder
WORKDIR /app
COPY --from=builder-astro /build/astro/dist/ ./public/
COPY --from=builder-demo /build/demo/target/x86_64-pc-windows-gnu/release/demo.exe ./demo.exe
COPY --from=builder-demo /build/demo/target/x86_64-unknown-linux-gnu/release/demo ./demo-linux
COPY --from=builder-server /build/server/target/release/dynamic-preauth ./dynamic-preauth
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9 --activate
# Copy package files for layer caching
COPY frontend/package.json frontend/pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source and build
COPY frontend/ ./
ARG RAILWAY_PUBLIC_DOMAIN
ENV RAILWAY_PUBLIC_DOMAIN=${RAILWAY_PUBLIC_DOMAIN}
RUN pnpm build
# Pre-compress static assets
RUN ./compress.sh
# --- Runtime Stage ---
FROM debian:12-slim
ARG APP=/app
ARG APP_USER=appuser
ARG UID=1000
ARG GID=1000
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
tzdata \
wget \
&& rm -rf /var/lib/apt/lists/*
ARG TZ=Etc/UTC
ENV TZ=${TZ}
# Create non-root user
RUN addgroup --gid $GID $APP_USER \
&& adduser --uid $UID --disabled-password --gecos "" --ingroup $APP_USER $APP_USER \
&& mkdir -p ${APP}
WORKDIR ${APP}
# Copy built artifacts
COPY --from=frontend-builder --chown=$APP_USER:$APP_USER /app/dist/ ./public/
COPY --from=demo-builder --chown=$APP_USER:$APP_USER /app/target/x86_64-pc-windows-gnu/release/demo.exe ./demo.exe
COPY --from=demo-builder --chown=$APP_USER:$APP_USER /app/target/x86_64-unknown-linux-gnu/release/demo ./demo-linux
COPY --from=server-builder --chown=$APP_USER:$APP_USER /app/target/release/dynamic-preauth ./dynamic-preauth
# Set proper permissions
RUN chmod +x ${APP}/dynamic-preauth
USER $APP_USER
# Build-time arg for PORT, default to 5800
ARG PORT=5800
# Runtime environment var for PORT, default to build-time arg
ENV PORT=${PORT}
EXPOSE ${PORT}
CMD ["/app/dynamic-preauth"]
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT}/session || exit 1
CMD ["./dynamic-preauth"]