Skip to content

Commit 4d2ce91

Browse files
dazzling-no-moreweb-flowclaude
authored
fix(docker): cargo-chef so tunnel-node builds without BuildKit (#620, #1117)
Fixes #620 — `tunnel-node/Dockerfile` used BuildKit-only `RUN --mount=type=cache` directives, breaking on Cloud Run's `gcloud run deploy --source .` path (the underlying `gcr.io/cloud-builders/docker` builder doesn't enable BuildKit, and `--set-build-env-vars DOCKER_BUILDKIT=1` doesn't flip it on either). Reworked to use **cargo-chef**: a dedicated planner stage emits `recipe.json` for dependency metadata, a `cargo chef cook` stage builds just the deps in their own Docker layer, the final build stage adds `src/` on top. Docker's regular layer cache handles dependency reuse — warm rebuilds where only `src/` changes still skip the slow crate compile. ## Changes (`tunnel-node/Dockerfile`-only) - Dropped `# syntax=docker/dockerfile:1` parser directive and all `RUN --mount=type=cache,...` blocks - Added cargo-chef multi-stage build (`chef` → `planner` → `builder`) - Pinned `cargo-chef` to exact `0.1.77` with `--locked` for reproducible installs - Bumped base from `rust:1.85-slim` → `rust:1.90-slim` (cargo-chef's transitive deps require rustc 1.86+; tunnel-node's `Cargo.toml` has no `rust-version` pin so the bump is internal-only) - Removed `ARG TARGETPLATFORM` per-platform cache-id workaround — Docker's regular layer cache is already arch-scoped ## Non-changes (deliberate) - `tunnel-node/Cargo.toml` left alone — the old Dockerfile comment claimed "matches MSRV in Cargo.toml" but no `rust-version` field actually exists. The Docker base bump is internal build-env, not a declared MSRV. - Base image digest pinning left on tag refs — without Renovate/Dependabot to keep digests fresh, pinning trades automatic glibc/openssl/ca-certificates CVE patching for a reproducibility property this repo doesn't currently need. ## Verified locally - `cd tunnel-node && cargo build --release`: clean (binary side unchanged) - `cd tunnel-node && cargo test --release`: 36/36 - Local `docker build` couldn't run (daemon not started on the dev machine); the PR author's test plan documents successful build under classic Docker daemon. Reviewed via Anthropic Claude. Co-Authored-By: dazzling-no-more <noreply@github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ab05c53 commit 4d2ce91

1 file changed

Lines changed: 21 additions & 34 deletions

File tree

tunnel-node/Dockerfile

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# syntax=docker/dockerfile:1
2-
#
31
# Multi-stage build for the mhrv-tunnel-node service.
42
#
5-
# Build stage compiles a release binary against rust 1.85 (matches MSRV in
6-
# Cargo.toml). Cargo's incremental build cache is mounted via BuildKit
7-
# `--mount=type=cache` so a `docker build` against an unchanged dependency
8-
# tree skips re-downloading + re-compiling crates — first build ~6 min,
9-
# warm builds ~30 s.
3+
# Build stage compiles a release binary on a recent stable Rust.
4+
# Dependency caching is done via `cargo-chef`: a separate layer cooks
5+
# just the dependencies first, so warm rebuilds where only `src/`
6+
# changes reuse that layer and skip recompiling crates.
7+
#
8+
# This intentionally avoids BuildKit `--mount=type=cache` directives so
9+
# the Dockerfile builds on classic Docker daemons too — notably Cloud
10+
# Run's `gcloud run deploy --source .` builder, which does not enable
11+
# BuildKit (see issue #620).
1012
#
1113
# Runtime stage is `debian:bookworm-slim` for libc compatibility (the
1214
# binary dynamically links against glibc) plus `ca-certificates` so HTTPS
@@ -27,43 +29,28 @@
2729
# `--health-cmd 'curl -fsS http://localhost:8080/ || exit 1'` on the
2830
# `docker run` if you want compose-level health gating.
2931

30-
FROM rust:1.85-slim AS builder
32+
FROM rust:1.90-slim AS chef
33+
RUN cargo install cargo-chef --locked --version 0.1.77
3134
WORKDIR /app
32-
# Copy lockfile so cargo uses pinned versions identically to local builds.
35+
36+
FROM chef AS planner
3337
COPY Cargo.toml Cargo.lock ./
3438
COPY src/ ./src/
35-
# BuildKit cache mounts: cargo's registry/git caches and the target/
36-
# directory persist across builds, dramatically speeding up rebuilds when
37-
# only application code changes.
38-
#
39-
# `id=...-$TARGETPLATFORM` is load-bearing on multi-arch builds. Without
40-
# it, BuildKit defaults to a single shared cache across architectures
41-
# and the `linux/amd64` + `linux/arm64` jobs race on the same on-disk
42-
# `/usr/local/cargo/registry/src/.../<crate>/.cargo-ok` extraction. The
43-
# second-arriving arch hits `File exists (os error 17)` mid-unpack and
44-
# the whole multi-arch build fails. Per-platform cache id keeps each
45-
# arch's cache isolated; warm-build speedup is preserved per-arch.
46-
# `target` cache is also platform-scoped because target/ holds object
47-
# files for one ABI and sharing them across arches would just produce
48-
# misses or, worse, invalid linking.
49-
ARG TARGETPLATFORM
50-
RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETPLATFORM} \
51-
--mount=type=cache,target=/usr/local/cargo/git,id=cargo-git-${TARGETPLATFORM} \
52-
--mount=type=cache,target=/app/target,id=app-target-${TARGETPLATFORM} \
53-
cargo build --release --bin tunnel-node && \
39+
RUN cargo chef prepare --recipe-path recipe.json
40+
41+
FROM chef AS builder
42+
COPY --from=planner /app/recipe.json recipe.json
43+
RUN cargo chef cook --release --recipe-path recipe.json
44+
COPY Cargo.toml Cargo.lock ./
45+
COPY src/ ./src/
46+
RUN cargo build --release --bin tunnel-node && \
5447
cp /app/target/release/tunnel-node /usr/local/bin/tunnel-node
5548

5649
FROM debian:bookworm-slim
57-
# `ca-certificates` for HTTPS upstream targets; nothing else needed at
58-
# runtime since the binary is statically linked against musl-equivalents
59-
# only for the parts that don't touch glibc.
6050
RUN apt-get update \
6151
&& apt-get install -y --no-install-recommends ca-certificates \
6252
&& rm -rf /var/lib/apt/lists/*
6353

64-
# Non-root runtime user. The service does no filesystem writes outside
65-
# /tmp, so a static-uid unprivileged user is sufficient and prevents
66-
# accidental host-FS writes if the container is volume-mounted.
6754
RUN useradd --system --uid 1000 --no-create-home --shell /usr/sbin/nologin tunnel
6855

6956
COPY --from=builder /usr/local/bin/tunnel-node /usr/local/bin/tunnel-node

0 commit comments

Comments
 (0)