Spring Boot AWS Elastic Beanstalk example


aws-beanstalk-spring

Goal of this example

This example demonstrates how to deploy a Spring boot based application to Amazon Web Services (AWS) using Elastic Beanstalk. The topics covered are :

  • Deploying the official way using CLI
  • Deploying using the unofficial maven plugin
  • Setting up a CI/CD tool using Github and Travis CI
  • Cost saving tips for when using Elastic Beanstalk

Technology Used

  • Spring boot 1.4.RELEASE
  • Beanstalker 1.5.0 maven plugin
  • Elastic Beanstalk
  • Travis
  • GitHub

Briefly about Elastic Beanstalk

AWS consists of more than 70 services (and the number is still growing), for a newstarter it can be quite confusing to do simple things like deploying an application to a server or creating multiple environments. Elastic Beanstalk is an umbrella service, calling other services so we don’t have to do deal with the inner details of AWS (but we can if we want to). What it does :

  • Automatically provisioning infrastructure including EC2 servers, load balancers, security groups, auto scaling groups, CName
  • Manages environments (dev, staging, prod, etc)
  • Manages code deployment across multiple servers including multiple update strategies

It has a easy to use web console and CLI that can handle most needs. Those who want to access the advanced features can directly access the underlying infrastructure components and modify them just like they would be created manually. Elastic Beanstalk supports multiple programming languages, this example covers usage with an Spring Boot based application.

Elastic Beanstalk’s default deployment process consists of the following stages :

  1. upload code to AWS
  2. compile code
  3. deploy artifacts to EC2 servers

For a java project there is no reason to share source code with AWS, or rely on AWS building it. Instead it is better and cleaner to manage the build process by a dedicated build tool or CI server and upload only the artifact, may it be a war or jar file. So our process will look like this

  1. Build artifact locally or through a CI tool
  2. upload artifact to AWS
  3. deploy artifacts to EC2 servers

 

Deploying the official way using CLI

Prerequisites

  • Download the Elastic Beanstalk CLI tool from here
  • You will need an AWS user’s access id and secret key. For security reasons don’t use the master user,
    instead create a new IAM user as described here
 
#Optional step, download an empty spring boot application (or use your existing one) 
curl https://start.spring.io/starter.tgz -d style=web -d style=actuator -d name=demo | tar -xzvf - 

#Initialize the Elastic Beanstalk environment, select the region and application name.
eb init 

When prompted, select region and application name. For platforms you are presented with the following options

 
Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
(default is 1):
  • If your project is compiled into a jar file and runs with embedded web server (the default behaviour of Spring Boot) then choose option 11) Java
  • If your project is a war file choose 5) Tomcat
  • If your project is build with docker then choose 7) Docker

For this example we assume you selected 11) Java, then continue with the setup, select java version and create a keypair. Once this is done you will see the .elasticbeanstalk/config.yml file created. Add the following lines to ensure it uploads the artifact, not the sources

deploy:
  artifact: target/demo-0.0.1-SNAPSHOT.jar

(change the name to match your application’s build artifact name)

Elastic Beanstalk has a concept of environment. It is possible to create multiple environments like dev, staging, prod, etc. To deploy the code at least one environment has to be created. This can be done either from the web console or from command line, but before that the code needs to be compiled to generate the deployment artifact. Depending on your build tool either do one of the below

# for maven projects
mvn clean install

# for gradle projects
gradle build

This is needed because in the next step will immediately upload the artifact to AWS. To create an environment run one of the below commands


# Create a load balancing, auto-scaling environment
eb create

# Create a single instance environment
eb create -s

A load balancer costs roughly 18 USD per month, A single instance environment is cheaper and
it can be enough for development purposes. Once it is done open the console by

eb console

It shows an URL to the instance, open it and see that you get and nginx error page. The reason is that nginx runs on port 80 and it expects our application to run on port 5000 so it can proxy it. The easiest solution is to change our application to listen on port 5000 by adding this to application.properties

server.port=5000

Updating application using CLI

Updating the application is also quite straightforward

# Build artifact
mvn install

# deploy
eb deploy

Once the application is redeployed with the correct port setting, it should finally work as expected, the application is running in AWS!

Elastic Beanstalk maven plugin

Even though the CLI can do everything we need, there are legitimate reason to use the beanstalker maven plugin instead :

  • Keep all Elastic Beanstalk configuration in one place, the pom.xml
  • No need to install the CLI
  • Out of the box integration with any CI system that can run maven

