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

Cannot Run Containerized Spring Boot Application #6

Open
ryanjbaxter opened this issue Dec 7, 2017 · 13 comments
Open

Cannot Run Containerized Spring Boot Application #6

ryanjbaxter opened this issue Dec 7, 2017 · 13 comments

Comments

@ryanjbaxter
Copy link

I tried using metaparticle to containerize a simple Spring Boot application but it is not running. I am not sure if maybe I am not doing something correctly or if it is a mismatch between what metaparticle expects and the conventions of Spring Boot. Here is my basic Boot app

package com.example.demo;

import io.metaparticle.annotations.Package;
import io.metaparticle.annotations.Runtime;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static io.metaparticle.Metaparticle.Containerize;

@SpringBootApplication
@RestController
public class DemoApplication {

	private static final int port = 8080;

	@Runtime(ports = {port})
	@Package(repository="docker.io/ryanjbaxter",
			jarFile="target/demo-0.0.1-SNAPSHOT.jar")
	public static void main(String[] args) {
		Containerize(() -> {
			SpringApplication.run(DemoApplication.class, args);
		});
	}

	@RequestMapping
	public String index() {
		return "Hello Metaparticle!";
	}
}

I then run ./mvnw clean package and it all works fine. But then I run the app using the traditional java -jar target/demo-0.0.1-SNAPSHOT.jar, (This is how you would normally run a Boot app) but it fails with

	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.lang.NoClassDefFoundError: io/metaparticle/Util$1
	at io.metaparticle.Util.once(Util.java:45)
	at io.metaparticle.Metaparticle.Containerize(Metaparticle.java:115)
	at com.example.demo.DemoApplication.main(DemoApplication.java:23)
	... 8 more
Caused by: java.lang.ClassNotFoundException: io.metaparticle.Util$1
	at java.net.URLClassLoader$1.run(URLClassLoader.java:370)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 11 more
Caused by: java.io.EOFException: Unexpected end of ZLIB input stream
	at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:240)
	at org.springframework.boot.loader.jar.ZipInflaterInputStream.fill(ZipInflaterInputStream.java:62)
	at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
	at org.springframework.boot.loader.jar.ZipInflaterInputStream.read(ZipInflaterInputStream.java:52)
	at sun.misc.Resource.getBytes(Resource.java:124)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:462)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	... 17 more
@ryanjbaxter
Copy link
Author

I noticed that the tutorial example creates a jar with all the class files for all of the dependencies, why is that necessary?

@brendandburns
Copy link
Contributor

Right now the Java app assumes everything is in one jar.

So you need to build in all of your dependencies.

We could also change the app so that it could allow alternate FROM images that had the spring libraries already in there...

@ryanjbaxter
Copy link
Author

@brendandburns when you say "java app" are you referring to the metaparticle code? The metaparticle jars should be on the classpath, why do the class files need to be packaged in the jar itself?

@brendandburns
Copy link
Contributor

Yes the metaparticle code itself.

The thing is that once the application is packaged as a container, the local classpath is gone, the only classes are the classes in the image.

It would be interesting to introspect the ${CLASSPATH} in the Java code and move any and all of the classses in the $CLASSPATH into the image somewhere...

Probably worth doing, but in the meantime if you want to get a Spring app working, you'll need to package of of the classes into a single .jar.

@ryanjbaxter
Copy link
Author

So could we create an image that contains the metaparticle code within it so its not necessary for each app to do this?

@willpenington
Copy link
Contributor

willpenington commented Dec 17, 2017

Spring Boot bundles the libraries by default, but the reason it won't start is that the plugin it uses to create a fat jar injects a wrapper that calls the real main method and moves all of the non loader classes to BOOT-INF/classes internally so when the command in the Dockerfile tries to start the class directly it isn't found. I tried with an empty new Boot project and got the same error. It does add some metadata to MANIFEST.mf that could be used for special handling, but that would probably only work on the class that Spring thinks is the main method.

META-INF/MANIFEST.mf from my test Boot project for reference:

Manifest-Version: 1.0
Implementation-Title: metaparticle-demo
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: will
Implementation-Vendor-Id: com.willpenington
Spring-Boot-Version: 1.5.9.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.willpenington.metaparticledemo.MetaparticleDemoApplic
 ation
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.5.2
Build-Jdk: 1.8.0_144
Implementation-URL: http://projects.spring.io/spring-boot/metaparticle
 -demo/

@frankreno
Copy link

Curious if anyone has found a good workaround for this. I am definitely interested in giving Metaparticle a go, but this is a blocker.

@brendandburns
Copy link
Contributor

brendandburns commented Jan 5, 2018 via email

@georgeharley
Copy link
Contributor

georgeharley commented Mar 16, 2018

