Heroku slug size calculation? - java

How does Heroku calculate slug size?
I was doing a simple Google web toolkit web app. I used Spring Roo to help me with the boiler code and it created a small app "expenses", the same app as showed at Google IO.
To the generated POM.xml file I added a <artifactId>jetty-runner</artifactId> as one do in the spring mvc tutorial for Heroku.
Now when I run git push heroku master in the terminal maven start fetching dependencies on the Heroku side and I get [INFO] BUILD SUCCESS but then Heroku rejects my push.
-----> Push rejected, your compiled slug is 138.0MB (max is 100MB).
See: http://devcenter.heroku.com/articles/slug-size ! Heroku push rejected, slug too large
My generated war file ends up 31Mb when locally created. But the target directory ends up something like the compiled slug size so I added a slugignore file.
$ cat .slugignore
target/*
!target/*.war
Pushed up this to Heroku and it still throws this at me Push rejected, your compiled slug is 138.0MB (max is 100MB).
So my question is Heroku calculate its slug size? I have read their documentation but it's very sparse.

Since this is one of the first links on Google for this, I figured I'd add the solution that I found, via a simple blog post by Chris Auer which details adding a maven plugin execution: http://enlightenmint.com/blog/2012/06/22/reducing-slug-size-for-heroku.html
EDIT: based on oers comment, the highlights from the above link are to declare the maven-clean-plugin in your plugin executions and include the target/ directory (and all subdirectories) while excluding *.war files. The executions phase of that plugin should be invoked on the install phase (not just the clean phase).
In it's entirety (in case that site goes down), it looks about like this:
<plugins>
....
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<filesets>
<fileset>
<directory>target/</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>dependency/*.jar</exclude>
<exclude>*.war</exclude>
</excludes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
<excludeDefaultDirectories>true</excludeDefaultDirectories>
</configuration>
<executions>
<execution>
<id>auto-clean</id>
<phase>install</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
....

Slug size from https://devcenter.heroku.com/articles/slug-compiler
Your slug size is displayed at the end of a successful compile. You can roughly estimate slug size locally by doing a fresh checkout of your app, deleting the .git directory, and running du -hsc.
$ du -hsc | grep total

Related

spring boot maven plugin - jvm arguments - direct memory size setting

I am using spring-boot-maven-plugin to build a docker image from my app with this command:
spring-boot::build-image
And I need to change default size of jvm direct memory (default is 10m) with this jvm argument:
-XX:MaxDirectMemorySize=64M
and this is my desperate attempt to do that in pom.xml of app where I was trying everything what came to my mind:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<jvmArguments>-XX:MaxDirectMemorySize=64M</jvmArguments>
<environmentVariables>
<JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</JAVA_TOOL_OPTIONS>
<JAVA_OPTS>-XX:MaxDirectMemorySize=64M</JAVA_OPTS>
</environmentVariables>
<systemPropertyVariables>
<JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</JAVA_TOOL_OPTIONS>
<JAVA_OPTS>-XX:MaxDirectMemorySize=64M</JAVA_OPTS>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
<image>
<name>docker.io/example/${project.artifactId}:${project.version}</name>
</image>
<excludeDevtools>true</excludeDevtools>
<systemPropertyVariables>
<JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</JAVA_TOOL_OPTIONS>
<JAVA_OPTS>-XX:MaxDirectMemorySize=64M</JAVA_OPTS>
</systemPropertyVariables>
<environmentVariables>
<JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</JAVA_TOOL_OPTIONS>
<JAVA_OPTS>-XX:MaxDirectMemorySize=64M</JAVA_OPTS>
</environmentVariables>
<jvmArguments>-XX:MaxDirectMemorySize=64M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005</jvmArguments>
</configuration>
</plugin>
But direct memory is still setup to 10M :(
which I can see in log file when the app is booting up:
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 -Xmx11521920K -XX:MaxMetaspaceSize=144375K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
Can anybody advise me what Am I doing wrong?
NOTES:
Spring Boot: 2.3.7.RELEASE
when I run docker image manually with -e, change of jvm argument is working:
docker run -e JAVA_TOOL_OPTIONS=-XX:MaxDirectMemorySize=64M docker.io/example/app-name:0.0.1
this mvn plugin is using Buildpack to create the docker image:
https://paketo.io/docs/buildpacks/language-family-buildpacks/java/#about-the-jvm
UPDATE:
by the doc this should be working solution but it is not:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
<image>
<name>docker.io/example/${project.artifactId}:${project.version}</name>
<env>
<JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</JAVA_TOOL_OPTIONS>
</env>
</image>
</configuration>
this is a working solution on how to make runtime env "sticky":
....
<image>
<name>docker.io/example/${project.artifactId}:${project.version}</name>
<env>
<BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=64M</BPE_APPEND_JAVA_TOOL_OPTIONS>
<BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
</env>
</image>
...
spring-boot-maven-plugin is using paketo buildpacks:
doc:
https://github.com/paketo-buildpacks/environment-variables
It depends on your goal here. It sounds like you want to change the max direct memory in your container when your app runs so you should be just doing what you indicated works here (and what's listed in the docs).
docker run -e JAVA_TOOL_OPTIONS=-XX:MaxDirectMemorySize=64M docker.io/example/app-name:0.0.1
The doc link you're referencing is talking about configuring options at runtime which is what you are doing with the env variables.
For anyone using Docker on Mac + Spring Boot's build-image, be aware that as of writing the default setting of Docker on Mac is a 2GB VM image that runs Linux with Docker running inside this linux VM.
With the current Spring 2.3.X default Pareto builder, this results in a docker image that has ~300MB available, but a fairly simple spring app (for me) needs ~600MB according to the memory calculator.
So no matter what the value of maxDirectMemorySize, it just would not work. As soon as I upped the mem available to the docker VM it worked just fine

How to share/pass a variable with application properties and pom file

I have a multi module spring-boot project, before integration tests of my app, I start another child module (which is Stub made by another spring boot app) You can see it is attached to "pre-integration-test" and it is working fine finally.
Parent Pom
|
|----myRealApp module(spring boot app)
|----stub module(This is also a spring-boot app)
My question is, is there a way to randomize And share this port (not fixed to 8090), so concurrent builds on Jenkins server can run tests and not fail because address is in use already.
I know I can generate random numbers/ports in spring properties file. But couldn't find a way to pass it to Pom.
application-test.properties of myRealApp:
stub.port=8090
stub.url=http://localhost:${stub.port}/stub/api/v1/domains/
Pom of myRealApp:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${spring.boot.mainclass}</mainClass>
</configuration>
<executions>
<execution>
<id>start-stub</id>
<configuration>
<arguments>
<argument>--server.port=8090</argument>
</arguments>
<mainClass>io.swagger.Stub</mainClass>
<classesDirectory>../my-stub/target/classes</classesDirectory>
</configuration>
<goals>
<goal>start</goal>
</goals>
<phase>pre-integration-test</phase>
</execution>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
You can do that via jenkins Port Allocator Plugin
Once you assign the port (lets say HTTP_PORT), then you can pass this as command line
-Dstub.port=$HTTP_PORT
I recommend you not to randomize at all. My suggestion is to parametrize the server port in the POM and application-test.properties files, and set a value based upon some Jenkins-provided variable: For example, BUILD_NUMBER, which is incremented on every build and thus uniqueness is guranteed.
However, there is a problem about this: You also need to enclose the port number within valid boundaries: TCP ports must be within 1024 and 65535, however BUILD_NUMBER is not limited at all.
How to cope with this? I think a simple Ant task bound to the initialize phase could read the BUILD_NUMBER value, apply it a simple formula 1024+(BUILD_NUMBER % 64512), and set it as the definitive port number variable, which is the one you will reference in the POM and application-test.properties files.

UI code goes missing when deploying to Heroku

I have a Spring-MVC app that uses AngularJS for the front-end and Java in the backend. The java code is in src/main/java and the UI code is in src/main/resources/static. I'm building a fat jar using Maven.
Running locally = everything works.
I can also run the jar from the command line and everything works.
When I deploy to Heroku, the app returns a 404 on / ... it seems like it can't find the UI code anywhere.
I have an identical app with a different (less fancy) AngularJS UI, and it deploys to Heroku without any issues. The only real difference is the UI code exists at the parent src/main/resources/static while my custom app uses gulp - and gulp builds the ui code src/main/resources/static/dist. My Maven POM moves that /dist to target/classes/static when I run the package job, and that's working fine... After mvn clean package I can run my app through IntelliJ or at the command line using java -jar target/blah.jar. But when I push it to Heroku I get an application error, and the Heroku log cites a 404 on path="/".
Note my starting point for these projects was the Stormpath examples for spring-boot-web-angular. The stock example deploys fine with same Procfile, so the only difference is the /dist that my custom UI code has - but Maven should be taking care of that.
My Procfile contains:
web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar
Pom excerpt that copies the UI code to the right spot in target/:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/static</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/static/dist</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
I've been googling for days and reached out to Heroku support, but they said they can't help.
I can't tell if the Maven piece isn't getting picked up when I git push heroku master (after packing locally), or if I'm missing a config option or something in my Procfile.
Would very much appreciate a pointer in the right direction.

Running app jar file on spark-submit in a google dataproc cluster instance

I'm running a .jar file that contains all dependencies that I need packaged in it. One of this dependencies is com.google.common.util.concurrent.RateLimiter and already checked it's class file is in this .jar file.
Unfortunately when I hit the command spark-submit on the master node of my google's dataproc-cluster instance I'm getting this error:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch$1.<init>(RateLimiter.java:417)
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch.createFromSystemTimer(RateLimiter.java:416)
at com.google.common.util.concurrent.RateLimiter.create(RateLimiter.java:130)
at LabeledAddressDatasetBuilder.publishLabeledAddressesFromBlockstem(LabeledAddressDatasetBuilder.java:60)
at LabeledAddressDatasetBuilder.main(LabeledAddressDatasetBuilder.java:144)
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:497)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:672)
at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:180)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:120)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
It seems something happened in the sense of overwriting my dependencies. Already decompiled the Stopwatch.class file from this .jar and checked that method is there. That just happened when I ran on that google dataproc instance.
I did grep on the process executing the spark-submit and I got the flag -cp like this:
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -cp /usr/lib/spark/conf/:/usr/lib/spark/lib/spark-assembly-1.5.0-hadoop2.7.1.jar:/usr/lib/spark/lib/datanucleus-api-jdo-3.2.6.jar:/usr/lib/spark/lib/datanucleus-rdbms-3.2.9.jar:/usr/lib/spark/lib/datanucleus-core-3.2.10.jar:/etc/hadoop/conf/:/etc/hadoop/conf/:/usr/lib/hadoop/lib/native/:/usr/lib/hadoop/lib/*:/usr/lib/hadoop/*:/usr/lib/hadoop-hdfs/lib/*:/usr/lib/hadoop-hdfs/*:/usr/lib/hadoop-mapreduce/lib/*:/usr/lib/hadoop-mapreduce/*:/usr/lib/hadoop-yarn/lib/*:/usr/lib/hadoop-yarn/*
Is there anything I can do to solve this problem?
Thank you.
As you've found, Dataproc includes Hadoop dependencies on the classpath when invoking Spark. This is done primarily so that using Hadoop input formats, file systems, etc is fairly straight-forward. The downside is that you will end up with Hadoop's guava version which is 11.02 (See HADOOP-10101).
How to work around this depends on your build system. If using Maven, the maven-shade plugin can be used to relocate your version of guava under a new package name. An example of this can be seen in the GCS Hadoop Connector's packaging, but the crux of it is the following plugin declaration in your pom.xml build section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>your.repackaged.deps.com.google.common</shadedPattern>
</relocation>
</relocations>
</execution>
</execution>
</plugin>
Similar relocations can be accomplished with the sbt-assembly plugin for sbt, jarjar for ant, and either jarjar or shadow for gradle.

Maven Jetty Plugin OutOfMemoryError When Sharing Instance Between Two Web Apps

I'm using the maven jetty plugin to run my two web applications. One web application is spring mvc UI and the other is a RESTful web application. I able to get the two web applications to communicate when I run two separate mvn jetty:run instances and assign different ports. I have successfully deploy both in the same jetty instance using the same port using the below maven pom.xml configuration. I eventually get a ava.lang.OutOfMemoryError: PermGen space error. What is the best workaround for this?
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.6.8.v20121106</version>
<configuration>
<jvmArgs>-Xmx2024m -Xms2024m</jvmArgs>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<contextHandlers>
<contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
<war>../../api/target/main-api.war</war>
<contextPath>/test</contextPath>
</contextHandler>
</contextHandlers>
</configuration>
</plugin>
Add following jvm argument, if you get error regarding cannot allocate memory then try using lesser value (128 and 256)
-XX:PermSize=256M -XX:MaxPermSize=512M
Reference
What is 'PermSize' in Java?
-XX:MaxPermSize with or without -XX:PermSize
Try running Jetty in forked mode like this:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.6.8.v20121106</version>
<executions>
<execution>
<id>start-jetty</id>
<!-- Set this to the appropriate phase:
pre-integration-test, or earlier test-compile-->
<phase>pre-integration-test</phase>
<goals>
<goal>run-forked</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmArgs>-Xmx2048m -Xms1536m -XX:PermSize=128m -XX:MaxPermSize=256m</jvmArgs>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<contextHandlers>
<contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
<war>../../api/target/main-api.war</war>
<contextPath>/test</contextPath>
</contextHandler>
</contextHandlers>
</configuration>
</plugin>
For more details check Running Jetty in a forked JVM.
And... Make sure you really have 2048 MB of free RAM before starting this.
Try using Plumbr to diagnose any memory leak issues with both your web apps.
http://plumbr.eu/
I eventually get a java.lang.OutOfMemoryError: PermGen space error
How long is eventually? Is one of the webapps redeploying frequently due to changes?
It is very easy to leak classes when redeploying a web app. I would run maven with this setting added to MAVEN_OPTS
-XX:+HeapDumpOnOutOfMemoryError
Run until you get an out of memory error, then load the dump with eclipse mat and see what is filling up your perm gen. Most likely your web app is leaking classes on redeploy.
It depends on which JVM instance require more memory. As example, if tests are forked (by default), and fails due OutOfMemoryError then try configure plugin which launching them:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xmx1024m</argLine>
</configuration>
</plugin>
OR
Apart from heap memory. You have to increase perm size also to resolve that exception in maven use these variables in environment variable. And Sometimes is good also to extend perm memory size -
Set the environment variable:
variable name: MAVEN_OPTS variable value: -Xmx512m -XX:MaxPermSize=256m
Compiled data from resource
Please clarify: what jvm version you are using, what operating system you are on, how many physical memory is installed on your computer. What happen if you reduce your memory requirements for example to 1400M (it could help if you run on 32bit jvm), i.e.:
<jvmArgs>-Xmx1400m -Xms1400m</jvmArgs>

Categories

Resources