diff --git a/tunnel-node/Dockerfile b/tunnel-node/Dockerfile index 801a0ac..c5329dd 100644 --- a/tunnel-node/Dockerfile +++ b/tunnel-node/Dockerfile @@ -1,12 +1,14 @@ -# syntax=docker/dockerfile:1 -# # Multi-stage build for the mhrv-tunnel-node service. # -# Build stage compiles a release binary against rust 1.85 (matches MSRV in -# Cargo.toml). Cargo's incremental build cache is mounted via BuildKit -# `--mount=type=cache` so a `docker build` against an unchanged dependency -# tree skips re-downloading + re-compiling crates — first build ~6 min, -# warm builds ~30 s. +# Build stage compiles a release binary on a recent stable Rust. +# Dependency caching is done via `cargo-chef`: a separate layer cooks +# just the dependencies first, so warm rebuilds where only `src/` +# changes reuse that layer and skip recompiling crates. +# +# This intentionally avoids BuildKit `--mount=type=cache` directives so +# the Dockerfile builds on classic Docker daemons too — notably Cloud +# Run's `gcloud run deploy --source .` builder, which does not enable +# BuildKit (see issue #620). # # Runtime stage is `debian:bookworm-slim` for libc compatibility (the # binary dynamically links against glibc) plus `ca-certificates` so HTTPS @@ -27,43 +29,28 @@ # `--health-cmd 'curl -fsS http://localhost:8080/ || exit 1'` on the # `docker run` if you want compose-level health gating. -FROM rust:1.85-slim AS builder +FROM rust:1.90-slim AS chef +RUN cargo install cargo-chef --locked --version 0.1.77 WORKDIR /app -# Copy lockfile so cargo uses pinned versions identically to local builds. + +FROM chef AS planner COPY Cargo.toml Cargo.lock ./ COPY src/ ./src/ -# BuildKit cache mounts: cargo's registry/git caches and the target/ -# directory persist across builds, dramatically speeding up rebuilds when -# only application code changes. -# -# `id=...-$TARGETPLATFORM` is load-bearing on multi-arch builds. Without -# it, BuildKit defaults to a single shared cache across architectures -# and the `linux/amd64` + `linux/arm64` jobs race on the same on-disk -# `/usr/local/cargo/registry/src/...//.cargo-ok` extraction. The -# second-arriving arch hits `File exists (os error 17)` mid-unpack and -# the whole multi-arch build fails. Per-platform cache id keeps each -# arch's cache isolated; warm-build speedup is preserved per-arch. -# `target` cache is also platform-scoped because target/ holds object -# files for one ABI and sharing them across arches would just produce -# misses or, worse, invalid linking. -ARG TARGETPLATFORM -RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETPLATFORM} \ - --mount=type=cache,target=/usr/local/cargo/git,id=cargo-git-${TARGETPLATFORM} \ - --mount=type=cache,target=/app/target,id=app-target-${TARGETPLATFORM} \ - cargo build --release --bin tunnel-node && \ +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json +COPY Cargo.toml Cargo.lock ./ +COPY src/ ./src/ +RUN cargo build --release --bin tunnel-node && \ cp /app/target/release/tunnel-node /usr/local/bin/tunnel-node FROM debian:bookworm-slim -# `ca-certificates` for HTTPS upstream targets; nothing else needed at -# runtime since the binary is statically linked against musl-equivalents -# only for the parts that don't touch glibc. RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates \ && rm -rf /var/lib/apt/lists/* -# Non-root runtime user. The service does no filesystem writes outside -# /tmp, so a static-uid unprivileged user is sufficient and prevents -# accidental host-FS writes if the container is volume-mounted. RUN useradd --system --uid 1000 --no-create-home --shell /usr/sbin/nologin tunnel COPY --from=builder /usr/local/bin/tunnel-node /usr/local/bin/tunnel-node