Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support building an image when using war packaging with Gradle #23825

Closed
wilkinsona opened this issue Oct 23, 2020 · 7 comments
Closed

Support building an image when using war packaging with Gradle #23825

wilkinsona opened this issue Oct 23, 2020 · 7 comments
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@wilkinsona
Copy link
Member

It's possible at the moment using a little bit of manual configuration:

bootBuildImage {
	jar = bootWar.archiveFile
}

The jar property is unfortunately named in this case. archive or archiveFile may be better as it would cover both jars and wars. We could also react to the war plugin being applied and automatically make the configuration change above.

@wilkinsona wilkinsona added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Oct 23, 2020
@wilkinsona wilkinsona changed the title Support building an image when using war packaging with Maven Support building an image when using war packaging with Gradle Oct 23, 2020
@wilkinsona
Copy link
Member Author

Here's the output from bootBuildImage when configured to use a war file as its "jar":

> Task :bootBuildImage
Building image 'docker.io/library/build-image-from-war:0.0.1-SNAPSHOT'

 > Pulling builder image 'docker.io/paketobuildpacks/builder:base' ..................................................
 > Pulled builder image 'paketobuildpacks/builder@sha256:bae0a6b7ec5d51867eb49ac598775bb5fa8788bba458d2edfda314611be554a8'
 > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' ..................................................
 > Pulled run image 'paketobuildpacks/run@sha256:155ee4b4389fa88b8b9bd5444601417218b3da44e491b1aad8c8b6c2365e7909'
 > Executing lifecycle version v0.9.3
 > Using build cache volume 'pack-cache-9462ad59044b.build'

 > Running creator
    [creator]     ===> DETECTING
    [creator]     6 of 18 buildpacks participating
    [creator]     paketo-buildpacks/ca-certificates   1.0.1
    [creator]     paketo-buildpacks/bellsoft-liberica 5.2.0
    [creator]     paketo-buildpacks/executable-jar    3.1.3
    [creator]     paketo-buildpacks/apache-tomcat     2.4.1
    [creator]     paketo-buildpacks/dist-zip          2.2.2
    [creator]     paketo-buildpacks/spring-boot       3.4.0
    [creator]     ===> ANALYZING
    [creator]     Restoring metadata for "paketo-buildpacks/bellsoft-liberica:helper" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/bellsoft-liberica:java-security-properties" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jre" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jvmkill" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/executable-jar:class-path" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/apache-tomcat:catalina-base" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/apache-tomcat:helper" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/apache-tomcat:tomcat" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/spring-boot:helper" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/spring-boot:spring-cloud-bindings" from app image
    [creator]     Restoring metadata for "paketo-buildpacks/spring-boot:web-application-type" from app image
    [creator]     ===> RESTORING
    [creator]     ===> BUILDING
    [creator]     
    [creator]     Paketo CA Certificates Buildpack 1.0.1
    [creator]       https://github.com/paketo-buildpacks/ca-certificates
    [creator]       Launch Helper: Contributing to layer
    [creator]         Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
    [creator]         Writing profile.d/helper
    [creator]     
    [creator]     Paketo BellSoft Liberica Buildpack 5.2.0
    [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
    [creator]       Build Configuration:
    [creator]         $BP_JVM_VERSION              11.*            the Java version
    [creator]       Launch Configuration:
    [creator]         $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    [creator]         $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    [creator]         $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    [creator]         $JAVA_TOOL_OPTIONS                           the JVM launch flags
    [creator]       BellSoft Liberica JRE 11.0.9: Contributing to layer
    [creator]         Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.9.1+1/bellsoft-jre11.0.9.1+1-linux-amd64.tar.gz
    [creator]         Verifying checksum
    [creator]         Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
    [creator]         Adding 138 container CA certificates to JVM truststore
    [creator]         Writing env.launch/BPI_APPLICATION_PATH.default
    [creator]         Writing env.launch/BPI_JVM_CACERTS.default
    [creator]         Writing env.launch/BPI_JVM_CLASS_COUNT.default
    [creator]         Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    [creator]         Writing env.launch/JAVA_HOME.default
    [creator]         Writing env.launch/MALLOC_ARENA_MAX.default
    [creator]       Launch Helper: Contributing to layer
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
    [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
    [creator]         Writing profile.d/helper
    [creator]       JVMKill Agent 1.16.0: Reusing cached layer
    [creator]       Java Security Properties: Contributing to layer
    [creator]         Writing env.launch/JAVA_SECURITY_PROPERTIES.default
    [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.append
    [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.delim
    [creator]     
    [creator]     Paketo Executable JAR Buildpack 3.1.3
    [creator]       https://github.com/paketo-buildpacks/executable-jar
    [creator]       Process types:
    [creator]         executable-jar: java org.springframework.boot.loader.WarLauncher
    [creator]         task:           java org.springframework.boot.loader.WarLauncher
    [creator]         web:            java org.springframework.boot.loader.WarLauncher
    [creator]     
    [creator]     Paketo Apache Tomcat Buildpack 2.4.1
    [creator]       https://github.com/paketo-buildpacks/apache-tomcat
    [creator]       Build Configuration:
    [creator]         $BP_TOMCAT_CONTEXT_PATH                  the application context path
    [creator]         $BP_TOMCAT_EXT_CONF_SHA256               the SHA256 hash of the external Tomcat configuration archive
    [creator]         $BP_TOMCAT_EXT_CONF_STRIP           0    the number of directory components to strip from the external Tomcat configuration archive
    [creator]         $BP_TOMCAT_EXT_CONF_URI                  the download location of the external Tomcat configuration
    [creator]         $BP_TOMCAT_EXT_CONF_VERSION              the version of the external Tomcat configuration
    [creator]         $BP_TOMCAT_VERSION                  9.*  the Tomcat version
    [creator]       Launch Configuration:
    [creator]         $BPL_TOMCAT_ACCESS_LOGGING_ENABLED       the Tomcat access logging state
    [creator]       Apache Tomcat 9.0.39: Contributing to layer
    [creator]         Downloading from https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.39/bin/apache-tomcat-9.0.39.tar.gz
    [creator]         Verifying checksum
    [creator]         Expanding to /layers/paketo-buildpacks_apache-tomcat/tomcat
    [creator]         Writing env.launch/CATALINA_HOME.default
    [creator]       Launch Helper: Contributing to layer
    [creator]         Creating /layers/paketo-buildpacks_apache-tomcat/helper/exec.d/access-logging-support
    [creator]         Writing profile.d/helper
    [creator]       Apache Tomcat Support: Reusing cached layer
    [creator]       Process types:
    [creator]         task:   catalina.sh run
    [creator]         tomcat: catalina.sh run
    [creator]         web:    catalina.sh run
    [creator]     
    [creator]     Paketo Spring Boot Buildpack 3.4.0
    [creator]       https://github.com/paketo-buildpacks/spring-boot
    [creator]       Launch Helper: Contributing to layer
    [creator]         Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
    [creator]         Writing profile.d/helper
    [creator]       Web Application Type: Reusing cached layer
    [creator]       Spring Cloud Bindings 1.6.0: Reusing cached layer
    [creator]       Image labels:
    [creator]         org.springframework.boot.spring-configuration-metadata.json
    [creator]         org.springframework.boot.version
    [creator]     ===> EXPORTING
    [creator]     Adding layer 'paketo-buildpacks/ca-certificates:helper'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
    [creator]     Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
    [creator]     Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
    [creator]     Reusing layer 'paketo-buildpacks/executable-jar:class-path'
    [creator]     Reusing layer 'paketo-buildpacks/apache-tomcat:catalina-base'
    [creator]     Adding layer 'paketo-buildpacks/apache-tomcat:helper'
    [creator]     Adding layer 'paketo-buildpacks/apache-tomcat:tomcat'
    [creator]     Adding layer 'paketo-buildpacks/spring-boot:helper'
    [creator]     Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
    [creator]     Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
    [creator]     Reusing 1/1 app layer(s)
    [creator]     Adding layer 'launcher'
    [creator]     Adding layer 'config'
    [creator]     Adding label 'io.buildpacks.lifecycle.metadata'
    [creator]     Adding label 'io.buildpacks.build.metadata'
    [creator]     Adding label 'io.buildpacks.project.metadata'
    [creator]     Adding label 'org.springframework.boot.spring-configuration-metadata.json'
    [creator]     Adding label 'org.springframework.boot.version'
    [creator]     *** Images (55754fd6d792):
    [creator]           docker.io/library/build-image-from-war:0.0.1-SNAPSHOT

Successfully built image 'docker.io/library/build-image-from-war:0.0.1-SNAPSHOT'

Things seem to get a bit confused as the executable jar, Tomcat, and Spring Boot buildpacks all get involved. It's not clear to me from the output which will win and, therefore, how the application will be launched.

We can run the image and see what happens. Here's the output from doing so:

Setting Active Processor Count to 8
WARNING: Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx659378K -XX:MaxMetaspaceSize=81997K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 50, Loaded Class Count: 12063, Headroom: 0%)
Adding 138 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Using CATALINA_BASE:   /layers/paketo-buildpacks_apache-tomcat/catalina-base
Using CATALINA_HOME:   /layers/paketo-buildpacks_apache-tomcat/tomcat
Using CATALINA_TMPDIR: /layers/paketo-buildpacks_apache-tomcat/catalina-base/temp
Using JRE_HOME:        /layers/paketo-buildpacks_bellsoft-liberica/jre
Using CLASSPATH:       /layers/paketo-buildpacks_apache-tomcat/catalina-base/bin/tomcat-logging-support-3.3.0.RELEASE.jar:/layers/paketo-buildpacks_apache-tomcat/tomcat/bin/bootstrap.jar:/layers/paketo-buildpacks_apache-tomcat/tomcat/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=8 -XX:MaxDirectMemorySize=10M -Xmx659378K -XX:MaxMetaspaceSize=81997K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol         INFO    Initializing ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.catalina.startup.Catalina               INFO    Server initialization in [404] milliseconds
[CONTAINER] org.apache.catalina.core.StandardService           INFO    Starting service [Catalina]
[CONTAINER] org.apache.catalina.core.StandardEngine            INFO    Starting Servlet engine: [Apache Tomcat/9.0.39]
[CONTAINER] org.apache.catalina.startup.HostConfig             INFO    Deploying web application directory [/layers/paketo-buildpacks_apache-tomcat/catalina-base/webapps/ROOT]
[CONTAINER] org.apache.jasper.servlet.TldScanner               INFO    At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[CONTAINER] lina.core.ContainerBase.[Catalina].[localhost].[/] INFO    2 Spring WebApplicationInitializers detected on classpath

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.4.0-M4)

2020-11-10 10:41:26.193  INFO 1 --- [           main] c.e.b.ServletInitializer                 : Starting ServletInitializer using Java 11.0.9.1 on 9f715d4be99a with PID 1 (/workspace/WEB-INF/classes started by cnb in /workspace)
2020-11-10 10:41:26.196  INFO 1 --- [           main] c.e.b.ServletInitializer                 : No active profile set, falling back to default profiles: default
[CONTAINER] lina.core.ContainerBase.[Catalina].[localhost].[/] INFO    Initializing Spring embedded WebApplicationContext
2020-11-10 10:41:26.660  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 422 ms
2020-11-10 10:41:26.954  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-10 10:41:27.078  INFO 1 --- [           main] c.e.b.ServletInitializer                 : Started ServletInitializer in 1.239 seconds (JVM running for 2.699)
[CONTAINER] org.apache.catalina.startup.HostConfig             INFO    Deployment of web application directory [/layers/paketo-buildpacks_apache-tomcat/catalina-base/webapps/ROOT] has finished in [2,148] ms
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol         INFO    Starting ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.catalina.startup.Catalina               INFO    Server startup in [2183] milliseconds

It's a traditional war deployment with our war file being deployed to Tomcat. If a Spring Boot user is building a war file purely to use JSPs (the only reason to use war packaging), a traditional war deployment may not be what they want. In many cases they'll want their executable war to be launched as an executable jar would have been. Among other things, launching an executable war will ensure that the app's choice of embedded container (it may not be Tomcat) and the configuration of it is honoured.

@nebhale What's the current state of the art with Paketo and building an executable war-based image? Is it possible today with some config options, or would some builder changes be required for it to support either executable war or traditional war deployments?

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Nov 10, 2020
@nebhale
Copy link
Member

nebhale commented Nov 10, 2020

@wilkinsona The executable WAR thing is really problematic for us because it’s not immediately obvious what the user was intending. In general we have to do some serious surgery (note the lack of the plugin) on an application to get a coherent sample. That sample is the outcome of many people asking why, when they changed a Boot application to <packaging>war</packaging>, it didn’t run in a contributed Tomcat.

The good news is that we’re in a better situation with CNBs than we were with CF buildpacks, because applications don’t need to have be unambiguously one type or another. They can define the same process types in a last-wins priority, they can define multiple process types allowing the user to decide which command to run, etc.

I see a couple of options and welcome your opinion on what you’d like to see:

  1. Change the priority so that the application is run as an executable JAR if there is a Main-Class regardless of packaging. Tomcat would be contributed, even though it’d likely rarely be used.
  2. Change the detection logic so that apache-tomcat would not participate if there was a Main-Class even if it was a WAR. Tomcat would not be contributed, even though it might be desired for some applications.
  3. Something we haven’t thought of yet...

/cc @ekcasey

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 10, 2020
@wilkinsona
Copy link
Member Author

Thanks, Ben.

In an ideal world, I think we'd make two things possible:

  1. Build an image without a Servlet container that runs the war as an executable jar
  2. Build an image with a Servlet container that deploys the war to the container

Generally speaking, I think these two are compatible with you second option. In fact, from a general CNB perspective, I think they're completely compatible. From a Spring Boot perspective things may be a bit trickier, particularly with Maven where I think you may have to jump through some hoops to build a war file without a Main-Class while still being able to make use of the build-image goal. It's more straightforward with Gradle where you'd either use the output of the war task or the bootWar task depending on the sort of image that you wanted.

nebhale added a commit to paketo-buildpacks/apache-tomcat that referenced this issue Nov 12, 2020
Previously, whenever a WAR, built by Spring Boot was presented to this
buildpack confusing things would happen.  The fact that the WAR file had both
a WEB-INF/ directory (as all WAR files must) and a Main-Class declaration
meant that how the WAR file would be run (via an external or embedded Tomcat)
was confusing.

This change updates the detection logic so that Tomcat is only contributed if
both the file is a WAR file and there is no Main-Class declared.

[spring-projects/spring-boot#23825]

Signed-off-by: Ben Hale <bhale@vmware.com>
nebhale added a commit to paketo-buildpacks/apache-tomcat that referenced this issue Nov 13, 2020
Previously, whenever a WAR, built by Spring Boot was presented to this
buildpack confusing things would happen.  The fact that the WAR file had both
a WEB-INF/ directory (as all WAR files must) and a Main-Class declaration
meant that how the WAR file would be run (via an external or embedded Tomcat)
was confusing.

This change updates the detection logic so that Tomcat is only contributed if
both the file is a WAR file and there is no Main-Class declared.

[spring-projects/spring-boot#23825]

Signed-off-by: Ben Hale <bhale@vmware.com>
@nebhale
Copy link
Member

nebhale commented Nov 13, 2020

@wilkinsona this has been released and is propagating out to the various places that buildpacks live.

@scottfrederick scottfrederick added type: bug A general bug and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Dec 14, 2020
@scottfrederick scottfrederick added this to the 2.3.x milestone Dec 14, 2020
@scottfrederick
Copy link
Contributor

Without the bootBuildImage.jar configuration suggested in this issue, running the bootBuildImage task will always result in a jar file being built and used in the image creation, even though the packaging type is set to war. Re-classifying this as a bug since that behavior isn't what the user would expect.

@scottfrederick scottfrederick self-assigned this Dec 14, 2020
@scottfrederick
Copy link
Contributor

After further discussion, #24521 has been created to replace the unexpected behavior with an error message for current releases. Proper support for image building with war packaging will be a future enhancement.

@scottfrederick scottfrederick added type: enhancement A general enhancement and removed type: bug A general bug labels Dec 15, 2020
@scottfrederick scottfrederick modified the milestones: 2.3.x, 2.5.x Dec 15, 2020
@scottfrederick scottfrederick removed their assignment Jan 4, 2021
@philwebb
Copy link
Member

Possibly related to #22195

@scottfrederick scottfrederick self-assigned this Feb 23, 2021
scottfrederick added a commit that referenced this issue Feb 24, 2021
This commit updates the Gradle image building task to support building
images from executable and non-executable war files.

Fixes gh-23825
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants