Skip to content

Latest commit

 

History

History
325 lines (252 loc) · 15.3 KB

README.md

File metadata and controls

325 lines (252 loc) · 15.3 KB

Generic SpringBoot Docker files and image management

Get started with your SpringBoot App in seconds. Use your favorite builder (Gradle or Maven) and choose your version of JRE - without needing to manage a complex maze of non-business logic steps in your Dockerfiles!

Benefits

  • No more copy-and-paste Dockerfile boilerplate code for your App's Runnable Jars!
    • We use the Builder Pattern for Gradle and Maven
  • Decoupled way to run your application in different JVMs
    • Our parameterized builds allows you to just select which JVM implementation to run!
  • Enterprise-friendly: Pick and choose the base images for your applications and push to your internal Docker Registry!

Current Support

  • Builder: Any Java build system.
    • Maven different base Operating Systems.
    • Gradle different base Operating Systems.
    • Bazel Waiting for contributions.
  • Linker: Any JDK 9+ with support to jlink to generate custom JVMs tailored for your application .jar
    • OpenJDK on Alpine musl`
    • OpenJDK on Debian glibc`
  • Runner: Any JVM available to the Custom JVMs generated by the Linker for your application.

All images are located at the DockerHub Intuit Repository.

Process

  1. Create a Dockerfile selecting the first stage the Builder image

This step requires the inspection of the build system used and the target JVM instructions used. For instance, the Kotlin tutorial uses Gradle and JDK8. For this reason, the builder image selected is using Gradle with JDK8 (See the dir samples/gradle-kotlin-jdk8-jre8 for details)

  1. If the project is to run in a JRE 8, then select the Runner image with the base OS desired

Since the Kotlin tutorial simply runs the application, then we just selected the base image with JRE8. That simplified the initial process of using the current tutorial.

  1. If your team is already required to migrate to JRE11 using the current code, then select a Linker image to create a custom JRE for your application based on the .jar produced by the Builder image.

This requires a matching base image with a matching OS type. For instance, if you will run on Alpine, then select a Linker image on Alpine with the JDK11 type (See the dir samples/gradle-java-jdk8-x-jre11-custom-alpine for details)

Builder

Implementation of a set of ONBUILD instructions that are reusable for any SpringBoot Application:

  • Injection of env vars such as GIT_SHA, GIT_BRANCH and BUILD_NUMBER for versioning.
  • Supports the following builders:
    • Gradle 4.x.x + openjdk 1.8
    • Maven 3.x.x + openjdk 1.8
    • Gradle 4.x.x + openjdk 1.11
    • Gradle 5.x.x + openjdk 1.12
    • Maven 3.x.x + openjdk 1.11
    • Maven 3.x.x + openjdk 1.12

Linker

Implementation of the call using jlink to create a custom VM for your application based on the generated Jar.

  • Custom Runners will consume the linker JVM.

Runner

Implementation of all tasks to execute a runnable WAR from a SpringBoot application.

  • Uses the built executable file to the appropriate directory.
  • Prepares the image with underlying capabilities to make TLS calls.
  • Uses small JRE images.
  • Supports the following Runners:
    • JRE Slim 1.8
    • JRE Slim 1.11
    • JRE Slim 1.12
    • JRE Alpine 1.11
    • JRE Alpine 1.12

Samples

Implementation of samples using the Docker Images for both builder and runner. Look at the directory samples for details.

  • Samples with the following:
    • Java/Gradle SpringBoot 2.x with JRE Slim 1.8
    • Java/Gradle SpringBoot 2.x with JRE Slim 1.11
    • Java/Gradle SpringBoot 2.x with JRE Slim 1.12
    • Kotlin/Gradle SpringBoot 2.x with JRE Slim 1.8
    • Kotlin/Gradle SpringBoot 2.x with JRE Slim 1.11

Gradle Builder

  • Gradle version is latest 5.0.0, currently at 4.10.1 with OpenJDK 1.8

  • Uses Gradle as the builder tool and requires:

    • build.gradle: Main build file
    • settings.gradle: Project settings
  • Regular Java project using maven structure

    • src: java source directory with any code

Gradle Builder Args

Requires the use of the following build args

Gradle Builder Dockerfile

