Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/intuit/unmazedboot

🐳 Generic SpringBoot Docker files and image management 🍃
https://github.com/intuit/unmazedboot

alpine alpine-linux centos custom-jre debian docker docker-image java11 java8 jdk-jlink jdk11 jdk8 jlink jre11 jre8 slim springboot springboot2

Last synced: 3 months ago
JSON representation

🐳 Generic SpringBoot Docker files and image management 🍃

Awesome Lists containing this project

README

        

# 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.
* [x] `Maven` different base Operating Systems.
* [x] `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`
* [x] `OpenJDK on Alpine `musl`
* [x] `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](https://cloud.docker.com/u/intuit/repository/list?name=unmazedboot&namespace=intuit).

## 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](https://spring.io/guides/tutorials/spring-boot-kotlin/) 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)

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

Since the [Kotlin tutorial](https://spring.io/guides/tutorials/spring-boot-kotlin/) simply runs the application, then we just selected the base image with JRE8. That simplified the initial process of using the current tutorial.

3. 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:
* [x] Gradle 4.x.x + openjdk 1.8
* [x] Maven 3.x.x + openjdk 1.8
* [x] Gradle 4.x.x + openjdk 1.11
* [x] Gradle 5.x.x + openjdk 1.12
* [x] Maven 3.x.x + openjdk 1.11
* [x] 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:
* [x] JRE Slim 1.8
* [x] JRE Slim 1.11
* [x] JRE Slim 1.12
* [x] JRE Alpine 1.11
* [x] 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:
* [x] Java/Gradle SpringBoot 2.x with JRE Slim 1.8
* [x] Java/Gradle SpringBoot 2.x with JRE Slim 1.11
* [x] Java/Gradle SpringBoot 2.x with JRE Slim 1.12
* [x] 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

* `BUILDER_GIT_SHA`: the current sha of the project
* `BUILDER_GIT_BRANCH`: the current branch of the project
* `BUILDER_GRADLE_BUILD_CMD`: the command used to build the executable `jar` or `war`
* `gradle bootRepackage` or `gradle build`
* https://docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/html/build-tool-plugins-gradle-plugin.html#build-tool-plugins-gradle-packaging

## 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.

```dockerfile
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.

```dockerfile
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

* See [Contributing](https://github.com/intuit/unmazedboot/blob/master/.github/CONTRIBUTING.md)

# 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