refactor(infra): use upstream images verbatim, seed config via docker cp (refs #30)
All checks were successful
CI / lint (pull_request) Successful in 49s
CI / build (pull_request) Successful in 44s
CI / unit (pull_request) Successful in 44s
CI / compose-smoke (pull_request) Successful in 4m15s

Drops the inline-build images for the upstream services. The compose now
references the published images directly (openzaak/open-zaak,
openzaak/open-notificaties, keycloak, curl, flowable-rest) with no build for
them, and the config they need is streamed into external named volumes by
infra/seed-config.sh:

  rr-oz-config  -> oz-init     /app/setup_configuration   (data.yaml)
  rr-kc-realms  -> keycloak    /opt/keycloak/data/import   (realm exports)
  rr-fl-bpmn    -> flowable-init /work                     (registratie.bpmn)

How: the seeder creates each volume, `docker create`s a throwaway helper that
mounts it, `docker cp`s the files in, and removes it. docker cp streams over the
Docker API, so it works in Docker-in-Docker (the CI runner) where bind mounts
mount empty. It uses plain `docker create`/`cp` — NOT `docker compose create`,
which podman-compose (local dev) lacks. `external: true` fixed names keep the
volumes identical across docker compose and podman-compose.

Consequence: bare `docker compose up` no longer self-seeds, so use `make up`
(seeds then starts). Every `*-up` target seeds first; `*-down` removes the
external volume. acl/bff are still built (they're our apps, not upstream images).

Verified end-to-end on podman-compose: `make keycloak-up` seeds rr-kc-realms,
the upstream Keycloak mounts it, and --import-realm imports all four realms
(digid realm returns 200). Seeder runs in ~2s.

Docs updated: gitea-actions-gotchas.md, ci.md, openzaak.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-25 10:22:14 +02:00
parent 6d8e1d0830
commit b349dff496
9 changed files with 182 additions and 100 deletions

View File

@@ -38,39 +38,50 @@ not happen on a runner that executes jobs directly on the host (the previous
self-hosted `respellion-linux` setup), which is why switching to `ubuntu-latest`
exposed it.
**Fix: bake assets into derived images instead of bind-mounting them.** Anything
a Compose service needs at runtime that lives in the repo is `COPY`-ed into a
small derived image, so it is present regardless of where the daemon runs. We use
**`build.dockerfile_inline`** — the 2-line recipe lives in the compose file, so
there are no standalone `Dockerfile` artifacts to maintain:
**Fix: the upstream images are used verbatim (no build); config is streamed into
external named volumes with `docker cp`.** `infra/seed-config.sh` creates a fixed-
name volume per asset, runs a throwaway helper container that mounts it, and
`docker cp`s the files in. `docker cp` streams bytes over the Docker API, so it
works no matter where the daemon runs (including Docker-in-Docker). The services
then mount those volumes:
| Asset | Derived image | Built by |
| Asset | External volume | Mounted by → at |
|---|---|---|
| OpenZaak `setup_configuration/data.yaml` | `register-referentie/openzaak:dev` | `oz-init` `dockerfile_inline` |
| Keycloak realm exports | `register-referentie/keycloak:dev` | `keycloak` `dockerfile_inline` |
| `workflows/registratie.bpmn` | `register-referentie/flowable-init:dev` | `flowable-init` `dockerfile_inline` |
| OpenZaak `setup_configuration/data.yaml` | `rr-oz-config` | `oz-init``/app/setup_configuration` |
| Keycloak realm exports | `rr-kc-realms` | `keycloak``/opt/keycloak/data/import` |
| `workflows/registratie.bpmn` | `rr-fl-bpmn` | `flowable-init` `/work` |
The base image tag is interpolated by Compose (`${OPENZAAK_TAG}`) so pinning is
unchanged. OpenZaak's `openzaak`/`oz-celery` reuse the one image `oz-init` builds.
Open Notificaties needs **no** bake — `nrc-init` runs migrations only, so all NRC
services use the plain base image.
The volumes are declared `external: true` with fixed `name:`s so they resolve
identically under docker compose and podman-compose. The seed step (`make` runs
it before every `up`) recreates them fresh each time; `make down` / the
per-service `*-down` targets remove them. Open Notificaties needs no config at all
`nrc-init` runs migrations only.
`dockerfile_inline` works on both the CI runner (docker compose) and local
**podman-compose**, unlike `docker cp`-into-volumes (which needs `docker compose
create`, a subcommand podman-compose lacks).
**Two hard constraints drove this design:**
- Use plain `docker volume create` / `docker run` / `docker cp`**not**
`docker compose create`, which **podman-compose** (the local dev runtime) does
not implement.
- `docker cp` rather than a bind mount of the source dir, because that bind mount
is exactly what fails on the containerized runner.
**Consequence: bare `docker compose up` no longer self-seeds** — the `external`
volumes must be populated first, so use **`make up`** (or `make <svc>-up`), which
seeds then starts. CI uses `make smoke`, which does the same.
**Why not the alternatives.**
- *Bake into a (possibly inline) image* — clean and portable, but it's a build;
rejected here because the goal was to use the upstream images verbatim.
- *Compose `configs:` with inline `content`* — Compose materialises these as a
temp file on the **client** side and bind-mounts it, so it hits the exact same
daemon-can't-see-the-path problem.
- *A self-hosted runner that runs jobs on the host* — works, but reintroduces a
bespoke runner and undoes the move to the hosted `ubuntu-latest` label.
temp file on the **client** side and bind-mounts it → same daemon-can't-see-it
problem.
- *A self-hosted runner that runs jobs on the host* — bind mounts would then work
with zero seeding, but it reintroduces a bespoke runner and undoes the move to
the hosted `ubuntu-latest` label.
**Consequence for local dev.** There is now no bind mount of these config files,
so the SELinux `:z`/`:Z` relabel flag is no longer needed anywhere in `infra/`,
and rootless Podman no longer needs the files to be world-readable. One mechanism
(build) works on both Podman locally and Docker-in-Docker in CI.
No bind mounts of these config files remain, so the SELinux `:z`/`:Z` relabel flag
is no longer needed anywhere in `infra/` (named volumes don't need relabeling).
## `--wait` fails on one-shot containers with no dependant