Setting up the plugin

We need an S3 bucket or AWS Code commit repo to store our changes. The easiest is to create an S3 bucket manually here. You can enable versioning of the bucket, if you are interested in keeping all previous versions of the deployment artifacts.

Add the plugin like this :

            <plugin>
                <groupId>br.com.ingenieux</groupId>
                <artifactId>beanstalk-maven-plugin</artifactId>
                <version>1.5.0</version>
                <configuration>
                    <applicationName>spring-boot-aws-elasticbeanstalk-example</applicationName>
                    <s3Bucket>spring-boot-aws-example</s3Bucket>
                    <s3Key>${project.artifactId}/${project.build.finalName}.jar</s3Key>
                    <cnamePrefix>spring-boot-aws-elasticbeanstalk-example-dev</cnamePrefix>
                    <environmentName>dev</environmentName>
                    <environmentRef>dev</environmentRef>
                    <solutionStack>64bit Amazon Linux 2016.09 v2.4.0 running Java 8</solutionStack>
                </configuration>
            </plugin>

The s3Bucket, cnamePrefix, environmentName, environmentRef names should be replaced with the actual values.

Notes:

  • both environmentName and and environmentRef has to be present and set to the same value otherwise you get a NullPointerException when deploying 🙂
  • If there are more environments then the environment name should not be written into the pom.xml, but specified as a -D parameter e.g.:-Dbeanstalk.environmentRef=staging . For some reason the -D parameter has effect only if the value is NOT specified in pom.xml
  • If you don’t specify the solutionStack then by default the application will use a 32 bit tomcat machine image, that is not the best option. The actual value of solution stack may change over time, the current values for Java 8 can be found by running the below command
    mvn beanstalk:list-stacks | grep "Java 8"

Creating environment using the plugin

To create an environment run :

    mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:create-environment

Updating application using the plugin

Updating the application requires uploading a new artifact, creating a new version and update the environment. Options are

  • Plugin execution to wait until redeploy is done or terminate when upload is successful
  • Specify environment name in pom.xml or as a parameter

The options are listed below

    # Update environment speficied in pom.xml without waiting for redeploy
    mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment

    # Update environment specified in pom.xml and wait for redeploy
    mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment beanstalk:wait-for-environment

    # Update environment specified by -D parameter and wait for redeploy
    mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment beanstalk:wait-for-environment -Dbeanstalk.environmentRef=staging

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

Using the the above knowledge the CI / CD process can be set up. This example uses Travis and GitHub, but these steps can be implemented by combining almost any other VCS and CI tool. We aim for the following workflow :

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

Steps are :

  1. Link your github account with Travis CI. Steps are here
  2. Enable build triggering for your repo
  3. Set the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_KEY to their appropriate value. The process is described here
  4. Create a .travis.yml file in your project’s root with the below content
 
language: java
jdk: oraclejdk8
script: AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" AWS_SECRET_KEY="$AWS_SECRET_KEY" mvn clean install beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment

The script works because the maven plugin can read access key and secret from environment variables.

Once the build is triggered it will call the beanstalker plugin, that deploys the application to AWS. Environment variable values are not displayed in the build logs, therefore it is a safe option.

Cleaning up

As for any activity in AWS you have to be very careful about what you use and for how long. Elastic Beanstalk itself is free of charge, but the EC2 containers and especially the load balancers are expensive. The safest option is to delete the environments as soon as you can by any of the below commands

 
  # EB CLI
  eb terminate environment name

  # beanstalker plugin
  mvn beanstalk:terminate-environment

Summary

Using Spring Boot, Elastic Beanstalk, Beanstalker, GitHub and Travis CI a complete application can be created and deployed using free tools (or at least the free plans of paid tools). The power of Elastic Beanstalk is that even though it can be easily operated, the underlying platform consists of standard EC2 components that can be further configured.

Bonus: AWS Code Pipeline

Commits should be auto deployed to the dev environment but updating staging or prod should require, manual review or QA approval. AWS Code Pipeline can be used to create such a build pipeline so changes can be reviewed in Dev then after manual approval propogated to staging and prod. Such a pipeline looks like this:
aws-code-pipeline
Note: be aware of the pricing.

Advertisements

One comment

  1. This is exactly what I was looking for. Thanks for the nice article

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: