Description of the issue: Impossible to run an executable WAR
Expected behavior: Override the default WAR containerization behavior to permit to run executable WAR
Steps to reproduce:
/jetty/webapps/ROOT has the WAR content Environment:
jib-maven-plugin Configuration:
...
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib-maven-plugin.version}</version>
<configuration>
<from>
<image>openjdk:8-jre-alpine</image>
</from>
<to>
<image>jhipster:latest</image>
</to>
<container>
<entrypoint>
<script>/entrypoint.sh</script>
</entrypoint>
<ports>
<port>8080</port>
</ports>
<environment>
<SPRING_OUTPUT_ANSI_ENABLED>ALWAYS</SPRING_OUTPUT_ANSI_ENABLED>
<JHIPSTER_SLEEP>0</JHIPSTER_SLEEP>
</environment>
<useCurrentTimestamp>true</useCurrentTimestamp>
</container>
<extraDirectories>
<permissions>
<permission>
<file>/entrypoint.sh</file>
<mode>700</mode>
</permission>
</permissions>
</extraDirectories>
</configuration>
</plugin>
...
jib-gradle-plugin Configuration:
jib {
from {
image = 'openjdk:8-jre-alpine'
}
to {
image = 'jhipster:latest'
}
container {
entrypoint = ['/entrypoint.sh']
ports = ['8080']
environment = [
SPRING_OUTPUT_ANSI_ENABLED: 'ALWAYS',
JHIPSTER_SLEEP: '0'
]
useCurrentTimestamp = true
}
extraDirectories {
permissions = ['/entrypoint.sh': '700']
}
}
Log output:
Additional Information:
The presented issue (executable WAR) seems orthogonal to the 0.10.0 WAR behavior but is a neat feature provided by Spring Boot.
Hi @danielpetisme,
Our initial intention with Jib was not to support containerizing an executable JAR (or WAR). See https://github.com/GoogleContainerTools/jib/issues/1280#issuecomment-442255523 for a bit more details.
In your case, if you are fine with deploying an actual WAR onto some Servlet engine instead of containerizing an executable JAR, you may be able to config Jib to place the WAR in the right place using your choice of base Servlet engine image: https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#war-projects.
I also wonder if your project had been working prior to 0.10.0 which introduced the WAR containerizing feature. In that case, I guess it may continue to work if you change your packaging to JAR (i.e., removing <packaging>war</packaging> which will default to jar packaging type on Maven / not applying the WAR plugin on Gradle), which will make Jib not identify your project as a WAR project.
But I guess the jhipster generator doesn't want to remove the WAR packaging or the Gradle WAR plugin, right?
@chanseokoh I like your comment in an other thread:
It's actually our intention that Jib adds individual class files and dependency JARs into the container instead of putting a runnable JAR into the container.
This is a better way to explain than what is in the README.
To answer your question, yes it worked very well in the previous jib release which didn't detect and act on war packaging.
So from what I understand, we can close this issue by saying executable jar/war are not recommended.
I guess it would be an interesting point to explicit in the FAQ.
+1 to adding an FAQ entry, since the runnable JAR question does come up from time to time. We can leave this issue open for now and close it when that's added.
saying executable jar/war are not recommended
Hmm... to be clear, it's not that we are messaging that your project setup shouldn't produce an executable JAR (or WAR). It is basically that, when it comes to containerizing an app, we will not simply take a JAR as-is and put that into the container to do java -jar my-app.jar. Instead, we are opinionated to skip the somewhat unnecessary extra JAR-packaging step, which gives the benefit of a more optimal internal image structure.
However, some people have requested support for putting a JAR directly into the container, whether executable or not, or whether fat or not. Actually, we've been discussing if we should actually open the door of taking a packaged JAR.
That said, we do want Jib to be capable of containerizing most of the projects, which include projects set up to produce an executable JAR. Actually, whether a project produces a JAR or an executable JAR should not matter, because Jib just does not look into such JARs.
For example, Spring Boot creates an executable (and fat at the same time) JAR, and we definitely want to provide good support for Spring Boot projects. Fortunately, Jib actually works, more or less, with Spring Boot. However, it's not that Jib is doing any special handling to make Spring Boot work. Jib just does the normal containerizing by placing compiled .class files and dependency JARs into the container. So, actually, in some cases, the user may need to adjust their project setup to make Spring Boot work with Jib, which may not be desirable as a normal Spring Boot project setup.
We are also pretty interested in how JHipster is presenting Jib through the generator. I'd like to get the detailed use case and make Jib work well in this use case. It sounds like it used to work well before Jib 0.10.0, which started to identify the generated app as a WAR project. If this is not desirable, theoretically from our perspective the project should not be configured to package a WAR, but I can understand doing so may not be ideal for an app generator that uses some frameworks. So, I think the issue here is perhaps that you want Jib to not recognize your project as a WAR project, which is independent of whether the project produces an executable JAR. As I said, Jib should basically work for most projects; Jib is oblivious to JARs anyway.
Regarding executable jar 100% agree. Executable-jar packaging is not optimal in a container world, however if I want to build an image with an exec-jar jib I should be able to.
Regarding the executable war is more tricky. Indeed JHipster provide a default war (not jar) packaging with an executable and to be embedded flavors.
With jib 0.9.0, the packaging was not detected and jib only copied the jar files (ie. classes, lib, resources without all the WEB-INF structure) to /app.
The main issue with using the default jib/war behavior is that it forces the JHipster team to choose/explicit a default servlet engine for containers (jetty or another). So far the generated application was web container agnostic.
I asked the question to the JHipster team, don't have a clear answer yet.
If we look for technical solutions, without changing the JHipster packaging, I don't see how I could override jib behavior. Even if the jibDockerBuild/jib:dockerBuild action would be dependent of the bootJar step it wouldn't workaround the jib packaging detection. Right?
PS: thanks for the FAQ update 馃槈
- Generate a jhipster application: https://www.jhipster.tech/creating-an-app/
- Build the container: https://www.jhipster.tech/docker-compose/#3
@GoogleContainerTools/java-tools I haven't looked into how JHipster generates an app, but this makes me wonder if we should introduce a way to allow forcing non-WAR (or WAR) containerization?
I've played around with some demo app generated by JHipster. I tried to generate as much a simple app as possible, but still not a small app in that it uses MySQL, in-memory H2 persistence, Ehcache Spring cache abstraction, Angular, etc., which actually requires using docker-compose to make the Jib-built image to talk to the MySQL Docker container. The point is, it is not a toy hello world app.
The Jib configuration for my demo app looked something like this:
<configuration>
<from><image>openjdk:8-jre-alpine</image></from>
<to><image>demoapp:latest</image></to>
<container>
<entrypoint>
<shell>sh</shell>
<option>-c</option>
<arg>chmod +x /entrypoint.sh && sync && /entrypoint.sh</arg>
</entrypoint>
<ports><port>8080</port></ports>
<environment>
<SPRING_OUTPUT_ANSI_ENABLED>ALWAYS</SPRING_OUTPUT_ANSI_ENABLED>
<JHIPSTER_SLEEP>0</JHIPSTER_SLEEP>
</environment>
<useCurrentTimestamp>true</useCurrentTimestamp>
</container>
</configuration>
src/main/jib/entrypoint.sh is something like this:
#!/bin/sh
echo "The application will start in ${JHIPSTER_SLEEP}s..." \
&& sleep ${JHIPSTER_SLEEP}
exec java ${JAVA_OPTS} \
-Djava.security.egd=file:/dev/./urandom \
-cp /app/resources/:/app/classes/:/app/libs/* \
"com.example.demoapp.DemoappApp" \
"$@"
(Side note: one example of someone directly copy-and-pasting the current classpath.)
It is certain that the app needs to be containerized in the usual, non-WAR way. In that sense, one could argue that the project should not have <packaging>war</packaging>. However, it also configures the maven-war-plugin, and when the packaging is war, the end effect is that ./mvnw package will be able to create a WAR under the build directory, which will probably be JHipster's intention. One could argue that you can do ./mvnw war:war still after changing the packaging to jar, but looks like Spring Boot is also checking the packaging type, so it does not produce an executable WAR. Even if there is a way to configure Spring Boot to produce an executable WAR when the packaging is not war (I am a bit skeptical actually) and run some command separately for that purpose, it does not sound fair to have JHipster to switch the packaging just because of Jib. In the current settings, JHipster will not be able to upgrade Jib.
Considering these, and to have good support for JHipster, I think it's worth adding some means to force WAR or non-WAR containerization, which can also cover #1302.
Good News!
BTW, I did the jib integration for JHipster so if you found some stuff confusing or not intuitive, let me know. I'm definitively interested by feedback
@chanseokoh Thank you for investigating this. Indeed JHipster generates well beyond a hello-world app 馃槃! I also agree with your conclusion that jib should allow configuring war/non-war containerization and not just detect the packaging type.
Nethertheless I will bring up the discussion to the JHipster mailing list to push them to use jar as the default packaging (after all it is spring boot default) and recommend mvn war:war to create a war.
@danielpetisme you may already be aware, but starting with 0.10.0, instead of doing chmod +x manually, you could probably able to do:
<container>
<entrypoint>/entrypoint.sh</entrypoint>
</container>
<extraDirectories>
<permissions>
<permission>
<file>/entrypoint.sh</file>
<mode>755</mode>
<permission>
</permissions>
</extraDirectories>
And I mentioned copy-and-pasting the classpath, because we have thought about the possibility of potentially changing the classpath in the future and are a bit concerned that this may cause breaking changes for people who rely on the fixed nature of the classpath. Not that I have a good, future-proof solution for the JHipster case.
@patflynn there have been a lot of confusion in the meeting, and this comment may provide some context:
It is certain that the app needs to be containerized in the usual, non-WAR way. In that sense, one could argue that the project should not have
<packaging>war</packaging>. However, it also configures themaven-war-plugin, and when the packaging iswar, the end effect is that./mvnw packagewill be able to create a WAR under the build directory, which will probably be JHipster's intention. One could argue that you can do./mvnw war:warstill after changing the packaging tojar, but looks like Spring Boot is also checking the packaging type, so it does not produce an executable WAR. Even if there is a way to configure Spring Boot to produce an executable WAR when the packaging is notwar(I am a bit skeptical actually) and run some command separately for that purpose, it does not sound fair to have JHipster to switch the packaging just because of Jib. In the current settings, JHipster will not be able to upgrade Jib.
https://github.com/jhipster/generator-jhipster/pull/9034 is the PR working on migrating from WAR to JAR for both Maven and Gradle. It seems pretty involved, but eventually, I think it will happen. Closing this issue.
If your project uses the war packaging (spring-boot), then you can run it without any changes like this:
xml
<configuration>
<from>
<image>adoptopenjdk/openjdk11:alpine-jre</image>
</from>
<to>
<image>...</image>
</to>
<container>
<appRoot>/app</appRoot>
<entrypoint>
<entrypoint>java</entrypoint>
<!-- Start of JVM flags --->
<entrypoint>-Dlogging.config=/config/log4j2.xml</entrypoint>
<entrypoint>-Dspring.config.additional-location=/config/</entrypoint>
<!-- End of JVM flags --->
<entrypoint>-cp</entrypoint>
<entrypoint>/app:/app/WEB-INF/classes:/app/WEB-INF/lib/*</entrypoint>
<!-- use your actual main class, not the spring.boot.WarLauncher -->
<entrypoint>${mainClass}</entrypoint>
</entrypoint>
[...]
</container>
</configuration>
There is no need to use the wrapping tomcat or jetty images for that.
Most helpful comment
I've played around with some demo app generated by JHipster. I tried to generate as much a simple app as possible, but still not a small app in that it uses MySQL, in-memory H2 persistence, Ehcache Spring cache abstraction, Angular, etc., which actually requires using
docker-composeto make the Jib-built image to talk to the MySQL Docker container. The point is, it is not a toy hello world app.The Jib configuration for my demo app looked something like this:
src/main/jib/entrypoint.shis something like this:(Side note: one example of someone directly copy-and-pasting the current classpath.)
It is certain that the app needs to be containerized in the usual, non-WAR way. In that sense, one could argue that the project should not have
<packaging>war</packaging>. However, it also configures themaven-war-plugin, and when the packaging iswar, the end effect is that./mvnw packagewill be able to create a WAR under the build directory, which will probably be JHipster's intention. One could argue that you can do./mvnw war:warstill after changing the packaging tojar, but looks like Spring Boot is also checking the packaging type, so it does not produce an executable WAR. Even if there is a way to configure Spring Boot to produce an executable WAR when the packaging is notwar(I am a bit skeptical actually) and run some command separately for that purpose, it does not sound fair to have JHipster to switch the packaging just because of Jib. In the current settings, JHipster will not be able to upgrade Jib.Considering these, and to have good support for JHipster, I think it's worth adding some means to force WAR or non-WAR containerization, which can also cover #1302.