Configuring A Staged Java Application On AWS ElasticBeanstalk


Update

I’ve just blogged about a maven only solution.


In this blog post I’ll describe how to deploy and setup a maven driven Java application on AWS ElasticBeanstalk with different staging profiles. To understand what’s going on, you should have knowledge about AWS and how to setup an ElasticBeanstalk environment.

I have setup this github-repository which contains a very basic maven project. Let’s have a look at it’s pom:

No big surprises here: we set the compiler level to Java 8 and tell maven to build an executable fat-jar called app.jar during package-phase. The main itself just prints the passed arguments in the console as shown below:

Executing mvn clean install should generate the class files and a runnable jar file in the target-folder. One could take this file and upload it to ElasticBeanstalk. We’re done.

Just kidding.

Two problems may arise:

  1. ElasticBeanstalk will reject the file the next time you upload it, because it already has a file called app.jar.
  2. The application wouldn’t print any arguments, because you haven’t passed any to it.

Solving the first problem is quite easy: you can just append the current timestamp (or project version) to the jar. Either manually, or by changing the finalName-tag to something like

app-${project.version}

or

app-${maven.build.timestamp}

However this doesn’t solve the second problem. To tackle this, one must understand how the application is started after it was deployed. On the EC2-instance, next to the uploaded jar-file there is a file called Procfile, which is used to start the jar. By default, it consists of a one-liner:

web: java -jar app.jar

The only thing you need to know regarding the first word web is:

The command that runs the main JAR in your application must be called web, and it must be the first command listed in your Procfile.

Good news is, that a user can upload it’s own Procfile which leads us to the question: where to put the arguments, especially if you have different arguments for each staging profile. At first sight, a good fit are maven-profiles, but that solution has two major drawbacks: secret informations like passwords should not be stored in your pom. And secondly, each time you change the arguments, you have to redeploy the whole application.

A much better approach is to store that kind of data on the instance itself using ElasticBeanstalk’s configuration. To do so, go to your environment, select the application you are using (or create a new one based on Java SE-Platform), click on Configuration, Software Configuration and create two new environment variables: JAVA_OPTS and JAVA_ARGS. Set the value of JAVA_OPTS to something like -Dfile.encoding=utf8 and JAVA_ARGS to my_staging_property. The values are now available on the instance – time to create our own Procfile. To do so, I’ve created a bash-script (Windows users look here) like this:

  • Lines 3 and 4: I decided to append the timestamp to the build artifact to fix the redeploy issue.
  • Line 6, 7 and 8: Our Procfile just executes another shell script, called run.sh. That script uses the environment variables defined above to start the application.
  • Line 9: Zip everything into one file.

So instead of executing maven directly, run ./build.sh from the terminal. This will create a file with a name like app-2017-05-03_14-50-07.zip in the target/ – folder of your maven project which can then be deployed to any staging environment. Configuration is solely done by ElasticBeanstalk. E.g. set JAVA_OPTS to -Xmx256M on your dev-environment and -Xmx16G on production. Changing some parameters only requires to update your EB-configuration (e.g. tune some GC-settings during a load test) instead of redeploy everything.

Advertisements

8 thoughts on “Configuring A Staged Java Application On AWS ElasticBeanstalk”

  1. Hey, thanks a ton for this.
    I am stuck on exactly this issue – struggling to inject my own JAVA_OPT when my spring boot fatjar is launched.
    Your approach looks promising, however I don’t quite understand how to connect the dots.
    I have .elasticbeanstalk/config.yml that contains a “artifact: myapp.jar”.. how do I get EBS to pick my Procfile?
    Looks like just adding a Procfile next to my pom.xml doesn’t work. Have you seen this? Any ideas?

    Thank you in advance!

  2. So basically you need a zip file containing
    – the jar
    – the Procfile
    – eventually a bash script if referenced in the Procfile

    See also: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/java-se-platform.html
    Quote: If you only have one JAR file, Elastic Beanstalk will run it with java -jar application_name.jar. To configure the processes that run on the server instances in your environment, include an optional Procfile in your source bundle. A Procfile is required if you have more than one JAR in your source bundle root, or if you want to customize the java command to set JVM options.

    HTH, Jens

    1. Thanks, I am trying right now. Is then what you call “the zip file” (./target/app-$now.zip) the file you then deploy as an artifact on EB?

      1. Thanks for the hint. I got 80% there, but I am stuck.

        I create the bundle (zipfile – myapp.zip) following your approach. Works perfectly, everything is then deployed successfully as a bundle to EB.

        However the app doesn’t start, it looks like EB is not happy about it and doesn’t look for the Procfile at all?

        Below the content of my bundle
        myapp-12345.zip
        |
        + — myapp.jar [this is the executable fatjar, and if tested locally it works]
        + — run.sh
        + — Procfile

        Have you ever seen this? Any ideas?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s