prop/Dockerfile
Graham McIntire 4167eb54c9
Switch wgrib2 to OpenJPEG flag to match upstream CI matrix
Last attempt (-DUSE_PNG=ON -DUSE_AEC=ON on wgrib2) reached 100 %
linking libwgrib2.so and then died with "make: Error 2" on the
parallel wgrib2_exe link. wgrib2's own CI workflow
(.github/workflows/Linux_options.yml) never tests USE_PNG alone —
its matrix uses -DUSE_OPENJPEG=ON, and its NCEPLIBS-g2c is built
with -DUSE_OpenJPEG=ON -DUSE_Jasper=OFF. Copying that known-good
combination:

  * g2c now builds with USE_PNG + USE_AEC + USE_OpenJPEG (Jasper
    still disabled — libjasper is out of Debian anyway).
  * wgrib2 flips to -DUSE_OPENJPEG=ON -DUSE_AEC=ON.
  * libopenjp2-7-dev added to the builder apt list, libopenjp2-7
    added to the runtime apt list so libg2c.so + wgrib2 can
    resolve OpenJPEG symbols at exec time.

wgrib2 delegates actual GRIB2 decompression to g2c, which is
built with PNG support, so DRT 5.41 (MRMS PrecipRate) still
decodes through this link — wgrib2's own USE_PNG knob isn't
required for decoding, only for encoding we don't do.
2026-04-14 10:56:42 -05:00

163 lines
5.5 KiB
Docker

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian
# instead of Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?name=ubuntu
# https://hub.docker.com/_/ubuntu/tags
#
# This file is based on these images:
#
# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
# - https://hub.docker.com/_/debian/tags?name=trixie-20260316-slim - for the release image
# - https://pkgs.org/ - resource for finding needed packages
# - Ex: docker.io/hexpm/elixir:1.19.5-erlang-28.4.1-debian-trixie-20260316-slim
#
ARG ELIXIR_VERSION=1.19.5
ARG OTP_VERSION=28.4.1
ARG DEBIAN_VERSION=trixie-20260316-slim
ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
# ---- wgrib2 build stage (cached independently) ----
FROM ${RUNNER_IMAGE} AS wgrib2-builder
ARG WGRIB2_VERSION=3.6.0
ARG G2C_VERSION=1.9.0
RUN 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 \
&& rm -rf /var/lib/apt/lists/*
# NCEPLIBS-g2c: wgrib2 >= 3.x delegates GRIB2 decompression to this
# library via find_package whenever any of JASPER / OPENJPEG / PNG
# is enabled (wgrib2 CMakeLists:94). Build g2c with all the
# decoders we care about — PNG (MRMS PrecipRate / HRRR native),
# OpenJPEG (modern JPEG2000 replacement for the CVE-riddled Jasper),
# and AEC (CCSDS) — and disable Jasper so we don't need libjasper,
# which Debian dropped years ago.
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
# wgrib2 flag notes:
# * USE_OPENJPEG is what wgrib2's own CI (Linux_options.yml) tests
# — it triggers the g2c linkage and is known to build cleanly. An
# earlier attempt with -DUSE_PNG=ON alone got all the way to
# linking libwgrib2.so and then died with Error 2 on the wgrib2_exe
# link step (wgrib2 doesn't gate every jpeg_pk.c path properly
# when USE_PNG is set without USE_JASPER/USE_OPENJPEG).
# * wgrib2 delegates actual decompression to g2c, and g2c above was
# built with USE_PNG=ON, so PNG-packed GRIB2 (DRT 5.41, e.g. MRMS
# PrecipRate) still decodes correctly through this link even
# though wgrib2's own USE_PNG knob stays off.
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_OPENJPEG=ON -DUSE_AEC=ON \
&& make -j$(nproc) \
&& make install \
&& strip /usr/local/bin/wgrib2
# ---- Elixir build stage ----
FROM ${BUILDER_IMAGE} AS builder
# install build dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential git \
&& rm -rf /var/lib/apt/lists/*
# prepare build dir
WORKDIR /app
# install hex + rebar
RUN mix local.hex --force \
&& mix local.rebar --force
# set build ENV
ENV MIX_ENV="prod"
# install mix dependencies (changes only when mix.exs/mix.lock change)
COPY mix.exs mix.lock ./
COPY vendor vendor
RUN mix deps.get --only $MIX_ENV
# copy compile-time config before compiling deps
RUN mkdir config
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile
# install esbuild + tailwind binaries (cached until mix.exs changes)
RUN mix assets.setup
# create priv dir so :code.priv_dir works at compile time
# (actual contents copied after compile to avoid cache busting)
RUN mkdir -p priv/models priv/static
# copy application code and compile
COPY lib lib
COPY algo.md algo.md
RUN mix compile
# copy assets and build (JS/CSS changes don't require recompilation)
COPY assets assets
RUN mix assets.deploy
# copy priv contents (migrations, ML model, static files)
# after compile so model retraining doesn't bust the compile cache
COPY priv priv
# runtime config doesn't require recompilation
COPY config/runtime.exs config/
COPY rel rel
RUN mix release
# ---- Final runtime stage ----
FROM ${RUNNER_IMAGE} AS final
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libstdc++6 openssl libncurses6 locales ca-certificates snmp \
libgfortran5 libaec0 zlib1g libpng16-16t64 libopenjp2-7 \
&& rm -rf /var/lib/apt/lists/*
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV MIX_ENV="prod"
WORKDIR "/app"
RUN chown nobody /app
COPY --from=wgrib2-builder /usr/local/bin/wgrib2 /usr/local/bin/wgrib2
# wgrib2 links dynamically against libg2c.so from the builder stage —
# carry it across and refresh the loader cache so /usr/local/lib is
# searched at exec time.
COPY --from=wgrib2-builder /usr/local/lib/libg2c.so* /usr/local/lib/
RUN ldconfig
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/microwaveprop ./
USER nobody
CMD ["/app/bin/server"]