If you use gradle, declare your images with the dependency to gradle. Note that builder image must be annotated as unmazedboot-builder-artifacts, so that the runner knows where to get the built artifacts from.

  • The builder should be the first image of the Dockerfile
    • It can also include additional steps required by the builder such as installing new dependencies, etc.
FROM unmazedboot/builder:gradle4.10.2-jdk8-alpine as unmazedboot-builder-artifacts

RUN echo "You can add extra packages or anything needed to the final image for building"

Maven Builder

  • Latest Maven version with versions
  • Uses Maven as the builder tool and requires:
    • pom.xml: Main build file
    • settings.xml: Project settings
  • Regular Java project using maven structure
    • src: java source directory with any structure

Maven Builder Args

Requires the use of the following build args

  • BUILDER_GIT_SHA: the current sha of the project
  • BUILDER_GIT_BRANCH: the current branch of the project
  • BUILDER_MAVEN_BUILD_CMD: the command used to build the executable jar or war
    • mvn -s settings.xml install -P embedded
  • BUILDER_DIR: The directory containing the executable file.
    • Default Gradle directory is build/libs
    • Default Maven directory is target

Generic Runner

  • The runner image is the same for any builder supported
    • Gradle
    • Maven
  • The single image simplifies the whole process and requires a couple of args
  • Entrypoint defined as start.sh with the following capabilities
    • Sources a group of files under the /runtime/sources/*.sh dir
    • Creates JAVA_OPTS based on values under /runtime/java-opts/*.opt
    • Executes a default command provided, showing debug information

Those are the runtime information important when deploying springboot in Docker/Kubernetes environments. In addition:

  • /runtime/sources gives you a mount path to execute additional app-based hooks
  • You can use side-cars during Kubernetes deployments
  • runtime/java-opts are values that comes from sidecars or secret values

Runner Args

Requires the use of the following build args to build, providing optional values.

  • RUNNER_EXTENSION: the extension of the executable binary
    • Gradle has plenty of examples with jar and war files.
  • RUNNER_PORT: the port to use in the EXPOSE instruction in the final Docker Image
  • RUNNER_HOOKS_DIR_SOURCES: the sources directory to be executed before executing
  • RUNNER_ENV_HOOK_VAR: the name of the env to be populated with contents from RUNNER_HOOKS_ENV_VAR_DIR
    • Defaults to JAVA_OPTS
  • RUNNER_HOOKS_ENV_VAR_DIR: the directory with contents to be concatenated as value for RUNNER_ENV_HOOK_VAR
    • Those values for mem, agents, etc.
  • RUNNER_CMD_EXEC: The command to execute after the sources and var has been processed.
    1. source RUNNER_HOOKS_DIR_SOURCES/*.sh
    2. RUNNER_ENV_HOOK_VAR=concat of all RUNNER_HOOKS_ENV_VAR_DIR/*.opt
    3. RUNNER_CMD_EXEC is executed, already set for runnable springboot apps

This runnable image will require any stage of the Dockerfile to be named as unmazedboot-builder-artifacts as shown above.

Runner Dockerfile

  • The runner is the last image in the Dockerfile without any stage name.
    • It also should be specified the tag referring to the supported runtime.
  • The version supported is the tag with the kind of JRE to use.
FROM intuit/unmazedboot:runner-openjdk-8-jre-slim

RUN echo "You can include more libraries or anything in the Runnable image"

Gradle Sample

  • Provide the build args as needed.
  • You can use the default images.
    • See the samples for details
    • See the runner Dockerfile for details

Go to the directory sample directory for more details.

Build all Samples

Building all samples in parallel (docker-compose version 1.23.1, build b02f1306)

$ docker-compose -f docker-compose-samples.yaml build --parallel
Building sample-gradle-java-jre8          ...
Building sample-gradle-java-jre10         ...
Building samples-gradle-java-custom-jre10 ...
Building sample-gradle-java-jre8
Building samples-gradle-java-custom-jre10
Building sample-gradle-java-jre10

JVM Hooks

  • It's common to add the creation of JAVA_OPTS either in a shell script or a concatenation of all things needed.
    • In addition, JAVA_OPTS may depend on agents available in given environments
  • For a given environment, you can inject sources that will compute what's needed
    • In this case, something that is run before JAVA_OPTS gets created.

The support for hooks is added as a feature that uses an environment injector pattern. This is useful when dealing with containers in Kubernetes that are initialized via InitContainer and requires sources. There are 2 types as explained above and the sample is in JDK 11 cross-compiled

$ docker-compose -f docker-compose-samples.yaml up --build samples-gradle-java-jdk8-x-jre-custom-11
Building samples-gradle-java-jdk8-x-jre-custom-11
Step 1/10 : ARG BUILDER_GIT_SHA=${GIT_SHA:-0101010}
Step 2/10 : ARG BUILDER_GIT_BRANCH=${GIT_BRANCH:-develop}
Step 3/10 : ARG BUILDER_BUILD_NUMBER=${BUILD_NUMBER:-0223}
Step 4/10 : ARG BUILDER_GRADLE_DOWNLOAD_DEPENDENCIES_CMD="gradle downloadDependencies"
Step 5/10 : ARG BUILDER_GRADLE_BUILD_CMD="gradle build -x test"
Step 6/10 : ARG BUILDER_DIR="build/libs"
Step 7/10 : ARG RUNNER_EXTENSION="jar"
Step 8/10 : ARG RUNNER_PORT="8080"
Step 9/10 : FROM unmazedboot/builder:gradle-4-jdk8-x-jdk11 as unmazedboot-builder-artifacts
# Executing 18 build triggers
 ---> Using cache
...
... 
---> Using cache
 ---> dd9c8589da4c

Step 10/10 : FROM unmazedboot/runner:jre8-x-jre11-custom
# Executing 34 build triggers
 ---> Using cache
...
...
 ---> Using cache
 ---> 73f12942ee77

Successfully built 73f12942ee77
Successfully tagged sample-jdk8-x-jre11-custom:latest
Starting springboot-docker-reusable-images_samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 ... done
Attaching to springboot-docker-reusable-images_samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | => Initializing SpringBoot Runner 'start.sh'
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | => Processing env hooks at /runtime/sources
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | [1] source /runtime/sources/myself.sh
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | => Processing  hooks at /runtime/java-opts
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | [1]  << /runtime/java-opts/mem.opt
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | Exporting JAVA_OPTS= -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+UseStringDeduplication -XX:MaxRAMFraction=2 -XshowSettings:vm
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | ####### Debugging env before app start ##########
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | DEBUG_ENV=true
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | HOSTNAME=dc6ede09cd5b
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | JAVA_OPTS= -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+UseStringDeduplication -XX:MaxRAMFraction=2 -XshowSettings:vm
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | JAVA_HOME=/opt/jdk-custom/jre
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | RUNNER_EXTENSION=jar
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | BUILDER_DIR=build/libs
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | RUNNER_PORT=8080
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | RUNNER_HOOKS_ENV_VAR_DIR=/runtime/java-opts
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | PWD=/runtime
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | HOME=/root
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | NAME=Marcello
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | RUNNER_CMD_EXEC=java $JAVA_OPTS -jar /runtime/server.jar $JAR_OPTS
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | APP_TIMEZONE=PST
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | CITY=San Diego
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | SHLVL=1
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | RUNNER_HOOKS_DIR_SOURCES=/runtime/sources
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/jdk-custom/jre/bin
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | _=/usr/bin/env
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | Starting the app
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | java $JAVA_OPTS -jar /runtime/server.jar $JAR_OPTS
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | OpenJDK 64-Bit Server VM warning: Option MaxRAMFraction was deprecated in version 10.0 and will likely be removed in a future release.
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | VM settings:
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |     Max. Heap Size (Estimated): 3.89G
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |     Using VM: OpenJDK 64-Bit Server VM
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |   .   ____          _            __ _ _
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |   '  |____| .__|_| |_|_| |_\__, | / / / /
samples-gradle-java-jdk8-x-jre-custom-11_1_76ab77ce7bd7 |  =========|_|==============|___/=/_/_/_/
  • Execute the sample for more details

Start all samples

$ docker-compose -f docker-compose-samples.yaml up

Contributing

Builds

  • We use GitLab Build Pipelines
  • Deployments are from the Master Branch
  • Docker Images will be pushed to Docker Registry available at hub.docker.com