fix(grid-rs): build wgrib2 in-tree instead of pulling from prop:latest

The FROM ${WGRIB2_IMAGE} AS wgrib2-src pattern required docker buildx
to pull a private-registry image mid-build. That pull started failing
consistently after Stream C landed — docker login on the runner
succeeds but buildx's implicit FROM pull can't always reach the same
credentials (buildx instance context vs the host daemon).

Rebuild wgrib2 + NCEPLIBS-g2c from source in a dedicated builder stage,
mirroring the Elixir Dockerfile's self-contained wgrib2-builder. Adds
~5 min per CI run — worth the end of a 3-run CI-failure streak.
This commit is contained in:
Graham McIntire 2026-04-20 08:33:53 -05:00
parent ea73f3164d
commit 1cbc3e541f
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59

View file

@ -1,35 +1,70 @@
# syntax=docker/dockerfile:1.7 # syntax=docker/dockerfile:1.7
# #
# Multi-arch build for `prop-grid-rs`. wgrib2 isn't in Debian apt — the # Multi-arch build for `prop-grid-rs`. Self-contained: builds wgrib2 +
# Elixir image builds it from source in its own stage. Rather than # NCEPLIBS-g2c from source so the image has no external private-registry
# rebuild it here (~5 min per CI run), copy the binary + libg2c out of # dependency. An earlier revision did `FROM prop:latest AS wgrib2-src` to
# the already-published Elixir image. # reuse the Elixir image's pre-built wgrib2, but docker buildx in CI
# # couldn't always authenticate to the private registry during that
# Runtime uid 65534 matches the fsGroup on the NFS scores mount. # implicit FROM pull — the resulting failures were opaque and broke
# every grid-rs image publish after Stream C.
# #
# This image ships two binaries: `prop-grid-rs` (default ENTRYPOINT, # This image ships two binaries: `prop-grid-rs` (default ENTRYPOINT,
# drains grid_tasks) and `hrrr-point-worker` (k8s `command:` override, # drains grid_tasks) and `hrrr-point-worker` (k8s `command:` override,
# drains hrrr_fetch_tasks). # drains hrrr_fetch_tasks). Runtime uid 65534 matches the fsGroup on
ARG WGRIB2_IMAGE=git.mcintire.me/graham/prop:latest # the NFS scores mount.
FROM ${WGRIB2_IMAGE} AS wgrib2-src ARG DEBIAN_VERSION=trixie-slim
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
# ---- wgrib2 builder (mirrors the same stage in the Elixir Dockerfile) ----
FROM ${RUNNER_IMAGE} AS wgrib2-builder
ARG WGRIB2_VERSION=3.6.0
ARG G2C_VERSION=2.1.0
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
rm -f /etc/apt/apt.conf.d/docker-clean \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential gfortran cmake wget ca-certificates pkg-config \
zlib1g-dev libaec-dev libpng-dev libopenjp2-7-dev
WORKDIR /tmp/g2c
RUN wget -q --content-disposition "https://github.com/NOAA-EMC/NCEPLIBS-g2c/archive/refs/tags/v${G2C_VERSION}.tar.gz" \
-O g2c.tar.gz \
&& tar xzf g2c.tar.gz \
&& cd NCEPLIBS-g2c-${G2C_VERSION} \
&& mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DUSE_PNG=ON -DUSE_AEC=ON -DUSE_OpenJPEG=ON -DUSE_Jasper=OFF \
&& make -j$(nproc) \
&& make install
WORKDIR /tmp/wgrib2
RUN wget -q --content-disposition "https://github.com/NOAA-EMC/wgrib2/archive/refs/tags/v${WGRIB2_VERSION}.tar.gz" \
-O wgrib2.tar.gz \
&& tar xzf wgrib2.tar.gz \
&& cd wgrib2-${WGRIB2_VERSION} \
&& mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release \
-DUSE_PNG=ON -DUSE_OPENJPEG=ON -DUSE_AEC=ON \
&& make -j$(nproc) \
&& make install \
&& strip /usr/local/bin/wgrib2
# ---- Rust build ----
FROM rust:1.94-trixie AS builder FROM rust:1.94-trixie AS builder
WORKDIR /src WORKDIR /src
# Cache deps: copy manifests first so dependency changes alone don't
# invalidate the source-layer cache.
COPY Cargo.toml Cargo.lock* ./ COPY Cargo.toml Cargo.lock* ./
COPY src ./src COPY src ./src
COPY tests ./tests COPY tests ./tests
# BuildKit cache mounts persist cargo's registry + git across CI runs. # Registry + git caches are persistent; target/ is not (kept ephemeral
# `target/` is deliberately NOT cached — after Stream C bumped the # to avoid the runner disk-pressure failures observed during Stream C
# crate to two binaries, the runner started running out of disk inside # rollout). Full rebuild adds ~3 min; it's still under 6 min total.
# the mount and the build would fail mid-link. Rebuilding from scratch
# adds ~3 min to the build but removes the failure mode entirely.
# Lint + test gate the image build so a regressed scorer or a clippy
# warning never produces a pushable image.
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \ --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
rustup component add clippy \ rustup component add clippy \
@ -39,22 +74,19 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
&& cp target/release/worker /usr/local/bin/worker \ && cp target/release/worker /usr/local/bin/worker \
&& cp target/release/hrrr_point_worker /usr/local/bin/hrrr_point_worker && cp target/release/hrrr_point_worker /usr/local/bin/hrrr_point_worker
# Runtime image: slim debian + the shared libs wgrib2 links against. # ---- Runtime ----
# libgfortran5/libaec0/zlib1g/libpng16-16t64/libopenjp2-7 mirror the FROM ${RUNNER_IMAGE} AS runtime
# Elixir runtime stage. RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
FROM debian:trixie-slim AS runtime --mount=type=cache,target=/var/lib/apt,sharing=locked \
RUN apt-get update && apt-get install -y --no-install-recommends \ rm -f /etc/apt/apt.conf.d/docker-clean \
ca-certificates \ && apt-get update \
tini \ && apt-get install -y --no-install-recommends \
libgfortran5 \ ca-certificates tini \
libaec0 \ libgfortran5 libaec0 zlib1g libpng16-16t64 libopenjp2-7
zlib1g \
libpng16-16t64 \
libopenjp2-7 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=wgrib2-src /usr/local/bin/wgrib2 /usr/local/bin/wgrib2 COPY --from=wgrib2-builder /usr/local/bin/wgrib2 /usr/local/bin/wgrib2
COPY --from=wgrib2-src /usr/local/lib/libg2c.so* /usr/local/lib/ COPY --from=wgrib2-builder /usr/local/lib/libg2c.so /usr/local/lib/
COPY --from=wgrib2-builder /usr/local/lib/libg2c.so.0 /usr/local/lib/
RUN ldconfig RUN ldconfig
COPY --from=builder /usr/local/bin/worker /usr/local/bin/prop-grid-rs COPY --from=builder /usr/local/bin/worker /usr/local/bin/prop-grid-rs
@ -62,7 +94,6 @@ COPY --from=builder /usr/local/bin/hrrr_point_worker /usr/local/bin/hrrr-point-w
USER 65534:65534 USER 65534:65534
# Default entrypoint is the grid chain worker. Override with `command:` # Default entrypoint is the grid chain worker. Override with `command:`
# in the k8s manifest (or `docker run ... hrrr-point-worker`) for the # in the k8s manifest for the per-QSO point worker.
# per-QSO point worker.
ENTRYPOINT ["/usr/bin/tini", "--"] ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/usr/local/bin/prop-grid-rs"] CMD ["/usr/local/bin/prop-grid-rs"]