Skip to content

Commit

Permalink
add support for bundled dockerfileSet
Browse files Browse the repository at this point in the history
i.e.
dockerprepare{
	dockerfileSet '8u151-jre-alpine-buildlabels'
}
  • Loading branch information
gclayburg committed Mar 9, 2018
1 parent 1c01c0f commit cbb615f
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 8 deletions.
29 changes: 25 additions & 4 deletions README.md
Expand Up @@ -22,7 +22,7 @@ To use this, add this snippet to your build.gradle file or use the example at [s

```groovy
plugins {
id "com.garyclayburg.dockerprepare" version "1.3.1"
id "com.garyclayburg.dockerprepare" version "1.3.2"
}
```
The latest version with detailed install instructions can be found on the [gradle plugin portal](https://plugins.gradle.org/plugin/com.garyclayburg.dockerprepare)
Expand Down Expand Up @@ -85,7 +85,7 @@ This layer is created automatically by this plugin. It contains all dependent j

### build/docker/classesLayer3/

This layer is created automatically by this plugin. It contains everything else isn't in `build/docker/commonServiceDependenciesLayer1` or `build/docker/dependenciesLayer2`
This layer is created automatically by this plugin. It contains everything else that isn't in `build/docker/commonServiceDependenciesLayer1` or `build/docker/dependenciesLayer2`

# Quickstart with a new demo app

Expand Down Expand Up @@ -120,13 +120,13 @@ Notice the output generated when we stopped the app. Our ctrl-c on the terminal

This is just a simple Spring Boot application that runs an embedded Tomcat server with our simple app along with all its dependencies. Everything we need is in this one jar file - well everything except the JVM to run it and supporting OS libraries. Docker to the rescue!

## adding the preparedocker plugin
## adding the dockerprepare plugin
What we want to do now is take this app and bundle it inside a docker container with the JVM and everything the JVM needs.

1. Add this to your build.gradle file. Or use the example at [sample/demo](sample/demo)
```groovy
plugins {
id "com.garyclayburg.dockerprepare" version "1.3.1"
id "com.garyclayburg.dockerprepare" version "1.3.2"
}
```
2. Now run the build again and check the `build/docker` directory
Expand Down Expand Up @@ -445,6 +445,27 @@ dockerprepare{
}
```
### Use alternative Dockerfile as default
New in version 1.3.2, you can specify an alternative Dockerfile Set instead of creating your own and copying them between projects. It is configured like this:
```groovy
dockerprepare{
dockerfileSet '8u151-jre-alpine-buildlabels'
}
```
This will grab the [bundled Dockerfile and bootrunner.sh](src/main/resources/8u151-jre-alpine-buildlabels)
Or
```groovy
dockerprepare{
dockerfileSet '90111-jre-sid-buildlabels'
}
```
to try your build with a [Java 9 based Dockerfile](src/main/resources/90111-jre-sid-buildlabels)
Check out [Memuser](https://github.com/gclayburg/memuser) for an example of a customized project
# Errors
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -34,7 +34,7 @@ dependencies {
}

group = 'com.garyclayburg'
version = '1.3.2-SNAPSHOT'
version = '1.3.2'

uploadArchives {
repositories {
Expand Down
Expand Up @@ -190,6 +190,7 @@ class DockerPreparePlugin implements Plugin<Project> {
settings = project.extensions.create(DOCKERPREPARE_EXTENSION, DockerPreparePluginExt, project)
settings.dockerSrcDirectory = "${project.rootDir}/src/main/docker"
settings.dockerBuildDirectory = "${project.buildDir}/docker"
settings.dockerfileSet = "defaultdocker"
settings
}

Expand All @@ -206,8 +207,10 @@ class DockerPreparePlugin implements Plugin<Project> {
[settings.dockerBuildDirectory + "/Dockerfile", settings.dockerBuildDirectory + "/bootrunner.sh"]
}
doLast {
def dockerstream = this.getClass().getResourceAsStream('/defaultdocker/Dockerfile')
def bootrunnerstream = this.getClass().getResourceAsStream('/defaultdocker/bootrunner.sh')
def dockerfile = "/${settings.dockerfileSet}/Dockerfile"
def bootrunnerfile = "/${settings.dockerfileSet}/bootrunner.sh"
def dockerstream = this.getClass().getResourceAsStream(dockerfile)
def bootrunnerstream = this.getClass().getResourceAsStream(bootrunnerfile)
if (dockerstream != null && bootrunnerstream != null) {
getLogger().info "Copy opinionated default Dockerfile and bootrunner.sh into ${settings.dockerBuildDirectory} "
project.mkdir(settings.dockerBuildDirectory)
Expand All @@ -218,11 +221,12 @@ class DockerPreparePlugin implements Plugin<Project> {
2. testing via gradle test kit where Dockerfile is a normal file on the classpath
*/
} else {
getLogger().error('Cannot copy opinionated default Dockerfile and bootrunner.sh')
getLogger().error("Cannot copy opinionated default Dockerfile and bootrunner.sh from classpath \n ${dockerfile}\n ${bootrunnerfile}")
project.buildscript.configurations.classpath.findAll {
getLogger().error "classpath entry ${it.path}"
}
printDir(project.buildDir.getPath(), getLogger())
throw new IllegalStateException('Cannot copy opinionated default Dockerfile and bootrunner.sh')
}
}
}.onlyIf {
Expand Down
Expand Up @@ -238,6 +238,33 @@ documentation to work in a variety of cases
*/
List<String> commonService = []

/**
* Instead of using the default {@code Dockerfile} and {@code bootrunner.sh}, you can specify a different
* pre-packaged {@code Dockerfile} and {@code bootrunner.sh}:
*
* <br><br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/90111-jre-sid-buildlabels">90111-jre-sid-buildlabels</a>
* <pre>
* dockerfileSet = '90111-jre-sid-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/8u151-jre-alpine-buildlabels">8u151-jre-alpine-buildlabels</a>
* <pre>
* dockerfileSet = '8u151-jre-alpine-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/defaultdocker">defaultdocker</a>
* <pre>
* dockerfileSet = 'defaultdocker'
* </pre>
*
* If not specified, the {@code Dockerfile} and {@code bootrunner.sh} file from the dockerfileSet defaultdocker will be used, unless you also have files in {@link #dockerSrcDirectory}
*/
String dockerfileSet

DockerPreparePluginExt(Project project) {
this.project = project
}
Expand Down Expand Up @@ -361,4 +388,61 @@ documentation to work in a variety of cases
this.commonServiceDependenciesDirectory = dockerBuildDirectory + "/commonServiceDependenciesLayer1"
}

/**
* Instead of using the default {@code Dockerfile} and {@code bootrunner.sh}, you can specify a different
* pre-packaged {@code Dockerfile} and {@code bootrunner.sh}:
*
* <br><br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/90111-jre-sid-buildlabels">90111-jre-sid-buildlabels</a>
* <pre>
* dockerfileSet = '90111-jre-sid-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/8u151-jre-alpine-buildlabels">8u151-jre-alpine-buildlabels</a>
* <pre>
* dockerfileSet = '8u151-jre-alpine-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/defaultdocker">defaultdocker</a>
* <pre>
* dockerfileSet = 'defaultdocker'
* </pre>
*
* If not specified, the {@code Dockerfile} and {@code bootrunner.sh} file from the dockerfileSet defaultdocker will be used, unless you also have files in {@link #dockerSrcDirectory}
*/
void setDockerfileSet(String dockerfileSet){
this.dockerfileSet(dockerfileSet)
}

/**
* Instead of using the default {@code Dockerfile} and {@code bootrunner.sh}, you can specify a different
* pre-packaged {@code Dockerfile} and {@code bootrunner.sh}:
*
* <br><br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/90111-jre-sid-buildlabels">90111-jre-sid-buildlabels</a>
* <pre>
* dockerfileSet = '90111-jre-sid-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/8u151-jre-alpine-buildlabels">8u151-jre-alpine-buildlabels</a>
* <pre>
* dockerfileSet = '8u151-jre-alpine-buildlabels'
* </pre>
*
* <br>
* To use files from <a href="https://github.com/gclayburg/dockerPreparePlugin/tree/master/src/main/resources/defaultdocker">defaultdocker</a>
* <pre>
* dockerfileSet = 'defaultdocker'
* </pre>
*
* If not specified, the {@code Dockerfile} and {@code bootrunner.sh} file from the dockerfileSet defaultdocker will be used, unless you also have files in {@link #dockerSrcDirectory}
*/
void dockerfileSet(String dockerfileset){
this.dockerfileSet = dockerfileset
}
}
59 changes: 59 additions & 0 deletions src/main/resources/8u151-jre-alpine-buildlabels/Dockerfile
@@ -0,0 +1,59 @@
FROM openjdk:8u151-jre-alpine
# We choose this base image because:
# 1. it is the latest Java 8 version on alpine as of March 2018
# 2. jre-alpine instead of jdk-alpine is much smaller but still enough to
# run most microservice applications on the JVM
# 3. jre-alpine has a smaller security footprint than other official Java docker images
# 4. the explicit version number means the build will be repeatable
# i.e. not dependent on what :latest version may have been pulled from a
# docker registry before.

RUN adduser -D -s /bin/sh springboot
COPY ./bootrunner.sh /home/springboot/bootrunner.sh
RUN chmod 755 /home/springboot/bootrunner.sh && chown springboot:springboot /home/springboot/bootrunner.sh
WORKDIR /home/springboot
USER springboot
# We add a special springboot user for running our application.
# Java applications do not need to be run as root

ADD commonServiceDependenciesLayer1 /home/springboot/app/
# This layer is composed of all transitive dependencies of a
# commonService, e.g in your build.gradle:
#
#dockerprepare {
# commonService = ['org.springframework.boot:spring-boot-starter-web']
#}
#
# All 30 jar files pulled in from spring-boot-starter-web are added to this layer

ADD dependenciesLayer2/ /home/springboot/app/
# This layer contains dependent jar files of the app that aren't a
# commonService. Most of the time,
# having dependencies in this layer will take advantage of the docker build
# cache. This will give you faster build times, faster image
# uploads/downloads and reduced storage requirements.
# This layer is computed automatically from your spring boot application

ADD classesLayer3/ /home/springboot/app/
# This layer contains your application classes. It will
# likely change on each docker image build so we expect a docker cache miss.
# This layer is computed automatically from your spring boot application

ARG ORG_LABEL_SCHEMA_VCS_REF
ARG ORG_LABEL_SCHEMA_VCS_URL
ARG ORG_LABEL_SCHEMA_BUILD_DATE
ARG ORG_LABEL_SCHEMA_VERSION
ARG ORG_LABEL_SCHEMA_DESCRIPTION
ARG MAINTAINER
LABEL maintainer=${MAINTAINER:-"NA"} \
org.label-schema.vcs-ref=${ORG_LABEL_SCHEMA_VCS_REF} \
org.label-schema.vcs-url=${ORG_LABEL_SCHEMA_VCS_URL} \
org.label-schema.build-date=${ORG_LABEL_SCHEMA_BUILD_DATE} \
org.label-schema.version=${ORG_LABEL_SCHEMA_VERSION} \
org.label-schema.description=${ORG_LABEL_SCHEMA_DESCRIPTION}

VOLUME /tmp
EXPOSE 8080
ENV JAVA_OPTS="" \
SPRING_OUTPUT_ANSI_ENABLED=ALWAYS
ENTRYPOINT ["./bootrunner.sh"]
26 changes: 26 additions & 0 deletions src/main/resources/8u151-jre-alpine-buildlabels/bootrunner.sh
@@ -0,0 +1,26 @@
#!/bin/sh
date_echo(){
datestamp=$(date "+%F %T")
echo "${datestamp} $*"
}
#exec the JVM so that it will get a SIGTERM signal and the app can shutdown gracefully

if [ -d "${HOME}/app/WEB-INF" ]; then
#execute springboot expanded war, which may have been constructed from several image layers
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp ${HOME}/app org.springframework.boot.loader.WarLauncher $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp "${HOME}/app" org.springframework.boot.loader.WarLauncher "$@"
elif [ -d "${HOME}/app" ]; then
#execute springboot expanded jar, which may have been constructed from several image layers
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp ${HOME}/app org.springframework.boot.loader.JarLauncher $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp "${HOME}/app" org.springframework.boot.loader.JarLauncher "$@"
elif [ -f "${HOME}/app.jar" ]; then
# execute springboot jar
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar ${HOME}/app.jar $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar "${HOME}/app.jar" "$@"
else
date_echo "springboot application not found in ${HOME}/app or ${HOME}/app.jar"
exit 1
fi
53 changes: 53 additions & 0 deletions src/main/resources/90111-jre-sid-buildlabels/Dockerfile
@@ -0,0 +1,53 @@
FROM openjdk:9.0.1-11-jre-sid
# We choose this base image because:
# 1. it is the latest Java 9 version on alpine as of March 2018

RUN adduser --shell /bin/bash --home /home/springboot --gecos springboot --disabled-password springboot
COPY ./bootrunner.sh /home/springboot/bootrunner.sh
RUN chmod 755 /home/springboot/bootrunner.sh && chown springboot:springboot /home/springboot/bootrunner.sh
WORKDIR /home/springboot
USER springboot
# We add a special springboot user for running our application.
# Java applications do not need to be run as root

ADD commonServiceDependenciesLayer1 /home/springboot/app/
# This layer is composed of all transitive dependencies of a
# commonService, e.g in your build.gradle:
#
#dockerprepare {
# commonService = ['org.springframework.boot:spring-boot-starter-web']
#}
#
# All 30 jar files pulled in from spring-boot-starter-web are added to this layer

ADD dependenciesLayer2/ /home/springboot/app/
# This layer contains dependent jar files of the app that aren't a
# commonService. Most of the time,
# having dependencies in this layer will take advantage of the docker build
# cache. This will give you faster build times, faster image
# uploads/downloads and reduced storage requirements.
# This layer is computed automatically from your spring boot application

ADD classesLayer3/ /home/springboot/app/
# This layer contains your application classes. It will
# likely change on each docker image build so we expect a docker cache miss.
# This layer is computed automatically from your spring boot application

ARG ORG_LABEL_SCHEMA_VCS_REF
ARG ORG_LABEL_SCHEMA_VCS_URL
ARG ORG_LABEL_SCHEMA_BUILD_DATE
ARG ORG_LABEL_SCHEMA_VERSION
ARG ORG_LABEL_SCHEMA_DESCRIPTION
ARG MAINTAINER
LABEL maintainer=${MAINTAINER:-"NA"} \
org.label-schema.vcs-ref=${ORG_LABEL_SCHEMA_VCS_REF} \
org.label-schema.vcs-url=${ORG_LABEL_SCHEMA_VCS_URL} \
org.label-schema.build-date=${ORG_LABEL_SCHEMA_BUILD_DATE} \
org.label-schema.version=${ORG_LABEL_SCHEMA_VERSION} \
org.label-schema.description=${ORG_LABEL_SCHEMA_DESCRIPTION}

VOLUME /tmp
EXPOSE 8080
ENV JAVA_OPTS="" \
SPRING_OUTPUT_ANSI_ENABLED=ALWAYS
ENTRYPOINT ["./bootrunner.sh"]
26 changes: 26 additions & 0 deletions src/main/resources/90111-jre-sid-buildlabels/bootrunner.sh
@@ -0,0 +1,26 @@
#!/bin/sh
date_echo(){
datestamp=$(date "+%F %T")
echo "${datestamp} $*"
}
#exec the JVM so that it will get a SIGTERM signal and the app can shutdown gracefully

if [ -d "${HOME}/app/WEB-INF" ]; then
#execute springboot expanded war, which may have been constructed from several image layers
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp ${HOME}/app org.springframework.boot.loader.WarLauncher $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp "${HOME}/app" org.springframework.boot.loader.WarLauncher "$@"
elif [ -d "${HOME}/app" ]; then
#execute springboot expanded jar, which may have been constructed from several image layers
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp ${HOME}/app org.springframework.boot.loader.JarLauncher $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp "${HOME}/app" org.springframework.boot.loader.JarLauncher "$@"
elif [ -f "${HOME}/app.jar" ]; then
# execute springboot jar
date_echo "exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar ${HOME}/app.jar $*"
# shellcheck disable=SC2086
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar "${HOME}/app.jar" "$@"
else
date_echo "springboot application not found in ${HOME}/app or ${HOME}/app.jar"
exit 1
fi

0 comments on commit cbb615f

Please sign in to comment.