Repository navigation
fix(docker): make the image work with no network at runtime - #2
Merged
Merged
Conversation
`docker run ghcr.io/dmux/minilake` downloaded two things on first use, so an offline machine got a server that booted and then failed on the first query. The DuckDB `delta` extension was fetched from extensions.duckdb.org at startup, into $HOME/.duckdb — not a volume, so every fresh container paid for it again. The failure was swallowed as a warning and the health check stayed green, which is why it surfaced three layers away, at delta_scan() time. Both Dockerfiles now INSTALL it at build time into MINILAKE_DUCKDB_EXTENSION_DIR and the build fails outright if the fetch does not land; at runtime the pool only LOADs it, with autoinstall_known_extensions off so nothing can be fetched behind your back. A plain `pip install minilake` keeps the INSTALL path, since there is no image to bake anything into. The Delta and Unity Catalog jars were resolved from Maven Central by spark-submit --packages. A new build stage resolves them with spark-submit itself rather than curl: Ivy needs the ivy-*.xml metadata alongside the jar, and without it re-resolves over the network even when the jar is already on disk. The job container is a sibling and cannot see inside this one, so the only place both can reach is the data volume — _seed_ivy_cache copies the baked cache there once per volume, chmod'd for the Spark image's uid 185. The extension directory and the Ivy cache both live outside WORKDIR because docker-compose.test.yml bind-mounts the repo over /opt/minilake, which would otherwise hide them at runtime. Verified with spark-submit on a --internal Docker network (no route out): "0 artifacts copied, 30 already retrieved", and the script ran. scripts/verify- offline.sh pins the whole thing under `docker run --network none`. What an image cannot contain is another image, so the Spark image pull is still a network call; MINILAKE_SPARK_PREWARM=0 skips the attempt and the docs say to pull it once up front.
Minor rather than patch: the image gains a capability it did not have — it runs with no network at all — and two new configuration knobs, MINILAKE_IVY_SEED and MINILAKE_SPARK_PREWARM. Nothing existing changes behaviour. release.yml refuses to publish when the tag and pyproject.toml disagree, so the lock is regenerated in the same commit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
docker run ghcr.io/dmux/minilake:latestdownloaded two things on first use, so on a machine without internet the server booted and then failed on the first real query.deltaextension —duckdb_pool.pyranINSTALL deltaat startup, fetching fromextensions.duckdb.orginto$HOME/.duckdb. Not a volume, so every fresh container re-downloaded it. The failure was swallowed as awarningand the health check stayed green, so it surfaced three layers away as a brokendelta_scan().FEATURES.mdclaimed this "fails loudly" — it did not.spark-submit --packagesresolved from Maven Central on the first job run. The Ivy cache on the data volume avoided repeat resolution but not the first fetch, and the volume starts empty.Changes
DuckDB extension. Both Dockerfiles
INSTALLit at build time intoMINILAKE_DUCKDB_EXTENSION_DIR(/opt/duckdb-extensions), and the build fails outright if the fetch does not land — better than silently publishing an image that goes back to downloading at boot. At runtime the pool onlyLOADs it, withautoinstall_known_extensions=Falseso nothing can be fetched behind your back. A plainpip install minilakekeeps the oldINSTALLpath; there is no image to bake anything into.Spark jars. A new
sparkjarsbuild stage resolves Delta + the UC connector withspark-submititself rather than curl — Ivy needs theivy-*.xmlmetadata next to the jar, and without it re-resolves over the network even when the jar is on disk. The job container is a sibling and cannot see inside minilake's container, so_seed_ivy_cache()copies the baked cache onto the shared data volume once per volume, chmod'd for the Spark image's uid 185.Both directories live outside
WORKDIR:docker-compose.test.ymlbind-mounts the repo over/opt/minilakeand would otherwise hide them at runtime.Still a network call: the Spark image
docker pull. An image cannot contain another image.MINILAKE_SPARK_PREWARM=0skips the attempt, and the docs say to pull it once up front.Verification
tests/test_duckdb_extensions.pyruns against the Docker stack (not skipped) and asserts the extension resolves to/opt/duckdb-extensions/v1.5.5/linux_amd64/delta.duckdb_extension— anywhere else means it was downloaded at runtime._seed_ivy_cachetests: seeding, idempotency, non-clobbering, missing seed dir.scripts/verify-offline.shpasses underdocker run --network none.spark-submit --packageson an--internalDocker network (no route out) reported0 artifacts copied, 30 already retrievedand the script ran.Version
1.5.1→1.6.0. Minor rather than patch: the image gains a capability it did not have, plusMINILAKE_IVY_SEEDandMINILAKE_SPARK_PREWARM. No existing behaviour changes.Review notes
sparkjarsstage is pinned to--platform=$BUILDPLATFORM(jars are architecture-independent, so it skips QEMU in the multi-arch release build). The DuckDBRUNis deliberately not — that extension is per-architecture and must run on the target platform.docs/configuration.mddocumentedMINILAKE_DUCKDB_MEMORY_LIMITas "Passed to DuckDB", but it is never applied to any connection. Marked as a no-op; fixing it is out of scope here.