Spring Boot heroku example


heroku-spring

Goal of this example

This example demonstrates the deployment options of Spring boot applications to heroku. The topics covered are :

  • Deploy using Heroku CLI
  • Deploy using Heroku maven plugin
  • Deploying jar file without uploading the source code
  • Spring boot configuration for Heroku
  • Deploying multi module maven project
  • Implementing communication among applications
  • Using web/worker processes
  • Deploy using Travis CI

Technology Used

  • Spring boot 1.4.RELEASE
  • Heroku
  • Maven 3

Deploying spring boot to heroku, the official way using git

Deploying spring boot to heroku is quite easy, because heroku supports java applications packaged as war and jar out of the box. The prerequisites are

  • Create a heroku account
  • Install heroku CLI from here
  • set up heroku locally using
heroku login

Once it is done, the steps are straightforward

#Download an empty spring boot application
curl https://start.spring.io/starter.tgz -d style=web -d style=actuator -d name=heroku-example | tar -xzvf -

git init
git add .
git commit -m "initial commit"

# creates a heroku application with random name (and Domain)
# creates a git repository at heroku
# registers the remote repository by the name heroku
heroku create

#Pushes the source code to the heroku git repo, so heroku can build and deploy it
git push heroku master

#opens our app in a browser 
heroku open

What happens under the hood is

  1. After pushing code to the remote repository named heroku, the build process is triggered
  2. Given that the project has a pom.xml heroku recognizes it as a java application
  3. Spring boot has an embedded tomcat therefore it can be started up as a jar file and will work as a web server

This process is well described in the official heroku devcenter article. This is really easy and everything works out of the box, for simple applications, but raises some concerns :

  • What if my project is not using maven?
  • What if I am not using git?
  • What if I don’t wan’t heroku to build my app from source code, because
    • I am working a closed source project and don’t want to send the code to heroku
    • I am using maven dependencies available only in my company’s internal maven repo, so the code can’t be compiled on heroku
  • What if I my project is a multi module?
  • What if I want to deploy something to heroku without making a git commit?

We will address these concerns one by one.

Deploying jar file without source code using heroku deploy CLI Plugin

The heroku deploy CLI plugin can directly send jar files to heroku. It’s usage is quite easy

heroku plugins:install heroku-cli-deploy
mvn clean install
# Creates an app with the specified name, without setting up a git remote
heroku create <APP-NAME> --no-remote

#deploys the jar file
heroku deploy:jar target/demo-0.0.1-SNAPSHOT.jar --app <APP-NAME>

This is well descibed in the official devcenter article until this point. But interestingly it we open the app using

heroku open --app <APP-NAME>

nothing happens, the application is not accessible. Looking into the logs using

heroku logs --app <APP-NAME>

will explain the reasons. There is a relevant log line saying

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch 
Stopping process with SIGKILL

Heroku expects our application to listen on the port specified by the PORT environment variable, but it doesn’t. There are two ways to fix it :

Properties

Add the following to application.properties

server.port=${PORT:8080}

then rebuild and redeploy the app. This time the port will be read from the PORT environment variable. Locally where the PORT environment variable is not set it will fall back to 8080. The same can be achieved, by overriding the server.port in a different spring profile

Procfile

Heroku supports AWS style Procfile. Create one in the application root directory (with capital P) and enter

java $JAVA_OPTS -jar target/demo-0.0.1-SNAPSHOT.jar --server.port=$PORT $JAR_OPTS

Then deploy the app using

heroku deploy:jar -j target/demo-0.0.1-SNAPSHOT.jar -i Procfile --app 

Passing options

The heroku deploy:jar command can pass additional parameters to the jar file, so it should be possible to execute

heroku deploy:jar target/demo-0.0.1-SNAPSHOT.jar -o --server.port=$PORT --app 

but for some reason the escaping seems to be broken and it does not work

Deploying jar file using heroku maven plugin

Using the heroku maven plugin has a number of advantages:

  • It maintains all configuration in a single place, eliminating the need of a Procfile
  • It requires no installation of heroku CLI and heroku deploy CLI plugin
  • It works out of the box on any CI server without any additional setup

Configure the plugin like this :

  <properties>
     <full-artifact-name>target/${project.artifactId}-${project.version}.jar</full-artifact-name>
  </properties>

  <build>
   <plugins>
    <plugin>
        <groupId>com.heroku.sdk</groupId>
        <artifactId>heroku-maven-plugin</artifactId>
        <version>1.1.1</version>
        <configuration>
            <appName>YOUR APP NAME COMES HERE</appName>
            <includeTarget>false</includeTarget>
            <includes>
                <include>${basedir}/${full-artifact-name}</include>
            </includes>
            <jdkVersion>1.8</jdkVersion>
            <processTypes>
                <web>java $JAVA_OPTS -jar ${full-artifact-name}</web>
            </processTypes>
        </configuration>
    </plugin>
   </plugins>
  </build>

