|
| 1 | +# syntax=docker/dockerfile:1 |
| 2 | +# |
| 3 | +# Multi-stage build for the RustTraining book collection. |
| 4 | +# |
| 5 | +# Stage 1 (builder) — Rust toolchain + mdbook + mdbook-mermaid, runs |
| 6 | +# `cargo xtask build` to produce site/. |
| 7 | +# Stage 2 (runtime) — unprivileged nginx serving the static output. |
| 8 | +# No Rust, no mdbook, no source in the final image. |
| 9 | +# |
| 10 | +# Build from the REPOSITORY ROOT, not from docker/: |
| 11 | +# docker build -f docker/Dockerfile -t rust-training . |
| 12 | + |
| 13 | +ARG RUST_VERSION=1 |
| 14 | +ARG MDBOOK_VERSION=0.4.52 |
| 15 | +ARG MDBOOK_MERMAID_VERSION=0.14.0 |
| 16 | +ARG NGINX_VERSION=1.27 |
| 17 | + |
| 18 | +# ────────────────────────────────────────────────────────────────────── |
| 19 | +# Stage 1: build the books |
| 20 | +# ────────────────────────────────────────────────────────────────────── |
| 21 | +FROM rust:${RUST_VERSION}-slim-bookworm AS builder |
| 22 | + |
| 23 | +ARG MDBOOK_VERSION |
| 24 | +ARG MDBOOK_MERMAID_VERSION |
| 25 | +ARG TARGETARCH |
| 26 | + |
| 27 | +WORKDIR /build |
| 28 | + |
| 29 | +RUN apt-get update \ |
| 30 | + && apt-get install -y --no-install-recommends ca-certificates curl \ |
| 31 | + && rm -rf /var/lib/apt/lists/* |
| 32 | + |
| 33 | +# Prefer prebuilt release binaries — `cargo install mdbook mdbook-mermaid` |
| 34 | +# compiles both from source and adds several minutes to every cold build. |
| 35 | +# |
| 36 | +# Upstream does not ship a complete set of prebuilt targets, so we fall back |
| 37 | +# to compiling when an asset is missing: |
| 38 | +# - mdbook has linux-gnu on amd64, linux-musl on arm64 |
| 39 | +# - mdbook-mermaid has NO published arm64 Linux binary at all |
| 40 | +# musl builds are statically linked and run fine on this glibc base image. |
| 41 | +RUN set -eux; \ |
| 42 | + case "${TARGETARCH:-amd64}" in \ |
| 43 | + amd64) mdbook_triple=x86_64-unknown-linux-gnu; mermaid_triple=x86_64-unknown-linux-gnu ;; \ |
| 44 | + arm64) mdbook_triple=aarch64-unknown-linux-musl; mermaid_triple=aarch64-unknown-linux-musl ;; \ |
| 45 | + *) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ |
| 46 | + esac; \ |
| 47 | + \ |
| 48 | + install_tool() { \ |
| 49 | + bin="$1"; url="$2"; crate="$3"; ver="$4"; \ |
| 50 | + if curl -fsSL "$url" 2>/dev/null | tar -xz -C /usr/local/bin "$bin" 2>/dev/null; then \ |
| 51 | + echo "==> installed $bin from prebuilt binary"; \ |
| 52 | + else \ |
| 53 | + echo "==> no prebuilt $bin for ${TARGETARCH}, compiling from source"; \ |
| 54 | + cargo install "$crate" --version "$ver" --locked --root /usr/local; \ |
| 55 | + fi; \ |
| 56 | + }; \ |
| 57 | + \ |
| 58 | + install_tool mdbook \ |
| 59 | + "https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/mdbook-v${MDBOOK_VERSION}-${mdbook_triple}.tar.gz" \ |
| 60 | + mdbook "${MDBOOK_VERSION}"; \ |
| 61 | + install_tool mdbook-mermaid \ |
| 62 | + "https://github.com/badboy/mdbook-mermaid/releases/download/v${MDBOOK_MERMAID_VERSION}/mdbook-mermaid-v${MDBOOK_MERMAID_VERSION}-${mermaid_triple}.tar.gz" \ |
| 63 | + mdbook-mermaid "${MDBOOK_MERMAID_VERSION}"; \ |
| 64 | + \ |
| 65 | + mdbook --version; \ |
| 66 | + mdbook-mermaid --version |
| 67 | + |
| 68 | +# Copy the whole workspace. xtask resolves the project root from |
| 69 | +# CARGO_MANIFEST_DIR at compile time, so it must be built and run in place. |
| 70 | +COPY . . |
| 71 | + |
| 72 | +# `cargo xtask build` writes to site/ (the deploy target, docs/, is for |
| 73 | +# GitHub Pages and additionally prints commit instructions we don't want here). |
| 74 | +RUN --mount=type=cache,target=/usr/local/cargo/registry \ |
| 75 | + --mount=type=cache,target=/build/target \ |
| 76 | + cargo run --release --package xtask -- build \ |
| 77 | + && test -f site/index.html \ |
| 78 | + && echo "==> built $(find site -mindepth 1 -maxdepth 1 -type d | wc -l) books" |
| 79 | + |
| 80 | +# ────────────────────────────────────────────────────────────────────── |
| 81 | +# Stage 2: serve |
| 82 | +# ────────────────────────────────────────────────────────────────────── |
| 83 | +# nginx-unprivileged is the stock nginx image reconfigured to run as uid 101 |
| 84 | +# and listen on 8080, so the container needs no root and no NET_BIND_SERVICE. |
| 85 | +FROM nginxinc/nginx-unprivileged:${NGINX_VERSION}-alpine AS runtime |
| 86 | + |
| 87 | +COPY docker/nginx.conf /etc/nginx/conf.d/default.conf |
| 88 | +COPY --from=builder --chown=nginx:nginx /build/site /usr/share/nginx/html |
| 89 | + |
| 90 | +EXPOSE 8080 |
| 91 | + |
| 92 | +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ |
| 93 | + CMD wget -q -O /dev/null http://127.0.0.1:8080/ || exit 1 |
0 commit comments