NPRPC Runtime Docker Image
nprpc-runtime is the base image for running NPRPC applications in
production. It holds what a built application needs at run time and nothing
else:
- the Swift 6.3.3 runtime (it is based on
swift:6.3.3-slim) libnprpcandlibmsquic, without debug info- the Boost shared libraries
liburing2,libatomic1,ca-certificatesandtzdata
The image is about 320 MB, roughly 30 MB more than swift:6.3.3-slim.
Applications are compiled in the development image,
and their production images are built FROM nprpc-runtime.
Why a shared base
Every image built from the same nprpc-runtime shares its layers. Docker
stores them once on the host, and because every container maps the same
library files, the kernel loads libnprpc into memory once for all of them.
Build the runtime image once per NPRPC version and reuse it for every site on
a host. If each application copied the libraries into its own image instead,
each would have its own copy on disk and in memory.
Building
just build-dev-image # if nprpc-dev is not built yet
just build-runtime-image # from nprpc-dev:latest
The image is tagged nprpc-runtime:<version>-<revision> and
nprpc-runtime:latest. The revision is the NPRPC commit recorded in the dev
image, or the dev image's id for dev images built before the commit label
existed.
To build from another dev image, for example an application's builder image
derived from nprpc-dev:
just build-runtime-image myapp-builder:latest
Matching the build image
An application must run on a runtime image made from the same NPRPC build it
was compiled against. The Swift bridge is linked into the application itself,
while libnprpc comes from the runtime image. If the two differ, the library
misreads the application's configuration, and the process fails in confusing
ways (for example, a QUIC error from a server that never enabled QUIC).
Each runtime image records the sha256 of its libnprpc in the label
io.nprpc.libnprpc.sha256. Deploy scripts should compare it with the
library in their build image and refuse a mismatch:
runtime=$(docker image inspect --format \
'{{index .Config.Labels "io.nprpc.libnprpc.sha256"}}' nprpc-runtime:1.0.0-abc)
builder=$(docker run --rm --entrypoint sha256sum myapp-builder:latest \
/opt/nprpc/lib/libnprpc.so.1.0.0 | cut -d' ' -f1)
[ "$runtime" = "$builder" ] || { echo "runtime/builder mismatch" >&2; exit 1; }
When NPRPC changes, rebuild the dev image, the application's builder image and the runtime image together, then redeploy each application. Until an application is redeployed, it keeps running on its old runtime tag, and both runtime images stay on the host.
Using it
# Pin the versioned tag, not latest, so a newer runtime shipped for another
# application does not change this one.
ARG NPRPC_RUNTIME_IMAGE=nprpc-runtime:1.0.0-abc
FROM ${NPRPC_RUNTIME_IMAGE}
COPY MyServer /app/MyServer
COPY www /app/www
WORKDIR /app
ENTRYPOINT ["/app/MyServer"]
LD_LIBRARY_PATH already points at /opt/nprpc/lib, /opt/boost/lib and
the Swift runtime. Libraries only your application uses go in your own image.
Shipping to a server
just ship-runtime-image debian@example.com nprpc-runtime:1.0.0-abc
This does nothing if the server already has that exact image. Otherwise it
streams the image over ssh with docker save | docker load, which keeps the
layer digests, so images the server builds FROM it share its layers.
Pushing to a registry works as well.
Images for one tag are built from the same layers, so a server that builds
several applications from nprpc-runtime:1.0.0-abc holds the libraries once.
Remove old runtime tags with docker image rm once no container uses them.
docs/DOCKER_RUNTIME_IMAGE.md