The cause of the problem described in the original post is the mvn package step carried out in the Containerize(). That repackaging overwrites the Spring Boot Jar file that is currently being executed and doing that makes the Spring Jar launcher's index of file locations inside the Jar (created at startup) worthless. Consequently the next attempted load of a class from the Jar fails with the ZLIB problem.
I've tried a couple of fixes locally but so far not fully convinced by either of them. The first approach was to have the mvn package create a new jar in the target folder that had a different name to that specified in the @Package annotation's jarFile value and hence avoid the previously mentioned overwriting. That worked (i.e. java -jar target/mySpringBootjar.jar ran successfully) but that package step feels unnecessary in this case. Skip it completely if it could be detected that we're running inside an executable Jar? I've tried inferring that from some of the runtime information available to the Metaparticle package library (e.g. check if the class containing the Containerize()'d method has the @SpringBootApplication annotation; or examine the current stack and see if things started from a main method) but would that catch all cases? An alternative to all that might be to introduce support for a simple system property that could be used to disable the mvn package step however the runs?

Incidentally, there is another problem lurking here which concerns the generated Dockerfile. It's current final line (CMD java -classpath /main.jar %s) will not succeed in launching an executable Spring Boot Jar (or any executable Jar I think). One simple solution would be to add a new boolean field to the @Package annotation that could be set to true if the assembled Jar (identified by the jarFile field) is an executable Jar or false (the default) otherwise. I'll open an issue and submit a PR for consideration.

Update: the above assertion that the generated Dockerfile won't work for any executable Jar is incorrect. Regular executable Jars work OK with CMD java -classpath /main.jar %s but if it is a Spring Boot Jar things barf ("Error: Could not find or load main class..."). Maybe make the last line of the generated Dockerfile conditional on a check on the class containing the Containerize()'d method: if it does not have the @SpringBootApplication annotation then proceed as normal, otherwise if the annotation is detected then make the generated last line be CMD java -jar /main.jar ?

@georgeharley
Copy link
Contributor

@brendanburns For your consideration I've submitted a PR for this issue that consists of the changes to the library I made locally to containerize a Spring Boot application. Works for running with Maven (mvn spring-boot:run where the POM includes the Spring Boot plugin) and also for running using java -jar target/demo-0.0.1-SNAPSHOT.jar on the built Spring Boot executable Jar. Pretty much a straw man proposal - would be interested in your thoughts on this approach.

georgeharley pushed a commit to georgeharley/package that referenced this issue Mar 30, 2018
brendandburns pushed a commit that referenced this issue Apr 2, 2018
@faiz0019
Copy link

I still get the same issue and also have the latest version of Spring Boot. Can somebody help

@georgeharley
Copy link
Contributor

@faiz0019 I've just tried running a Boot application using the information in package samples and it all works OK. I'm using the latest level of package source and Spring Boot 2.0.3. What kind of failure do you see?

@faiz0019
Copy link

faiz0019 commented Jul 19, 2018

@georgeharley
Im getting the same issue as reported in this thread. I tried to use different methods and none of them worked.
The main problem I could see is that we are using the artifact that is from our own company and cannot use some of the dependencies mentioned in the package samples(Like I cannot use parent and metaparticle-package). I see that is the issue.

We moved from our company base pom and it solved the issue. Thank You for your support

srini85 added a commit to srini85/package that referenced this issue Sep 19, 2018
* Updated Java package tutorial instructions (metaparticle-io#110)

* Correct the expected value of the @Package/repository value
* Mention the need for the @Package/publish=true field
* Removed use of the @Runtime/publicAddress field since the tutorial does not appear to be ACI specific
* Correct the type of service created on the remote Kubernetes cluster

* Added missing unit test dependencies (metaparticle-io#109)

* Update README.md (metaparticle-io#106)

Slight typo. "a nd language of choice" ===> "and language of choice"

* Create dotnet testrunner (metaparticle-io#87)

* fix up examples and documentation

* fix up indenting

* add initial code for a dotnet test runner as part of metaparticle

* add missed file

* renamed env vars to be conssitent and also moved braces to new lines

* Add travis for dotnet (#3)

* add initial travis file

* fix up missing dash

* try again going into the right directory this time

* no need for mono and we can set the dotnet core version

* make build.sh executable

* see what directory we are on

* set to unix type

* build the examples

* move tests to attributes rather than using env var

* Initial Set of Unit Tests For DotNet Metaparticle.Package (metaparticle-io#111)

* Create unit test project and added tests for Metaparticle.Package.Config

* Added unit tests for Config and an initial set of tests for the Driver

* Minor update to Java sharding tutorial instructions (metaparticle-io#112) (metaparticle-io#113)

* Fix typo in tutorial (metaparticle-io#114)

* Support containerizing of Spring Boot apps (metaparticle-io#115)

* Necessary to upgrade PowerMock version in order to run tests
* Additions to gitignore file

* Support sharding. (metaparticle-io#118)

* Update definition of local functions with explicit const (metaparticle-io#120)

* Add some words on Spring Boot support to package Java tutorial (metaparticle-io#121)

* Follow up to previous PR for issue metaparticle-io#6

* Lazy intialize the Docker client. (metaparticle-io#119)

* Fix missing comma, go fmt (metaparticle-io#122)

* Adding rust (metaparticle-io#86)

* learned a touch of rust

* no decorator

* base rust entrypoint

* with bare docker builder and executor (#3)

* Real traits jim (#4)

* add placeholder functions for actual interface
* move builder trait into builder module
* move docker builder struct into correct module
* move existing functions onto trait implementation
* move executor trait to appropriate module
* move executor struct into correct module

* initial builder working

* added docker run support; cleaned up command execs

* refactor to str refs; added readme

* We need strings here, I think

The result of the format! call is a string, (as is the result
of other ways of getting a u64 into a string).

This change makes the method compile without changing anything's
type signature. There might be other was to accomplish this,
but I haven't found one.

* added web example

* use executable's path as Docker context

* wrapped docker cmds in sh

* write dockerfile to docker context explicitly

* fix copy paste error in README

* Python fix + cleanup (metaparticle-io#123)

* general cleanup

* nested options should be processed into objects from dictionaries

* comments on logging; pythonic json read

* Drive-by cleanups (metaparticle-io#126)

* Let requirements match the latest version

* Make print statements Python3-compatible

* insulate more against Py2 -> Py3 changes

* Add python additionalFiles option (metaparticle-io#125)

* add python additionalFiles option

* in case a dir is given

* added tests for additionalFiles

* Lazy intialize the Docker client. (metaparticle-io#127)

* more efficient base image (metaparticle-io#131)

* more efficient base image

* squashed layers

* removed breaking character

* adding certs

* sized down to 6mb and tested with simple http server
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants