Containerizing a 1990s numerical model is different from containerizing a web app, mostly because the interesting decisions are invisible in a Dockerfile. This guide covers what matters when the binary being shipped is forty years of physics.
Decide first: compiler in the image, or wheel in the image?
Two honest architectures exist. Ship the compiled wheel and a
slim runtime — the generated nativegate Dockerfile's shape:
builder stage has the toolchain, runtime stage is
python:3.11-slim plus the wheel. Or ship the full
compiler toolchain and build at startup — which buys nothing at
runtime and costs attack surface, size, and startup time for a
program whose inputs are known. Prefer the wheel unless you
have a stated reason otherwise.
Run unprivileged — deliberately, in both files
The runtime user must be unprivileged, and it must match
somewhere. Generated nativegate images create an explicit UID
1000; its generated Kubernetes manifests set
runAsUser: 1000 and runAsNonRoot: true
— generated from one source so the two files can't drift into a
permissions failure nobody attributes to either. If you're
writing these by hand, copy the pattern: one number, declared
twice, in review together.
Read-only root filesystem is a Python contract
readOnlyRootFilesystem: true is a good posture and
a Python constraint: the interpreter writes .pyc
files next to source by default, and a read-only root turns
that into a crash. Set PYTHONDONTWRITEBYTECODE=1
(and PYTHONUNBUFFERED=1 so logs stream), or pick
your writable-disk story consciously.
Health probes match the model
/healthz and /readyz on a service
backed by native code should probe what you actually care
about: process up, and the native surface answering. The
generated service includes both, with SIGTERM draining so
rollout restarts don't cut off in-flight calls.
What containerization does not fix
- Statefulness. A container still runs one process with one COMMON-block store; the one-call-at-a-time rule survives containerization intact. Scale with replicas.
- Native crashes. A segfault in the physics kills the worker inside a perfectly configured container. Restarts and replicas contain it; the container never prevents it.
- Numerical drift. A container pins the build for the moment it was built; a golden record pins the answers across every rebuild. Ship both together.
An honest conclusion for a container built from generated artifacts: the image is the smallest and most boring part of the deliverable — which is precisely why it's a good candidate for generation.