Trim the Java runtime with jlink, on a shared base image - #2540
francoisferrand wants to merge 1 commit into
Conversation
| COPY --from=jre_build /javaruntime ${JAVA_HOME} | ||
|
|
||
| # Debian's base image is only rebuilt periodically, so patch it: we pin it, so nothing else will. | ||
| RUN apt-get update && apt-get upgrade -y && \ |
There was a problem hiding this comment.
Missing DEBIAN_FRONTEND=noninteractive on apt-get upgrade. The zookeeper Dockerfile sets it correctly; without it, debconf prompts (config file conflicts, service restarts) can hang the build.
| RUN apt-get update && apt-get upgrade -y && \ | |
| RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y && \ |
| RUN mkdir ${KAFKA_HOME} && apt-get update && apt-get install curl -y && apt-get clean | ||
| # Debian's base image is only rebuilt periodically, so patch it: we pin it, so nothing else will. | ||
| RUN mkdir ${KAFKA_HOME} && \ | ||
| apt-get update && apt-get upgrade -y && apt-get install curl -y && \ |
There was a problem hiding this comment.
Missing DEBIAN_FRONTEND=noninteractive on apt-get upgrade, unlike the zookeeper Dockerfile which sets it. Can hang the build on debconf prompts.
| apt-get update && apt-get upgrade -y && apt-get install curl -y && \ | |
| apt-get update && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y && apt-get install curl -y && \ |
|
CI note: the That is Maven Central refusing the runner, not a problem with this change — Verified the published image afterwards: Worth knowing for whoever picks this up: the |
75577fc to
4f39a4f
Compare
Hello francoisferrand,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
Incorrect fix versionThe
Considering where you are trying to merge, I ignored possible hotfix versions and I expected to find:
Please check the |
4a603c2 to
f426b11
Compare
|
|
||
| # Debian's base image is only rebuilt periodically, so patch it: we pin it, so nothing else will. | ||
| # curl is used by the operators' probes, ca-certificates by anything talking TLS. | ||
| RUN apt-get update && apt-get upgrade -y && \ |
There was a problem hiding this comment.
Missing DEBIAN_FRONTEND=noninteractive before apt-get upgrade. The zookeeper Dockerfile sets it; without it, debconf prompts (config file conflicts, service restarts) can hang the build. This is the same issue previously flagged on the downstream Dockerfiles, which no longer have their own apt-get upgrade since this PR moved it here.
| RUN apt-get update && apt-get upgrade -y && \ | |
| RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y && \ |
| image: jmx-javaagent | ||
| tag: 1.5.0 | ||
| envsubst: JMX_JAVAAGENT_TAG | ||
| java-base: # shared base for our java images; built, never deployed |
There was a problem hiding this comment.
java-base sorts before jmx-javaagent (a < m). The file header asks to keep first-level keys sorted.
8dedf81 to
a4a8ff4
Compare
|
CI note:
The meaningful signal is that both Zenko deployments came up and the CTST suite passed, which is what exercises kafka, zookeeper and cruise-control on the new shared |
The images ship a full Temurin JRE, most of which these services never load. Build a jlink runtime instead and put it on a Debian base. Both the runtime and the patched OS live in a shared java-base image rather than being repeated per image. Inlined, only the jlink output deduplicates and each apt layer is written separately, which cuts the saving across the three images from 135MB to 21MB. ALL-MODULE-PATH keeps every platform module rather than a jdeps-derived list. Netty, the JMX agent, SASL and TLS reach modules through reflection and ServiceLoader, so trimming would trade ~25MB for failures that only appear at runtime, on paths tests may never exercise. Pinning our own base means nothing else patches it, so it upgrades its packages at build time; without that the images inherit whatever was current when Debian last rebuilt the base, which scanned three critical and ten high CVEs worse than the images they replace. The base feeds the build tree hash of every image built on it, so bumping it republishes them all rather than silently changing the content behind an already published tag. Moving off Temurin's Ubuntu images frees uid 1000, so zookeeper no longer needs to delete the default ubuntu user to claim it. Issue: ZENKO-5361
a4a8ff4 to
b78a950
Compare
|
Correcting my previous comment — I said the What actually failed
4473 of 4474 scenarios passed in the sharded suite, so it looked like flake — but the parent commit ( Root cause: no CDS archivejlink does not generate a class data sharing archive, and the stock Temurin images ship one. Measured JVM startup, best of 5:
2.5x slower startup, and CDS also speeds class loading during the run, not just boot. Worth noting the Dockerfile this work replaced ran FixAdded to the RUN ${JAVA_HOME}/bin/java -Xshare:dumpVerified Cost is a 13.8 MB archive, paid once in the shared layer. Updated totals:
So the saving goes from 22% to 20%, in exchange for not shipping a 2.5x startup regression. Clearly the right trade. I cannot prove this was the cause of the two timeouts — both are timing-sensitive async tests and could be flaky — but shipping a measurable startup regression was a real defect regardless, and it is the most plausible explanation for two timeout failures on a commit whose parent was green. Pushed; new CI run will show whether the suites go green. |
|
CDS fix result: That is good evidence the missing CDS archive was the actual cause rather than flake: the failing scenarios were both timeouts, the parent commit was green, and restoring CDS restored the suite. |
Replaces the full Temurin JRE with a
jlink-trimmed runtime, carried in a sharedjavaimage that kafka, kafka-connect, cruise-control and zookeeper all buildFROM.Follow-up to #2539 (ZENKO-5188), now merged.
Size
Measured deduplicated — what a registry or the ISO actually stores for the three service images:
The shared base is what makes this worthwhile. Inlining the same stages in each Dockerfile saved only 21.5 MB (3.5%): the
jlinkoutput deduplicates because it is aCOPYfrom an identical stage, but each image'saptlayer is built separately and does not, so three copies of the OS upgrade ate the saving. Moving both into one base image they genuinely share recovers it.It also reduces Dockerfile complexity rather than adding it — each service image drops its
jlinkstage and itsaptblock, and the JDK/base decisions now live in one file instead of three.CVEs
Scanned with trivy. Java/application CVEs are unchanged (34 vs 34) — jlink does not touch app dependencies.
−9 medium, no change to critical or high. Marginal, and not the reason to do this.
bsdutils2.41.5 vs 2.39.3,libtinfo66.5 vs 6.4). Debian's tracker reports what Ubuntu's marks not-affected.The
apt-get upgradein the base is load-bearing. Pinning our own base transfers patching responsibility from Temurin (rebuilt continuously) to us; without it the images scanned 3 critical and 10 high worse than what they replace, becausedebian:trixie-slimwas three weeks stale.Class data sharing
jlink does not generate a CDS archive, and the stock Temurin images ship one. Measured JVM startup, best of 5:
-Xshare:dumpWithout it every JVM start re-parses class metadata that CDS would otherwise map straight into memory. The base therefore runs
java -Xshare:dump, as the pre-jlink Dockerfile did. This costs 13.8 MB in the shared layer — the difference between the 479.3 MB an archive-less build reaches and the 492.5 MB above — and is why the saving is 20% rather than 22%.Without this, two e2e scenarios timed out (
Deletion of an archived object,PRA (nominal case)); both pass with it.Notes for review
ghcr.io/scality/zenko/javais built and pushed but never deployed, so it is markedbuildOnly: trueindeps.yamland excluded from the ISO image list. The three service builds nowneeds: java, so CI wall-clock grows by one build.solution/javafeeds the build tree hash of every image built on it. Without that, changing the base would republish different content under an already-published immutable tag.ALL-MODULE-PATHrather than ajdeps-derived module list: Netty, the JMX agent, SASL and TLS resolve modules via reflection and ServiceLoader, which jdeps cannot see, so trimming saves ~21 MB in exchange for failures that only appear at runtime. Trivy does not detect the jlink runtime as a package at all, so trimming would not change CVE counts either. Dropping the dev-tooling modules to removejavac/jshellwas investigated and rejected separately: it is all-or-nothing (jdk.compileris pulled back byjdk.javadoc,jdk.jdepsandjdk.jshell), and the cluster includesjdk.jdwp.agentandjdk.hotspot.agent, so it would costjstack -F/jmap -Fon a hung JVM.--release-infois required: jlink otherwise drops all vendor metadata, including theIMPLEMENTOR="Eclipse Adoptium"marker that the ZENKO-5188 work relies on to prove we are not shipping Oracle JDK.ubuntuuser to claim it.dependencies_versions_env()now requires anenvsubstkey before emitting a variable.javahas none, and without the guard it emitted nameless=java/=21.0.12_8lines into the generated env.buildjob inbuild-kafka.yamlis renamedkafka, now that the workflow has four jobs.Verified
All four images:
IMPLEMENTOR="Eclipse Adoptium", 69 GPL legal notices, no NFTC text, andjava -Xshare:on -versionsucceeds.Beyond building — a real Kafka 3.9 KRaft broker starts and completes a produce/consume roundtrip; kafka-connect's jmx_prometheus javaagent loads (exercising
java.instrument); ZooKeeper reachesMode: standaloneat uid 1000 withsocat/hostname/ps/gosupresent for the operator probes; CruiseControl runs clean with no classpath errors.CI is green: the four build jobs,
build-iso, and all four e2e suites includingctst-end2end-sharded(4474 scenarios).Issue: ZENKO-5361