Skip to content

Commit

Permalink
Add local project dependencies to a different layer for jib
Browse files Browse the repository at this point in the history
By leveraging the fact that Quarkus can do workspace discovery
during the build, we are able to place dependencies in the
workspace in a separate layer above the normal dependencies.
The expectation is that the workspace dependencies will change
faster than the normal dependencies.

Closes: #9818
  • Loading branch information
geoand committed Jun 3, 2022
1 parent d65ab1d commit c4c9a9a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 15 deletions.
11 changes: 11 additions & 0 deletions docs/src/main/asciidoc/container-image.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ Other base images might provide launch scripts that enable debugging when an env

Finally, the `quarkus.jib.jvm-entrypoint` configuration property can be used to completely override the container entry point and can thus be used to either hard code the JVM debug configuration or point to a script that handles the details.

==== Multi-module projects and layering

When building a multi-module project containing a Quarkus application as one module and various supporting project dependencies as other modules,
Quarkus supports placing these supporting modules in a separate container image layer from the rest of the application dependencies, with the expectation
that these supporting modules will change more frequently than the regular application dependencies - thus making a rebuild faster if the
application dependencies have not changed.

To enable this feature, the property `quarkus.bootstrap.workspace-discovery` needs to be set to `true` either as a system property
when invoking the build tool, either as a build tool property. Setting this property in `application.properties` will **not** work because
this property needs to be known very early on in the build process.

[#docker]
=== Docker

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag
if (artifact == null) {
continue;
}
if (artifact.isWorkspaceModule()) {
fastChangingLibs.add(artifact);
continue;
}
String artifactVersion = artifact.getVersion();
if ((artifactVersion == null) || artifactVersion.isEmpty()) {
continue;
Expand Down Expand Up @@ -430,14 +434,14 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag
jibConfig.baseRegistryPassword));
if (fastChangingLibPaths.isEmpty()) {
// just create a layer with the entire lib structure intact
jibContainerBuilder.addLayer(Collections.singletonList(componentsPath.resolve(JarResultBuildStep.LIB)),
workDirInContainer);
addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.LIB)),
workDirInContainer, "fast-jar-lib");
} else {
// we need to manually create each layer
// the idea here is that the fast changing libraries are created in a later layer, thus when they do change,
// docker doesn't have to create an entire layer with all dependencies - only change the fast ones

FileEntriesLayer.Builder bootLibsLayerBuilder = FileEntriesLayer.builder();
FileEntriesLayer.Builder bootLibsLayerBuilder = FileEntriesLayer.builder().setName("fast-jar-boot-libs");
Path bootLibPath = componentsPath.resolve(JarResultBuildStep.LIB).resolve(JarResultBuildStep.BOOT_LIB);
try (Stream<Path> boolLibPaths = Files.list(bootLibPath)) {
boolLibPaths.forEach(lib -> {
Expand All @@ -462,7 +466,8 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag

Path deploymentPath = componentsPath.resolve(JarResultBuildStep.LIB).resolve(JarResultBuildStep.DEPLOYMENT_LIB);
if (Files.exists(deploymentPath)) { // this is the case of mutable-jar
FileEntriesLayer.Builder deploymentLayerBuilder = FileEntriesLayer.builder();
FileEntriesLayer.Builder deploymentLayerBuilder = FileEntriesLayer.builder()
.setName("fast-jar-deployment-libs");
Files.list(deploymentPath).forEach(lib -> {
AbsoluteUnixPath libPathInContainer = workDirInContainer.resolve(JarResultBuildStep.LIB)
.resolve(JarResultBuildStep.DEPLOYMENT_LIB)
Expand All @@ -472,31 +477,30 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag
jibContainerBuilder.addFileEntriesLayer(deploymentLayerBuilder.build());
}

jibContainerBuilder.addLayer(nonFastChangingLibPaths,
workDirInContainer.resolve(JarResultBuildStep.LIB).resolve(JarResultBuildStep.MAIN));
jibContainerBuilder.addLayer(new ArrayList<>(fastChangingLibPaths),
workDirInContainer.resolve(JarResultBuildStep.LIB).resolve(JarResultBuildStep.MAIN));
AbsoluteUnixPath libsMainPath = workDirInContainer.resolve(JarResultBuildStep.LIB)
.resolve(JarResultBuildStep.MAIN);
addLayer(jibContainerBuilder, nonFastChangingLibPaths, libsMainPath, "fast-jar-normal-libs");
addLayer(jibContainerBuilder, new ArrayList<>(fastChangingLibPaths), libsMainPath, "fast-jar-changing-libs");
}

if (appCDSResult.isPresent()) {
jibContainerBuilder.addFileEntriesLayer(FileEntriesLayer.builder().addEntry(
jibContainerBuilder.addFileEntriesLayer(FileEntriesLayer.builder().setName("app-cds").addEntry(
componentsPath.resolve(JarResultBuildStep.QUARKUS_RUN_JAR),
workDirInContainer.resolve(JarResultBuildStep.QUARKUS_RUN_JAR),
Files.getLastModifiedTime(componentsPath.resolve(JarResultBuildStep.QUARKUS_RUN_JAR)).toInstant())
.build());
jibContainerBuilder
.addLayer(Collections.singletonList(appCDSResult.get().getAppCDS()), workDirInContainer);
} else {
jibContainerBuilder.addFileEntriesLayer(FileEntriesLayer.builder().addEntry(
jibContainerBuilder.addFileEntriesLayer(FileEntriesLayer.builder().setName("fast-jar-run").addEntry(
componentsPath.resolve(JarResultBuildStep.QUARKUS_RUN_JAR),
workDirInContainer.resolve(JarResultBuildStep.QUARKUS_RUN_JAR)).build());
}

jibContainerBuilder
.addLayer(Collections.singletonList(componentsPath.resolve(JarResultBuildStep.APP)),
workDirInContainer)
.addLayer(Collections.singletonList(componentsPath.resolve(JarResultBuildStep.QUARKUS)),
workDirInContainer);
addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.APP)),
workDirInContainer, "fast-jar-quarkus-app");
addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.QUARKUS)),
workDirInContainer, "fast-jar-quarkus");
if (JibConfig.DEFAULT_WORKING_DIR.equals(jibConfig.workingDirectory)) {
// this layer ensures that the working directory is writeable
// see https://github.com/GoogleContainerTools/jib/issues/1270
Expand Down Expand Up @@ -533,6 +537,19 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag
}
}

public JibContainerBuilder addLayer(JibContainerBuilder jibContainerBuilder, List<Path> files,
AbsoluteUnixPath pathInContainer, String name)
throws IOException {
FileEntriesLayer.Builder layerConfigurationBuilder = FileEntriesLayer.builder().setName(name);

for (Path file : files) {
layerConfigurationBuilder.addEntryRecursive(
file, pathInContainer.resolve(file.getFileName()));
}

return jibContainerBuilder.addFileEntriesLayer(layerConfigurationBuilder.build());
}

private List<String> determineEffectiveJvmArguments(JibConfig jibConfig, Optional<AppCDSResultBuildItem> appCDSResult) {
List<String> effectiveJvmArguments = new ArrayList<>(jibConfig.jvmArguments);
jibConfig.jvmAdditionalArguments.ifPresent(effectiveJvmArguments::addAll);
Expand Down

0 comments on commit c4c9a9a

Please sign in to comment.