From 8d44fd5f3e50db4039bf99a4d2243d600971244a Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 4 Sep 2024 08:47:32 +0200 Subject: [PATCH] Improve docker without buildpacks documentation Explain how CDS can be enabled with plain Dockerfiles. Closes gh-42106 --- .../how-to/pages/class-data-sharing.adoc | 6 +++ .../container-images/dockerfiles.adoc | 53 ++++++++++--------- .../modules/reference/partials/dockerfile | 21 ++++++++ 3 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc index 7b94fec7a464..591b359b0b08 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc @@ -15,6 +15,12 @@ This will cause the buildpack to do a training run of the application, save the The Paketo Buildpack for Spring Boot https://github.com/paketo-buildpacks/spring-boot?tab=readme-ov-file#configuration[documentation] has information on other configuration options that can be enabled with builder environment variables, like `CDS_TRAINING_JAVA_TOOL_OPTIONS` that allows to override the default `JAVA_TOOL_OPTIONS`, only for the CDS training run. +[[howto.class-data-sharing.dockerfiles]] +== Packaging an Application Using CDS and Dockerfiles + +If you don't want to use Cloud Native Buildpacks, it is also possible to use CDS with a `Dockerfile`. +For more information about that, please see the xref:reference:packaging/container-images/dockerfiles.adoc#packaging.container-images.dockerfiles.cds[Dockerfiles reference documentation]. + [[howto.class-data-sharing.training-run-configuration]] == Preventing Remote Services Interaction During the Training Run diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc index a3a7417b0e2c..ec1e817612a8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc @@ -1,7 +1,7 @@ [[packaging.container-images.dockerfiles]] = Dockerfiles -While it is possible to convert a Spring Boot uber jar into a Docker image with just a few lines in the Dockerfile, using the xref:packaging/container-images/efficient-images.adoc#packaging.container-images.efficient-images.layering[layering feature] will result in an optimized image. +While it is possible to convert a Spring Boot uber jar into a Docker image with just a few lines in the `Dockerfile`, using the xref:packaging/container-images/efficient-images.adoc#packaging.container-images.efficient-images.layering[layering feature] will result in an optimized image. When you create a jar containing the layers index file, the `spring-boot-jarmode-tools` jar will be added as a dependency to your jar. With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers. @@ -28,32 +28,12 @@ Available commands: help Help about any command ---- -The `extract` command can be used to easily split the application into layers to be added to the Dockerfile. -Here is an example of a Dockerfile using `jarmode`. +The `extract` command can be used to easily split the application into layers to be added to the `Dockerfile`. +Here is an example of a `Dockerfile` using `jarmode`. [source,dockerfile] ---- -# Perform the extraction in a separate builder container -FROM bellsoft/liberica-openjre-debian:17-cds AS builder -WORKDIR /builder -# This points to the built jar file in the target folder -# Adjust this to 'build/libs/*.jar' if you're using Gradle -ARG JAR_FILE=target/*.jar -# Copy the jar file to the working directory and rename it to application.jar -COPY ${JAR_FILE} application.jar -# Extract the jar file using an efficient layout -RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted - -# This is the runtime container -FROM bellsoft/liberica-openjre-debian:17-cds -WORKDIR /application -# Copy the extracted jar contents from the builder container into the working directory in the runtime container -# Every copy step creates a new docker layer -# This allows docker to only pull the changes it really needs -COPY --from=builder /builder/extracted/dependencies/ ./ -COPY --from=builder /builder/extracted/spring-boot-loader/ ./ -COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ -COPY --from=builder /builder/extracted/application/ ./ +include::reference:partial$dockerfile[] # Start the application jar - this is not the uber jar used by the builder # This jar only contains application code and references to the extracted jar files # This layout is efficient to start up and CDS friendly @@ -67,10 +47,31 @@ Assuming the above `Dockerfile` is in the current directory, your Docker image c $ docker build --build-arg JAR_FILE=path/to/myapp.jar . ---- -This is a multi-stage Dockerfile. +This is a multi-stage `Dockerfile`. The builder stage extracts the directories that are needed later. Each of the `COPY` commands relates to the layers extracted by the jarmode. -Of course, a Dockerfile can be written without using the `jarmode`. +Of course, a `Dockerfile` can be written without using the `jarmode`. You can use some combination of `unzip` and `mv` to move things to the right layer but `jarmode` simplifies that. Additionally, the layout created by the `jarmode` is CDS friendly out of the box. + + + +[[packaging.container-images.dockerfiles.cds]] +== CDS + +If you want to additionally enable xref:reference:packaging/class-data-sharing.adoc[CDS], you can use this `Dockerfile`: +[source,dockerfile] +---- +include::reference:partial$dockerfile[] +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar application.jar +# Start the application jar with CDS enabled - this is not the uber jar used by the builder +# This jar only contains application code and references to the extracted jar files +# This layout is efficient to start up and CDS friendly +ENTRYPOINT ["java", "-XX:SharedArchiveFile=application.jsa", "-jar", "application.jar"] +---- + +This is mostly the same as the above `Dockerfile`. +As the last steps, it creates the CDS archive by doing a training run and passes the CDS parameter to `java -jar`. + diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile new file mode 100644 index 000000000000..8985fde04bee --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile @@ -0,0 +1,21 @@ +# Perform the extraction in a separate builder container +FROM bellsoft/liberica-openjre-debian:17-cds AS builder +WORKDIR /builder +# This points to the built jar file in the target folder +# Adjust this to 'build/libs/*.jar' if you're using Gradle +ARG JAR_FILE=target/*.jar +# Copy the jar file to the working directory and rename it to application.jar +COPY ${JAR_FILE} application.jar +# Extract the jar file using an efficient layout +RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted + +# This is the runtime container +FROM bellsoft/liberica-openjre-debian:17-cds +WORKDIR /application +# Copy the extracted jar contents from the builder container into the working directory in the runtime container +# Every copy step creates a new docker layer +# This allows docker to only pull the changes it really needs +COPY --from=builder /builder/extracted/dependencies/ ./ +COPY --from=builder /builder/extracted/spring-boot-loader/ ./ +COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ +COPY --from=builder /builder/extracted/application/ ./