Once the plugin is set up the application can be deployed with the following commands

#if the application is packaged as jar
mvn heroku:deploy
#if the application is packaged as war
mvn heroku:deploy-war

Heroku CLI has to be installed because the maven plugin will read authentication information from there. To eliminate installation, the maven plugin can be executed like this:

HEROKU_API_KEY="YOUR API KEY COMES HERE" mvn heroku:deploy

The API Key can be found here.

The official description of the heroku maven plugin is here.

Deploying multiple applications that communicate with each other

If there are multiple applications communicating with each other then heroku supports two approaches:

Create a single application with a web and a worker process

Within a single application multiple processes can be created. The free tier allows two process types, but only if credit card details are provided. It can be either defined in a proc file

web: java $JAVA_OPTS -jar web-service/target/web-service-0.0.1-SNAPSHOT.jar --server.port=$PORT
worker: java $JAVA_OPTS -jar worker-service/target/worker-service-0.0.1-SNAPSHOT.jar

or in the maven plugin

            <includes>
                <include>${full-artifact-name}</include>
                <include>${worker-full-artifact-name}</include>
            </includes>
            <processTypes>
                <web>java $JAVA_OPTS -jar ${full-artifact-name}</web>
                <worker>java $JAVA_OPTS -jar ${worker-full-artifact-name}</worker>
            </processTypes>

The benefit of this approach is that multiple processes can be deployed in a single step, still they can be scaled independently using

heroku ps:scale web=2 worker=4

There are also some limitations:

  • Only the web process can be accessed over http. This is true not just to end users, but even the web process won’t be able to access the worker process through internal network. It can be addressed even by subscribing to the enterprise package that has private spaces or by connecting the processes using messaging, e.g.: RabbitMQ that is available in the free tier too.
  • The processes can’t be deployed independently. The deployment of an application must always contain all process otherwise the missing processes are are removed

Due to these two limitations implementing a microservice project with lots of services is not really efficient this way.

Create multiple applications

A more robust option is to create separate heroku applications for every  application. If applications are separate maven applications then nothing additional is required, just configure the heroku plugin with the correct application name. All applications will be web workers, available on the public internet. Heroku applications still won’t be able to communicate with each other in a private network therefore they need to know each other’s public address. This is not just a possible source of performance degradation, but also may cause a security risk as every application must make sure that correct authentication and authorization is implemented.

If applications are in a multi module project then all module should include the heroku plugin (with different application name configured). If it is required to deploy all of them them with a single command then the heroku plugin can be moved to a profile like this

    <profiles>
        <profile>
            <id>heroku</id>
            <activation>
                <property>
                    <name>heroku</name>
                </property>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <groupId>com.heroku.sdk</groupId>
                        <artifactId>heroku-maven-plugin</artifactId>
                        <version>1.1.1</version>
                        <configuration>
                            <appName>YOUR APP NAME COMES HERE</appName>
                            <includeTarget>false</includeTarget>
                            <includes>
                                <include>${full-artifact-name}</include>
                            </includes>
                            <jdkVersion>1.8</jdkVersion>
                            <processTypes>
                                <web>java $JAVA_OPTS -jar ${full-artifact-name} --spring.profiles.active=heroku</web>
                            </processTypes>
                        </configuration>
                        <executions>
                            <execution>
                                <phase>verify</phase>
                                <goals>
                                    <goal>deploy</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

and triggered from the project root by like this

 
mvn clean install -Pheroku 

Putting it all together, creating a CI / CD pipeline and deploying with Travis CI

Using the the above knowledge a complete automated CI pipeline can be set up using Travis CI and GitHub. What we aim is :

  1. Developer commits to git
  2. Travis CI builds the project
  3. Travis CI executes the heroku maven plugin to deploy the applications to heroku

Steps are :

  1. Link your github account with Travis CI. Steps are here
  2. Enable build triggering for your repo
  3. Store the heroku API key in a repository variable, named HEROKU_API_KEY, as described here
  4. Create a .travis.yml file with the below content
 
language: java
jdk: oraclejdk8
script: HEROKU_API_KEY="$HEROKU_API_KEY" mvn clean verify -Pheroku

Once the build is triggered using the API key and the maven heroku plugin application(s) will be deployed to heroku. Travis CI variables are by default not included in the logs therefore it is safe to store secret keys there.

Summary

Using Spring Cloud, Heroku, GitHub and Travis CI a complete application can be created and deployed using free tools (or at least the free plans of paid tools). Heroku itself is very well documented and easy to use, the free version even allows custom domain names therefore it is suitable for professional use as well. Some advanced settings like private networks are not available in the free subscription.

Bonus, heroku pipelines

The build pipeline should automatically deploy commits to the dev environment, staging or prod deployment should be a manual process after QA approval. The heroku pipeline is a handy tool to move specific application versions across environments. The full description is here

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

%d bloggers like this: