The application images (frontend and the workers) depend on heavy C++ stacks from ECMWF — eckit, eccodes, metkit, fdb, gribjump and the MARS client. Building those from source takes tens of minutes and used to happen inside every app image build.
These stacks are now built once, published to ECCR as standalone images, and
pulled by the app Dockerfiles via a FROM ${..._LIBS_IMAGE} line. Day-to-day app
builds compile only Rust and never touch a C++ compiler.
| Directory | Image | Contents | Consumed by |
|---|---|---|---|
metkit/ |
eccr.ecmwf.int/polytope/cpp-metkit-libs |
eckit + metkit → /opt/metkit |
frontend/Dockerfile |
fdb/ |
eccr.ecmwf.int/polytope/cpp-fdb-libs |
eckit, eccodes, metkit, fdb → /opt/fdb |
workers/fdb-worker/Dockerfile |
fdb-gribjump/ |
eccr.ecmwf.int/polytope/cpp-fdb-gribjump-libs |
eckit, libaec, eccodes, metkit, fdb, gribjump → /opt/fdb |
workers/polytope-fe-worker/Dockerfile |
mars/ |
eccr.ecmwf.int/polytope/cpp-mars-libs |
MARS client bundle → /opt/mars-client |
workers/mars-worker/Dockerfile |
fdb and fdb-gribjump are separate images on purpose: the fdb-worker and
fe-worker pin different upstream versions (e.g. fdb 5.19.2 vs 5.21.3), so they
cannot share one image.
The library .so/.a/binaries are built RelWithDebInfo (ecbuild's default),
so they ship with DWARF debug symbols — the bulk of the image size. Each
Dockerfile therefore has two final targets:
| Target | Image | Symbols | Size |
|---|---|---|---|
libs (default) |
cpp-<name>-libs |
stripped | 147 MB |
libs-debug |
cpp-<name>-libs-debug |
full | 419 MB |
strip --strip-unneeded on the shared libraries keeps the exported dynamic
symbols (so consumers still link and load them) while dropping ~260 MB of debug
info. Runtime behaviour is identical; you only lose symbol names in gdb
backtraces — which is exactly what the -debug companion is for. The -debug
image shares the same tag under a -debug repo suffix (e.g.
cpp-fdb-gribjump-libs-debug:fdb5.21.3-gribjump0.12.0-r1).
The consuming app Dockerfiles pull the stripped default; grab the -debug
image only when you need to symbolicate a crash in a C++ library.
Each image directory has a TAG file containing a single line — the tag the
image is published under for releases and the tag the consuming Dockerfile
pulls by default. For example fdb/TAG holds fdb5.19.2-r1, which produces
eccr.ecmwf.int/polytope/cpp-fdb-libs:fdb5.19.2-r1.
Why a file and not just a hardcoded tag?
- It is the single source of truth the publish workflow reads, so the tag you publish and the tag you consume can never drift apart by a typo in two places.
- It makes the current version of each stack greppable and reviewable in one place.
Tags are immutable. The publish workflow refuses to overwrite an existing tag. When you change what goes into an image you must give it a new tag.
Naming convention: describe the stack's headline versions, then a -rN
"revision" suffix that you bump for rebuilds that don't change library versions
(base-image refresh, build-flag tweak, etc.):
cpp-metkit-libs:metkit1.17.0-r1cpp-fdb-libs:fdb5.19.2-r1cpp-fdb-gribjump-libs:fdb5.21.3-gribjump0.12.0-r1cpp-mars-libs:7.1.12-r2
The app Dockerfiles carry the current tag as the default of a build arg
(FDB_LIBS_IMAGE, METKIT_LIBS_IMAGE, MARS_LIBS_IMAGE), so a normal docker build / skaffold build needs no extra flags.
- Edit the immutable bundle/source refs in
docker/cpp-libs/<name>/Dockerfile; dependency versions should remain owned by the selected bundle where one exists. - Put a new tag in
docker/cpp-libs/<name>/TAG(bump the version part, reset-r1; or just bump-rNfor a no-version-change rebuild). - Publish the new library image (see Dev builds or Releasing).
- Update the matching
*_LIBS_IMAGEdefault in the consuming app Dockerfile to the new tag.
Steps 3 and 4 are what make the new libraries actually take effect; until the consumer's build arg points at the new tag, nothing changes.
The skaffold config mirrors the root skaffold.yaml tag policy:
| Scenario | How to invoke | Resulting tag |
|---|---|---|
| Dev build | omit FIXED_TAG |
git commit hash (e.g. cce9ed8) |
| Dev build with prefix | PREFIX=dev- |
dev-cce9ed8 |
| Release | FIXED_TAG=$(cat <img>/TAG) |
contents of TAG file |
ignoreChanges: true means a dirty working tree does not block the git tag
derivation (same behaviour as the app builds).
Use these when iterating on a library Dockerfile or pinning a new upstream commit. The image is tagged with the current git commit hash, which lets you reference it unambiguously when building the consuming app image.
Run from this directory (docker/cpp-libs/). Skaffold resolves each
artifact's context: (e.g. mars) relative to the current working directory,
not to the location of skaffold.yaml.
cd docker/cpp-libs
# Build and push one image; tag = git commit hash
GH_TOKEN="$GH_TOKEN" skaffold build --push \
--filename skaffold.yaml \
--build-image eccr.ecmwf.int/polytope/cpp-mars-libs
# Capture the tag skaffold assigned:
TAG=$(git rev-parse --short HEAD) # same value skaffold used
# Build the consuming mars-worker against your dev library:
cd ../..
docker build -f workers/mars-worker/Dockerfile \
--build-arg MARS_LIBS_IMAGE=eccr.ecmwf.int/polytope/cpp-mars-libs:$TAG \
--build-arg GIT_AUTH_TOKEN="$GH_TOKEN" \
-t mars-worker:dev .You can also use
PREFIX=dev-to make dev images visually distinct from release images in the registry.
Set FIXED_TAG to the contents of the relevant TAG file. Skaffold uses that
value as the image tag instead of the git commit.
cd docker/cpp-libs
# Publish the canonical versioned image (and its debug companion):
FIXED_TAG=$(cat fdb/TAG) skaffold build --push \
--filename skaffold.yaml \
--build-image eccr.ecmwf.int/polytope/cpp-fdb-libs \
--build-image eccr.ecmwf.int/polytope/cpp-fdb-libs-debug
# mars additionally needs GH_TOKEN:
FIXED_TAG=$(cat mars/TAG) GH_TOKEN="$GH_TOKEN" skaffold build --push \
--filename skaffold.yaml \
--build-image eccr.ecmwf.int/polytope/cpp-mars-libs \
--build-image eccr.ecmwf.int/polytope/cpp-mars-libs-debugBuild one stack per invocation with
--build-image. Without it, skaffold builds all eight artifacts (four stacks × stripped + debug) and applies the sameFIXED_TAGto every image — almost never what you want.
The workflow .github/workflows/cpp-libs.yaml
runs skaffold with FIXED_TAG from the TAG file. PRs touching docker/cpp-libs/
only run a Dockerfile lint (docker buildx build --check) — the images are never
built in PR CI (hours of C++ compilation). Publishing is manual dispatch only:
# image: fdb | metkit | fdb-gribjump | mars | all
gh workflow run cpp-libs.yaml -f image=fdb