-
Notifications
You must be signed in to change notification settings - Fork 634
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Sample utilizing Spring Boot Layered JAR to make the build more frien…
…dly for Docker build cache and to avoid rebuilding all the layers of image when just application code changes.
- Loading branch information
1 parent
2534a55
commit 0951e51
Showing
12 changed files
with
595 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
## d-m-p sample utilizing docker build cache | ||
|
||
This example shows: | ||
|
||
1. How to use docker-maven-plugin together with [Spring Boot Layered JAR](https://spring.io/blog/2020/08/14/creating-efficient-docker-images-with-spring-boot-2-3) feature to utilize Docker build cache. | ||
1. How to add files into the image with predefined permissions without [multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/). | ||
|
||
It is a simple "Hello world" Spring Boot REST controller in the root context. | ||
|
||
The [Dockerfile](image/src/main/docker/Dockerfile) is located in `image/src/main/docker`. | ||
|
||
This example follows [reproducible builds](https://maven.apache.org/guides/mini/guide-reproducible-builds.html) approach | ||
to generate archives which are friendly to the Docker build cache. | ||
|
||
All content which needs to be added from the host into the image is packaged into archives of TAR format. | ||
Content is separated based on the frequency of updates, so that changes in application code - | ||
in [app/src/main/java/io/fabric8/dmp/samples/buildcache/Application.java](app/src/main/java/io/fabric8/dmp/samples/buildcache/Application.java) - | ||
don't lead to modification of all archives, but impact just image/target/application.tar. | ||
|
||
Generated archives are extracted inside image with `ADD` Dockefile directive adding the host content into the image with | ||
desired UNIX file permissions, owner, group, creation and modification timestamps. | ||
|
||
Dockerfile directives are ordered based on the frequency of updates, e.g. archive with application dependencies | ||
(image/target/dependencies.tar) is added before archive with application code (image/target/application.tar) to avoid | ||
creation of new image layer containing application dependencies (i.e. to take that layer from the Docker build cache) | ||
when only application code changes. | ||
|
||
When building the project multiple times, note that subsequent builds log "Using cache" messages, like: | ||
|
||
``` | ||
$ mvn clean package | ||
... | ||
[INFO] DOCKER> Step 1 : FROM gcr.io/distroless/java-debian10 | ||
[INFO] DOCKER> ---> f6a5dc137f9b | ||
[INFO] DOCKER> Step 2 : USER nonroot | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> 26d64b9907e3 | ||
[INFO] DOCKER> Step 3 : ENTRYPOINT | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> 6eb12980cd38 | ||
[INFO] DOCKER> Step 4 : WORKDIR "/app" | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> d6a408147d7c | ||
[INFO] DOCKER> Step 5 : CMD tini -e 130 -e 143 -- java org.springframework.boot.loader.JarLauncher | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> 461505b7ade9 | ||
[INFO] DOCKER> Step 6 : ADD dependencies.tar / | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> df87a8d003f3 | ||
[INFO] DOCKER> Step 7 : ADD spring-boot-loader.tar / | ||
[INFO] DOCKER> ---> Using cache | ||
[INFO] DOCKER> ---> 2de1bf3ac9ad | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.fabric8.dmp.samples.build-cache</groupId> | ||
<artifactId>parent</artifactId> | ||
<version>0.34-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>app</artifactId> | ||
|
||
<properties> | ||
<!-- Avoid docker build cache invalidation due to new commits --> | ||
<project.build.outputTimestamp>1970-01-01T00:00:00Z</project.build.outputTimestamp> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>repackage</id> | ||
<goals> | ||
<goal>repackage</goal> | ||
</goals> | ||
<configuration> | ||
<mainClass>io.fabric8.dmp.samples.buildcache.Application</mainClass> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
23 changes: 23 additions & 0 deletions
23
samples/build-cache/app/src/main/java/io/fabric8/dmp/samples/buildcache/Application.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.fabric8.dmp.samples.buildcache; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@SpringBootApplication | ||
public class Application { | ||
|
||
public static void main(final String[] args) { | ||
SpringApplication.run(Application.class, args); | ||
} | ||
|
||
@RestController | ||
public static class HelloController { | ||
|
||
@RequestMapping("/") | ||
public String greeting() { | ||
return "Hello World!"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.fabric8.dmp.samples.build-cache</groupId> | ||
<artifactId>parent</artifactId> | ||
<version>0.34-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>image</artifactId> | ||
<packaging>pom</packaging> | ||
|
||
<properties> | ||
<!-- Avoid docker build cache invalidation due to new commits --> | ||
<project.build.outputTimestamp>1970-01-01T00:00:00Z</project.build.outputTimestamp> | ||
|
||
<tini.version>0.19.0</tini.version> | ||
<tini.sha512>8053cc21a3a9bdd6042a495349d1856ae8d3b3e7664c9654198de0087af031f5d41139ec85a2f5d7d2febd22ec3f280767ff23b9d5f63d490584e2b7ad3c218c</tini.sha512> | ||
|
||
<app-layers.dir>${project.build.directory}/app</app-layers.dir> | ||
<app-classes.output.dir>app</app-classes.output.dir> | ||
|
||
<image>fabric8/dmp-sample-build-cache-app:${project.version}</image> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>io.fabric8.dmp.samples.build-cache</groupId> | ||
<artifactId>app</artifactId> | ||
<exclusions> | ||
<!-- Minimize class path for the java goal of Exec Maven Plugin --> | ||
<exclusion> | ||
<groupId>*</groupId> | ||
<artifactId>*</artifactId> | ||
</exclusion> | ||
</exclusions> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>exec-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<!-- Extracting layers from Spring Boot Layered JAR --> | ||
<id>extract-app-layers</id> | ||
<phase>generate-resources</phase> | ||
<goals> | ||
<goal>java</goal> | ||
</goals> | ||
<configuration> | ||
<mainClass>org.springframework.boot.loader.JarLauncher</mainClass> | ||
<arguments> | ||
<argument>extract</argument> | ||
<argument>--destination</argument> | ||
<argument>${app-layers.dir}</argument> | ||
</arguments> | ||
<systemProperties> | ||
<property> | ||
<key>jarmode</key> | ||
<value>layertools</value> | ||
</property> | ||
</systemProperties> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>com.googlecode.maven-download-plugin</groupId> | ||
<artifactId>download-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<!-- | ||
tini (https://github.com/krallin/tini) to override Java exit code when stopping container, | ||
because JVM returns non-zero exit code when stopped with SIGINT, SIGTERM or any other signal. | ||
--> | ||
<id>download-tini</id> | ||
<phase>generate-resources</phase> | ||
<goals> | ||
<goal>wget</goal> | ||
</goals> | ||
<configuration> | ||
<url>https://github.com/krallin/tini/releases/download/v${tini.version}/tini</url> | ||
<outputFileName>tini</outputFileName> | ||
<sha512>${tini.sha512}</sha512> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>build-dependencies-tar</id> | ||
<phase>process-resources</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
<configuration> | ||
<finalName>dependencies</finalName> | ||
<appendAssemblyId>false</appendAssemblyId> | ||
<attach>false</attach> | ||
<descriptors> | ||
<descriptor>src/assembly/dependencies.xml</descriptor> | ||
</descriptors> | ||
</configuration> | ||
</execution> | ||
<execution> | ||
<id>build-spring-boot-loader-tar</id> | ||
<phase>process-resources</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
<configuration> | ||
<finalName>spring-boot-loader</finalName> | ||
<appendAssemblyId>false</appendAssemblyId> | ||
<attach>false</attach> | ||
<descriptors> | ||
<descriptor>src/assembly/spring-boot-loader.xml</descriptor> | ||
</descriptors> | ||
</configuration> | ||
</execution> | ||
<execution> | ||
<id>build-snapshot-dependencies-tar</id> | ||
<phase>process-resources</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
<configuration> | ||
<finalName>snapshot-dependencies</finalName> | ||
<appendAssemblyId>false</appendAssemblyId> | ||
<attach>false</attach> | ||
<descriptors> | ||
<descriptor>src/assembly/snapshot-dependencies.xml</descriptor> | ||
</descriptors> | ||
</configuration> | ||
</execution> | ||
<execution> | ||
<id>build-application-tar</id> | ||
<phase>process-resources</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
<configuration> | ||
<finalName>application</finalName> | ||
<appendAssemblyId>false</appendAssemblyId> | ||
<attach>false</attach> | ||
<descriptors> | ||
<descriptor>src/assembly/application.xml</descriptor> | ||
</descriptors> | ||
</configuration> | ||
</execution> | ||
<execution> | ||
<!-- Prepare docker build context with Dockerfile and all archives used by it --> | ||
<id>build-docker-context</id> | ||
<phase>process-resources</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
<configuration> | ||
<finalName>context</finalName> | ||
<appendAssemblyId>false</appendAssemblyId> | ||
<attach>false</attach> | ||
<descriptors> | ||
<descriptor>src/assembly/docker-context.xml</descriptor> | ||
</descriptors> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>io.fabric8</groupId> | ||
<artifactId>docker-maven-plugin</artifactId> | ||
<configuration> | ||
<images> | ||
<image> | ||
<name>${image}</name> | ||
<build> | ||
<contextDir>${project.build.directory}/context</contextDir> | ||
<!-- | ||
Don't remove image with the same name to reuse old image layers in case of reverting of changes. | ||
Use "-D docker.cleanup=try" in the Maven command line if need to remove image with the same name. | ||
--> | ||
<cleanup>${docker.cleanup}</cleanup> | ||
<!-- | ||
No need to filter Dockerfile - filtering is performed by Maven Assembly Plugin. | ||
Refer to build-docker-context execution. | ||
--> | ||
<filter>false</filter> | ||
</build> | ||
</image> | ||
</images> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<id>build-docker-image</id> | ||
<goals> | ||
<goal>build</goal> | ||
</goals> | ||
<phase>package</phase> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> | ||
<id>application</id> | ||
<formats> | ||
<format>tar</format> | ||
</formats> | ||
<includeBaseDirectory>false</includeBaseDirectory> | ||
<fileSets> | ||
<fileSet> | ||
<outputDirectory>${app-classes.output.dir}</outputDirectory> | ||
<directory>${app-layers.dir}/application</directory> | ||
<!-- Prevent modification of Java *.class files and other content which is considered a part of the application --> | ||
<fileMode>0444</fileMode> | ||
<!-- This directory defines Java class path, so don't allow creation of new files for security --> | ||
<directoryMode>0555</directoryMode> | ||
</fileSet> | ||
</fileSets> | ||
</assembly> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> | ||
<id>dependencies</id> | ||
<formats> | ||
<format>tar</format> | ||
</formats> | ||
<includeBaseDirectory>false</includeBaseDirectory> | ||
<fileSets> | ||
<fileSet> | ||
<outputDirectory>usr/bin</outputDirectory> | ||
<directory>${project.build.directory}</directory> | ||
<includes> | ||
<include>tini</include> | ||
</includes> | ||
<fileMode>0555</fileMode> | ||
</fileSet> | ||
<fileSet> | ||
<outputDirectory>${app-classes.output.dir}</outputDirectory> | ||
<directory>${app-layers.dir}/dependencies</directory> | ||
<fileMode>0444</fileMode> | ||
<directoryMode>0555</directoryMode> | ||
</fileSet> | ||
</fileSets> | ||
</assembly> |
Oops, something went wrong.