diff --git a/docs/src/main/asciidoc/container-image.adoc b/docs/src/main/asciidoc/container-image.adoc index 66a887657d665..2f1b9a798ee38 100644 --- a/docs/src/main/asciidoc/container-image.adoc +++ b/docs/src/main/asciidoc/container-image.adoc @@ -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 diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index 2e607e01f27ef..eab870372a6df 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -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; @@ -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 boolLibPaths = Files.list(bootLibPath)) { boolLibPaths.forEach(lib -> { @@ -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) @@ -472,14 +477,14 @@ 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()) @@ -487,16 +492,15 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag 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 @@ -533,6 +537,19 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag } } + public JibContainerBuilder addLayer(JibContainerBuilder jibContainerBuilder, List 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 determineEffectiveJvmArguments(JibConfig jibConfig, Optional appCDSResult) { List effectiveJvmArguments = new ArrayList<>(jibConfig.jvmArguments); jibConfig.jvmAdditionalArguments.ifPresent(effectiveJvmArguments::addAll);