Azure / draft-classic

A tool for developers to create cloud-native applications on Kubernetes.
https://draft.sh
MIT License
3.92k stars 395 forks source link

java pack Dockerfile: issues with jar name which breaks the pack #478

Closed jstrachan closed 6 years ago

jstrachan commented 6 years ago

the current Dockerfile hard codes the jar file generated by the maven build to helloworld-jar-with-dependencies.jar https://github.com/Azure/draft/blob/master/packs/java/Dockerfile#L9

which is great for the example but isn't gonna work for many other java projects out there (that are not called 'helloworld' and configure the assembly plugin just so ;).

This is actually a little tricky to fix generically for maven as there's so many ways to create the jar; e.g. via any plugins like: jar, spring-boot, assembly, vertx, wildflyswarm etc. Plus the file name often includes the version inside as well; which would mean having to edit the Dockerfile once on draft create and then again for each time the version changes in the pom.xml.

I did wonder about trying to specify the project.build.finalName on the CLI in the mvn command but that doesn't seem to work, which is a shame.

Replacing this:

COPY --from=BUILD /usr/src/app/target/helloworld-jar-with-dependencies.jar /opt/app.jar

with something like this seems to fix many use cases:

COPY --from=BUILD /usr/src/app/target/*.jar /opt/app.jar

that certainly works for many spring boot apps that just make a single jar.

However the example project creates 2 jars which is gonna cause issues.

So I wonder if a better fix is to try keep the Dockerfile simple and try delegate more power to the pom.xml.

We could use a docker maven profile which could add some custom configuration or steps to ensure that there will always be an app.jar created by copying/renaming the proper jar? Then the Dockerfile would work for all suitable pom.xml files and never need changing - we'd then just need to tweak pom.xml files to add the profile?

Another option could be to add a maven plugin which is invoked after the packaging is created, to look at the pom and contents of target and make an educated guess if there is no explicit configuration in place - for which is the correct jar to use?

e.g. something like switching this:

RUN mvn -f /usr/src/app/pom.xml clean package

with

RUN mvn -f /usr/src/app/pom.xml clean package com.microsoft.azure:draft-plugin:1.0.0:rename-app-jar -Pdocker

Then we can wrap up the logic to guess which jar should be used in a simple mvn plugin - which if well behaved pom.xml's do it already, there will be an app.jar so its a NOOP - otherwise the plugin can analyse the steps invoked so far to figure out the right jar to rename?

Thoughts? The maven plugin seems the simplest and least likely to require any major changes to the Dockerfile or the end users pom.xml - so maybe thats the best approach to start with? Then folks who know what they are doing and know a good canonical jar file can edit the Dockerfile to remove the plugin and use whatever name they choose?

jstrachan commented 6 years ago

if we go the maven plugin route am thinking the logic could be something like the following to find the jar to use:

draft.app.jar = foo-${project-version}.jar

Then once the jar is found, copy it to target/app.jar.

A more optimal approach might be to generate a text file at target/app-jar-name.txt and use that as the file name to copy into the resulting docker image? Then it saves copying 10-40mb of jar in java in each build?

jstrachan commented 6 years ago

Maybe to avoid running the mvn plugin on each build, we could just run it once every now and again (or as soon as the user runs draft create for the first time on the source code) and then use the above plugin logic to deduce the jar file but generate a file at src/main/resources/draft-app-jar-name.txt which could contain the text like ${project.build.finalName}.jar or whatever.

Then the Dockerfile could load the file name from target/classes/draft-app-jar-name.txt expanded via the maven build with any version / artefact id etc?

At least then we don't incur the cost of the mvn plugin on each build (or folks could run it on each build or not). Though one issue with this approach could be that the pom.xml disables filtering on src/main/resources; so we may need to store it in src/main/draft-resources and add an explicit <build><resources> section for this folder as part of draft create?

I'm kinda liking the approach that the mvn build should generate a canonical file at a canonical place that includes the jar file name to use for the app; as we can then try push this capability down into many plugins over time. (e.g. spring boot maven plugin maybe?)

diginoise commented 6 years ago

Hi James,

I've bumped into this via twitter... to specify your final build name as a maven command line argument, please see here: https://stackoverflow.com/questions/13876165/how-to-override-maven-property-in-command-line

Btw, I don't imply that this is the best solution to the jar name problem in docker file.

jstrachan commented 6 years ago

@diginoise thanks for the feedback - yeah I spotted that too; though it requires tweaks to the pom.xml to use a property so you can pass it in on the CLI; then assumes folks are using plugins correctly to respect the finalName property too; so it feels a little vague. It can certainly work for 1 project; but it doesn't feel right to work on any maven project

bacongobbler commented 6 years ago

closed via #590