{"id":20985049,"url":"https://github.com/maeddes/java-and-container","last_synced_at":"2025-04-09T10:05:42.930Z","repository":{"id":37742648,"uuid":"347469808","full_name":"maeddes/java-and-container","owner":"maeddes","description":"Sample code and instructions for steps through different container image build options.","archived":false,"fork":false,"pushed_at":"2025-03-27T10:48:51.000Z","size":4754,"stargazers_count":84,"open_issues_count":0,"forks_count":13,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-02T08:49:40.429Z","etag":null,"topics":["buildpacks","docker","dockerfile","jib","mav","paketo-buildpack"],"latest_commit_sha":null,"homepage":"","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maeddes.png","metadata":{"files":{"readme":"README.adoc","changelog":"history","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-13T20:16:53.000Z","updated_at":"2025-03-28T18:50:09.000Z","dependencies_parsed_at":"2024-11-19T06:07:25.425Z","dependency_job_id":"1ed867d9-62c2-4ea4-8952-a013b58130f1","html_url":"https://github.com/maeddes/java-and-container","commit_stats":null,"previous_names":["maeddes/java-and-container"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maeddes%2Fjava-and-container","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maeddes%2Fjava-and-container/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maeddes%2Fjava-and-container/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maeddes%2Fjava-and-container/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maeddes","download_url":"https://codeload.github.com/maeddes/java-and-container/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248018060,"owners_count":21034048,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["buildpacks","docker","dockerfile","jib","mav","paketo-buildpack"],"created_at":"2024-11-19T05:55:17.677Z","updated_at":"2025-04-09T10:05:42.899Z","avatar_url":"https://github.com/maeddes.png","language":"CSS","readme":"= Lab/Walkthrough instructions - Container Builds\n:sectnums:\n:toc:\n\nimage::pics/001-overview.png[Container Build Options,640]\n\n== About\n\nThis lab will walk you through steps to build container images with various technologies.\n\n* Slides: https://speakerdeck.com/maeddes/options-galore-from-source-code-to-container-image\n* Recording (English): https://www.youtube.com/watch?v=HFhIqfKn_XIdock\n* Recording (German): https://www.youtube.com/watch?v=ga8iqQ25lUY\n* Carlos Repo: https://github.com/carlosbarragan/java-containers-demo\n\n== Prereqs\n\nMandatory:\n\n* A Docker environment and Docker CLI https://docs.docker.com/get-docker/\n* Pack CLI for Cloud-Native Buildpacks https://buildpacks.io/docs/tools/pack/\n* Clone/Download this repo: https://github.com/maeddes/options-galore-container-build\n\nRecommended:\n\n* A Java (21 or later) Development Kit for Java examples, e.g https://adoptopenjdk.net/\n\nOptional:\n\n* Dive tool https://github.com/wagoodman/dive\n\nLinks:\n\n* https://home.robusta.dev/blog/stop-using-cpu-limits\n\n=== Validation\n\nValidate docker installation.\n\n[source]\n----\ndocker version\n----\n\nShould display output like (version might differ):\n\n----\nClient: Docker Engine - Community\n Version:           25.0.3\n API version:       1.44\n...\n\nServer: Docker Engine - Community\n Engine:\n  Version:          25.0.3\n  API version:      1.44 (minimum version 1.24)\n----\n\nValidate Java.\n\n[source]\n----\njava --version\n----\n\nShould display output like (version might differ):\n\n----\nopenjdk 21.0.7 2023-04-18\nOpenJDK Runtime Environment (build 21.0.7+7-Ubuntu-0ubuntu120.04)\nOpenJDK 64-Bit Server VM (build 21.0.7+7-Ubuntu-0ubuntu120.04, mixed mode, sharing)\n----\n\n== Dockerfile Exercises\n\n=== Set environment and build code\n\nDownload/clone the repo and change to the root folder. If you are running in gitpod,codespaces or using devcontainer, you can skip this step.\n[source, bash]\n----\ngit clone https://github.com/maeddes/options-galore-container-build\n----\n\n\nNote: Without git CLI you can download the repo as zip file here: https://github.com/maeddes/options-galore-container-build/archive/refs/heads/main.zip\nExtract it and change your command line shell to the root folder.\n\n[source, bash]\n----\ncd options-galore-container-build\n----\n\nBuild the code:\n\nChange to the Java sample app \n[source, bash]\n----\ncd java\n----\n\nOption 1 (with local JDK installed)\n[source]\n----\n./mvnw clean package\n----\n\n\n\nValidate build artefact (timestamps will of course be different)\n[source]\n----\nls -ltr ./target/simplecode-0.0.1-SNAPSHOT.jar\n----\n----\n-rw-r--r-- 1 root root 20951064 May  5 11:47 ./target/simplecode-0.0.1-SNAPSHOT.jar\n----\n\n=== Classic Dockerfile\n\nimage::pics/050-Dockerfile.png[Classic Dockerfile]\n\nObserve contents of Dockerfile-simple-ubuntu\n\n[source]\n----\ncat Dockerfile-simple-ubuntu\n----\n\n----\nFROM ubuntu:22.04\nRUN apt update \u0026\u0026 apt install openjdk-21-jre-headless -y\nCOPY target/simplecode-0.0.1-SNAPSHOT.jar /opt/app.jar\nCMD [\"java\",\"-jar\",\"/opt/app.jar\"]\n----\n\nBuild first image with this Dockerfile:\n\n[source]\n----\ndocker build -f Dockerfile-simple-ubuntu -t java-app:v-simple-ubuntu .\n----\n\nBuild images with other predefined base images:\n\n[source]\n----\ndocker build -f Dockerfile-simple-temurin -t java-app:v-simple-temurin .\n----\n\n[source]\n----\ndocker build -f Dockerfile-simple-ibm-semeru -t java-app:v-simple-ibm-semeru .\n----\n\nValidate images in local repo\n\n[source]\n----\ndocker images\n----\n\n----\nREPOSITORY   TAG                    IMAGE ID       CREATED              SIZE\njava-app     v-simple-ibm-semeru   3a7c058097d9   8 seconds ago    300MB\njava-app     v-simple-temurin      62c5ca75dad1   32 seconds ago   292MB\njava-app     v-simple-ubuntu       a491383f3f53   2 minutes ago    400MB----\n----\n\nObserve build history and differences of the 3 images\n\n[source]\n----\ndocker history java-app:v-simple-ubuntu\ndocker history java-app:v-simple-temurin\ndocker history java-app:v-simple-ibm-semeru\n----\n\nYou will observe different base layers and structure, but always the same top layer: \n----\nIMAGE          CREATED         CREATED BY                                      SIZE      COMMENT\n7209f28736c8   3 minutes ago   /bin/sh -c #(nop)  CMD [\"java\" \"-jar\" \"/opt/…   0B\ne5385e2e3146   3 minutes ago   /bin/sh -c #(nop) COPY file:90a1db2252f31169…   19MB\n----\n\nOptional: Use tool \"dive\" to show detailed history of image:\n\n[source]\n----\ndive java-app:v-simple-ubuntu\n----\n[source]\n----\ndive java-app:v-simple-temurin\n----\n[source]\n----\ndive java-app:v-simple-ibm-semeru\n----\n\nUsage: ctrl+l (ensure layer changes) \u003ctab\u003e ctrl+u (uncheck unmodified) \u003ctab\u003e \u003carrows\u003e for layer switch\n\n=== Multi-Stage\n\nimage::pics/055-Dockerfile-Buildkit-parallel.png[Multi-Stage Dockerfiles]\n\nBuild image with Multistage Dockerfile:\n\n[source]\n----\ndocker build -f Dockerfile-multistage-builder -t java-app:v-multistage-builder .\n----\n\nThis will take a while as all the maven dependencies need to be downloaded.\n\nValidate history:\n\n[source]\n----\ndocker history java-app:v-multistage-builder\n----\n\nExplore docker images: \n\n[source]\n----\ndocker images\n----\n\n----\nREPOSITORY     TAG                     IMAGE ID       CREATED          SIZE\njava-app     v-multistage-builder   816512fee0cd   21 seconds ago   291MB\n----\n\nPerform a slight modification in the source code which does not affect the behaviour of the application.\nYou can use the editor 'nano' to execute this:\n\n[source]\n----\nnano src/main/java/de/maeddes/simplecode/SimplecodeApplication.java\n----\n\nLocate the method hello()\n\n[java]\n----\n        @GetMapping(\"/\")\n        String hello(){\n\n                logger.info(\"Call to hello method on instance: \" + getInstanceId());\n                return getInstanceId()+\" Hello, Container people ! \";\n\n        }\n----\n\nand just add some characters to the method name, e.g.\n\n[java]\n----\n        String helloABC(){\n----\n\nAnd save it using Ctrl+X and confirm with 'Y'.\n\nNow you can repeat the docker build call.\n\n[source]\n----\ndocker build -f Dockerfile-multistage-builder -t java-app:v-multistage-builder .\n----\n\nYou can observe that all the dependencies will need to get downloaded again. This method does not cache anything.\n\n=== BuildKit\n\n\nBuild with multistage cache option: \n\nimage::pics/056-Dockerfile-MountCache.png[Dockerfile with Cache]\n\n[source]\n----\ndocker build -f Dockerfile-multistage-cache -t java-app:v-multistage-cache .\n----\n\nChange the code and rebuild: \n\nYou can use an editor to change a method name in\n----\nsrc/main/java/de/maeddes/simplecode/SimplecodeApplication.java\n----\nor simply execute\n\n[source]\n----\nsed -i 's/hello/helloABC/g' src/main/java/de/maeddes/simplecode/SimplecodeApplication.java\n----\n(Linux)\n\nor\n\n[source]\n----\nsed -i '' 's/hello/helloABC/g' src/main/java/de/maeddes/simplecode/SimplecodeApplication.java\n----\n(Mac)\n\nRebuild and observe faster build through caching: \n\n[source]\n----\ndocker build -f Dockerfile-multistage-cache -t java-app:v-multistage-cache .\n----\n\nObserve the history to validate that top layer is still 'monolithic': \n\n[source]\n----\ndocker history java-app:v-multistage-cache\n----\n\nBuild the code with a layered jar approach: \n\nimage::pics/061-considerations.png[Layer considerations for Java]\n\n[source]\n----\ndocker build -f Dockerfile-multistage-layered -t java-app:layered .\n----\n\nDisplay layered state\n\n[source]\n----\ndocker history java-app:layered\n----\n\n----\nIMAGE          CREATED         CREATED BY                                      SIZE      COMMENT\nde2cb7c4be82   8 seconds ago   ENTRYPOINT [\"java\" \"org.springframework.boot…   0B        buildkit.dockerfile.v0\n\u003cmissing\u003e      8 seconds ago   COPY application/application/ ./ # buildkit     6.12kB    buildkit.dockerfile.v0\n\u003cmissing\u003e      8 seconds ago   COPY application/snapshot-dependencies/ ./ #…   0B        buildkit.dockerfile.v0\n\u003cmissing\u003e      8 seconds ago   COPY application/spring-boot-loader/ ./ # bu…   245kB     buildkit.dockerfile.v0\n\u003cmissing\u003e      8 seconds ago   COPY application/dependencies/ ./ # buildkit    18.9MB    buildkit.dockerfile.v0\n----\n\nFinally have a look at the Dockerfile with specific JVM flags:\n\n[source]\n----\ncat Dockerfile-multistage-layered-jvm-flags \n----\n\nin the final line you can see how to apply alternative settings here.\n\n----\nENTRYPOINT [\"java\",\"-XX:+UseParallelGC\",\"-XX:MaxRAMPercentage=75\",\"org.springframework.boot.loader.JarLauncher\"]\n----\n\n\n== Jib\n\nThe following steps show how to build container images with the jib-maven plugin.\n\nimage::pics/090-jib.png[Jib from Google]\n\nAgain the use of the local maven wrapper (mvnw) will require a local JDK installation.\nIf it's not present use option 2.\n\nOption 1: \n[source]\n----\n./mvnw compile com.google.cloud.tools:jib-maven-plugin:3.4.4:dockerBuild -Dimage=java-app:jib -Djib.from.image=eclipse-temurin:21-jre\n----\n\nIn this case the *:dockerBuild* part will instruct the plugin to build to the local docker daemon.\nThe *-Dimage* parameter will specify the image name tag.\n\nIf you have a docker account you can login and push directly to the docker hub using:\n(Replace \u003cdocker_id\u003e with your own username)\n\n[source]\n----\n./mvnw compile com.google.cloud.tools:jib-maven-plugin:3.4.4:build -Dimage=\u003cdocker_id\u003e/java-app:jib -Djib.from.image=eclipse-temurin:21-jre\n----\n\nAnother option is to export the image directly to a tar. Use the following command.\n\n[source]\n----\n./mvnw compile com.google.cloud.tools:jib-maven-plugin:3.4.4:buildTar -Dimage=java-app:jib -Djib.from.image=eclipse-temurin:21-jre\n----\n\nYou will see an output saying\n\nAfter that you can import the image into the local registry.\n\n[source]\n----\ndocker load -i target/jib-image.tar\n----\n\n----\nnot showing any more\n15bbc04e2cf6: Loading layer [==================================================\u003e]  41.71MB/41.71MB\n7f270d883779: Loading layer [==================================================\u003e]  16.82MB/16.82MB\n496ff124a7de: Loading layer [==================================================\u003e]     213B/213B\n965a8d44c836: Loading layer [==================================================\u003e]  1.345kB/1.345kB\n5e91304a655b: Loading layer [==================================================\u003e]     219B/219B\nLoaded image: java-app:jib\n----\n\nOption 2: \n\nWithout local maven you can only perform the tar build and direct import via load.\n\n[source]\n----\ndocker run -it --rm --name my-maven-project -v \"$(pwd)\":/opt/app -w /opt/app maven:3.6.3-jdk-11 mvn compile com.google.cloud.tools:jib-maven-plugin:3.3.1:buildTar -Dimage=java-app:jib\n----\n\nLoad the exported tar file as image into the local registry.\n\n[source]\n----\ndocker load -i target/jib-image.tar\n----\n\n----\n15bbc04e2cf6: Loading layer [==================================================\u003e]  41.71MB/41.71MB\n7f270d883779: Loading layer [==================================================\u003e]  16.82MB/16.82MB\n496ff124a7de: Loading layer [==================================================\u003e]     213B/213B\n965a8d44c836: Loading layer [==================================================\u003e]  1.345kB/1.345kB\n5e91304a655b: Loading layer [==================================================\u003e]     219B/219B\nLoaded image: java-app:jib\n----\n\nBoth options - final steps:\n\nNow that you've built and loaded the image into the local registry using one of the options above, inspect the layered structure of the image.\n\n[source]\n----\ndocker history java-app:jib\n----\n\n----\nIMAGE          CREATED        CREATED BY                                      SIZE      COMMENT\n2275828677a8   N/A            jib-maven-plugin:3.4.4                          1.66kB    jvm arg files\n\u003cmissing\u003e      N/A            jib-maven-plugin:3.4.4                          2.46kB    classes\n\u003cmissing\u003e      N/A            jib-maven-plugin:3.4.4                          1B        resources\n\u003cmissing\u003e      N/A            jib-maven-plugin:3.4.4                          23.1MB    dependencies\n----\n\nOptional: Perform some small modifications in the code similar to the ones during the Dockerfile exercise.\nRe-run the build steps and observe the caching and improved performance.\n\nNote: All of the previous examples referenced the jib plugin directly in the maven call. An alternative (and probably the clean way) to the steps above is to add the plugin to your pom.xml:\n\nThe \u003cto\u003e tag in the following xml sets the target image path in the image registry. In our case we are using the local registry and thus just providing the image tag. \n\nYou can add the following plugin to your pom.xml\n[source]\n----\n\u003cplugin\u003e\n    \u003cgroupId\u003ecom.google.cloud.tools\u003c/groupId\u003e\n    \u003cartifactId\u003ejib-maven-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e3.4.4\u003c/version\u003e\n    \u003cconfiguration\u003e\n        \u003cfrom\u003e\n            \u003cimage\u003eeclipse-temurin:21-jre\u003c/image\u003e\n        \u003c/from\u003e       \n        \u003cto\u003e\n            \u003cimage\u003ejava-app:jib-v2.0\u003c/image\u003e\n        \u003c/to\u003e\n    \u003c/configuration\u003e\n\u003c/plugin\u003e\n----\n\nIn this case the invocation looks much simpler.\n\n[source]\n----\n./mvnw compile jib:dockerBuild\n----\n\nThe *:build* and *:buildTar* options work accordingly.\n\nIt is of course also possible to define custom JVM arguments with Jib. However this is not possible with a plain mvn call.\nYou also can of course apply these settings not during build time, but when starting the container:\n\n[source]\n----\ndocker run --env JAVA_TOOL_OPTIONS='-XX:+UseParallelGC -XX:MaxRAMPercentage=75' java-app:jib\n----\n\n\n== Cloud-native buildpacks\n\nimage::pics/104-buildpacks-flow.png[Cloud-Native Buildpacks]\n\nAccess the pack CLI and list the suggest builders. A builder includes the buildpacks and environment that will be used for building and running your app.\n\n\n[source]\n----\npack builder suggest\n----\n\nSet a default builder to avoid specifying a builder every time you build. For the examples in this tutorial use the base builder image from Paketo buildpacks.\n\n[source]\n----\npack config default-builder paketobuildpacks/builder-jammy-base\n----\n\n\n\nNow all is set to build the container image using the buildpack. Simply execute:\n\n[source]\n----\npack build java-app:pack\n----\n\n\n\nThe first invocation will take a long time. The builder image is big as it contains all the logic plus buildpacks.\n\nAfter it is downloaded can now observe the output - the so-called bill of materials.\nThis gives detailed information about the build.\n\nShould display output like:\n----\n===\u003e ANALYZING\n...\n===\u003e DETECTING\n...\n===\u003e RESTORING\n===\u003e BUILDING\n...\n===\u003e EXPORTING\n...\n\nSuccessfully built image java-app:pack^\n----\n\n\nOptimize the build with:\n\n----\npack build java-app:pack-compressed --env BP_JVM_JLINK_ENABLED=true\n----\n\nIf you want to configure specific JVM settings with Paketo Buildpacks you can extend the call to use alternative configuration:\n\n[source]\n----\npack build -e BPE_APPEND_JAVA_TOOL_OPTIONS='-XX:+UseParallelGC -XX:MaxRAMPercentage=75' -e BPE_DELIM_JAVA_TOOL_OPTIONS=' ' java-app:pack\n----\n\n\nPaketo buildpacks can be configured using different  for external configuration (Environment Variables, buildpack.yml, Bindings, Procfiles). \n\nUse an environment variable to configure the JVM version installed by the Java Buildpack and build a new version of the container image\n\n[source]\n----\npack build java-app:pack-v2.0 --env BP_JVM_VERSION=11\n----\n\nObserve the usage of (JDK 11.0.19, JRE 11.0.19) in the BUILDING phase of the output.\n\n\nGet an overview of the built Images\n\n[source]\n----\ndocker images\n----\n\nUsing pack it is possible to swap out the underlying OS layers (run image) of an app image with another run image version, without re-building the application. \n\nRebase app image with a version pinned run image \n\n[source]\n----\npack rebase java-app:pack --run-image paketobuildpacks/run:1.3.48-full-cnb\n----\n\nShould display output like:\n\n----\n1.3.48-full-cnb: Pulling from paketobuildpacks/run\n83525de54a98: Already exists\nc1dbbbd2a415: Pull complete\n283105c565ee: Pull complete\n7ead7caf102c: Pull complete\nDigest: sha256:005e54c4254bd49fa5b0b55fd7b7f16a2654bc6643963dece1cd03f7a0abce24\nStatus: Downloaded newer image for paketobuildpacks/run:1.3.48-full-cnb\nRebasing java-app:pack on run image paketobuildpacks/run:1.3.48-full-cnb\nSaving java-app:pack...\n*** Images (a938edc476a8):\n      java-app:pack\nRebased Image: a938edc476a85ab53d6aa52a5cc6288c1dffdafd9b3654236cf8b62bbce70a83\nSuccessfully rebased image java-app:pack\n----\n\n\n\n== Paketo with Spring Boot and Maven\n\nimage::pics/108-paketo-springboot.png[Paketo, Spring Boot, Maven]\n\nFor a Spring Boot application you can also invoke Paketo Buildpacks directly via maven.\n\n[source]\n----\n./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=java-app:paketo\n----\n\n\nAfter compiling and testing the code within a standard Maven build, the build-image phase appears in the build log, in which you should observe display output like:\n\n----\n===\u003e DETECTING\n...\n===\u003e ANALYZING\n...\n===\u003e RESTORING\n===\u003e BUILDING\n...\n===\u003e EXPORTING\n...\nSuccessfully built image 'docker.io/library/java-app:paketo'\n----\n\n\nGet an overview of the built Images\n\n\n== Options\n\nYou have now completed the core exercise.\nFeel free to do some modifications yourself.\nSuggestions:\n* Edit the pom.xml and alternate the Java version (8,11,21 have been tested).\n* Do minor or major code modifications and observe changes\n* Use dive to analyze the created images.\n\n\n(C) Matthias Haeussler. Free for private purposes. (Re)distribution for commercial purposes not allowed without owner permissions.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaeddes%2Fjava-and-container","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaeddes%2Fjava-and-container","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaeddes%2Fjava-and-container/lists"}