How to setup a Spring Boot application on Amazon AWS

I have read a couple of blog posts on how to run a Spring Boot application on Amazon AWS EBS. I haven’t found any tutorial regarding how to install Sprint Boot on EC2.

Deploying Spring Boot on EBS of course is easier and offers many advantages but it hides many details a developers should know to understand how things works under the hood.

So I wrote this tutorial which I think will be very useful to developers who just started to work on Amazon AWS and Spring Boot.

In this tutorial I will assume you have already got an Amazon AWS account. You can use free tier only to run the tutorial.

Step 1: Create a Key Pair

To access the AMI (Amazon Machine Image) we will need a key pair. Amazon EC2 stores the public key only, and you store the private key. Anyone who possesses your private key can decrypt your login information, so it’s important that you store your private keys in a secure place. The keys that Amazon EC2 uses are 2048-bit SSH-2 RSA keys. You can have up to five thousand key pairs per region.

On the left menu of the console go to Key Pairs and then Create Key Pair.

Give a name to the pair, you will need it later.

Then the Fingerprint column will show a RSA key. The RSA key is a short sequence of bytes used to identify a longer public key. It is based on the host’s public key, usually based on “/etc/ssh/ssh_host_rsa_key.pub”. It is used for identification/verification of the host we are connecting to.

Step 2: Choose and configure AMI

We will now chose an Amazon Machine Image were we will install the software.

Step 2.1: Amazon Linux 2

I have chosen the Amazon Linux 2 AMI. It has been released from few month and it is the next generation of Amazon Linux, a Linux server operating system from Amazon Web Services (AWS).

It finally includes the systemd “System and Service Manager”, an initsystem used to bootstrap the user space and to manage system processes after booting. One of the main goals of systemd is the unification of basic configuration and service behaviors across Linux distributions. Since at least 2015, the majority of Linux distributions have adopted systemd, and it is considered a de facto standard.[3]

Step 2.2: create a t2.micro instance

To be eligible for a free tier usage we will choice a t2.micro instance. It will be enough for our learning purposes

Step 2.3 Configure Instace Details

In this step we can change and configure many parameters/value of the VM, such as the network configuration or the number of VMs to launch. For now, keep the defaults,

Step 2.4: Add Storage

There are different options for storing data on AWS. For now, accept all the default values provided

Step 2.5: Add Tags

Tags are important to keep everything in order. I will add a key named “name” and its value will be “spring-boot-1” indicating that this is my first machine running a spring boot instance.

Step 2.6 Configure Security Group

Add here SSH and HTTP protocol. I will use SSH to admin the instance and HTTP as interface to run services.

Select the key pair created before to be able to access the AMI with the private key we stored somewhere.

Clicking on Launch Instances will make the OS of the VM to start running.

In the console, under Instances it is possible to see the Instance State, Instance ID and the Public DNA which we will use later.

Step 2.7 Connect to the instance through ssh client

It is now possible to connect to the instance just created through an ssh client. I will use here a command line client in a ubuntu linux installation.

First, get the ID of your instance using the Amazon EC2 console (please look at the picture above). Also the Public DNS (IPv4) is important, store it somewhere.

Run the following command on your ssh client:

ssh -i SpringBootKey.pem ec2-user@ec2-34-202-9-171.compute-1.amazonaws.com

 

In the above command:

  • SpringBootKey.pem is our private key. Please be sure you launch the ssh command in the same directory where the key is placed (otherwise write the correct path to the key)
  • ec2-user is the user for an Amazon Linux AMI 2
  • ec2-3-83-213-44.compute-1.amazonaws.com is the public ip address of our instance

The result of this command will be:

The authenticity of host ' ec2-3-83-213-44.compute-1.amazonaws.com (3.83.213.44' can't be established.
RSA key fingerprint is a3:8b:10:bd:b6:f5:a3:3f:f1:be:b7:bd:b4:e8:fe:05:00:aa:9c:fc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ec2-34-202-9-171.compute-1.amazonaws.com,34.202.9.171' (RSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
15 package(s) needed for security, out of 16 available
Run "sudo yum update" to apply all updates.

 

We answered yes, confirming this is a fingerprint we recognize. This is stored somewhere in our laptop and checked for every new connection we will establish to our instance. Please be aware that when we stop and the restart our instance, public ip will change and so will do the public key.

We are in our Amazon Linux 2 AMI! We ca execute cat /proc/cpuinfo and free -m to see information about the machine’s capabilities:

processor    : 0
vendor_id    : GenuineIntel
cpu family    : 6
model        : 63
model name    : Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz
stepping    : 2
microcode    : 0x3d
cpu MHz        : 2400.211
cache size    : 30720 KB
physical id    : 0
siblings    : 1
core id        : 0
cpu cores    : 1
apicid        : 0
initial apicid    : 0
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes

[…]
              total        used        free      shared  buff/cache   available
Mem:            985          45         782           0         158         794
Swap:             0           0           0

Step 3 Installing Java & Maven

We will install OpenJDK, the reference implementation of Java which is fully open source while Oracle Java contains some proprietary code. Most Java applications will work fine with either.

sudo yum install java-1.8.0-openjdk-devel

 

After successful install we can check the version installed:

[ec2-user@ip-172-31-41-75 ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

 

We will then install maven:

[ec2-user@ip-172-31-41-75 ~]$ wget http://mirrors.sonic.net/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.zip
[ec2-user@ip-172-31-41-75 ~]$ unzip apache-maven-3.6.0
[ec2-user@ip-172-31-41-75 ~]$ sudo mv apache-maven-3.6.0 /opt
[ec2-user@ip-172-31-41-75 ~]$ sudo mv /opt/apache-maven-3.6.0/  /opt/maven

 

Create a file for updating path environment variable:

[ec2-user@ip-172-31-41-75 ~]$ Vi /etc/profile.d/maven.sh

 

And write the following line inside:

export PATH=/opt/maven/bin:${PATH}

 

then reload your environment variables

[ec2-user@ip-172-31-41-75 ~]$ source /etc/profile.d/maven.sh

 

And check everything is ok

[ec2-user@ip-172-31-41-75 ~]$ mvn -version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T18:41:47Z)
Maven home: /opt/maven
Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.amzn2.x86_64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.14.88-88.73.amzn2.x86_64", arch: "amd64", family: "unix"

 

Step 4: Spring Boot install

Let’s create a directory as follows:

[ec2-user@ip-172-31-41-75 ~]$ cd /home/ec2-user
[ec2-user@ip-172-31-41-75 ~]$ mkdir SpringBootProject
[ec2-user@ip-172-31-41-75 ~]$ vi pom.xml

 

And insert the following text [2]. In the comments many lines are explained.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jtrainer.education</groupId>
    <artifactId>SpringBootTutorial</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!—This is the main class we will use as entry point -->
    <properties>
        <start-class>jtrainer.education.springboot.Application</start-class>
    </properties>
    <!-- Inherit defaults from Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
    </parent>
    <!-- spring-boot-starter-web is the dependency for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <!-- Package as an executable jar so the application will be configured as a service -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 

Then write the following command:

[ec2-user@ip-172-31-41-75 ~]$ mvn clean package

 

This will produce a ./target directory with a SpringBootTutorial-0.0.1-SNAPSHOT.jar file ready to be executed:

[ec2-user@ip-172-31-41-75 ~]$ cd /home/ec2-user/target
[ec2-user@ip-172-31-41-75 ~]$ java -jar SpringBootTutorial-0.0.1-SNAPSHOT.jar

 

Which of course will fail as we don’t have written any jtrainer.education.springboot.Application class.

Step 5: Our First Java Class with Spring Boot

Create the following directory structure:

/home/ec2-user/src/main/java/jtrainer/education/springboot

And within the springboot dir create the java class Application.java [1] as follows:

[ec2-user@ip-172-31-41-75 ~]$ vi Application.java

 

With the following content:

package jtrainer.education.springboot;

import java.util.Arrays;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {

            System.out.println("Let's inspect the beans provided by Spring Boot:");

            String[] beanNames = ctx.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            for (String beanName : beanNames) {
                System.out.println(beanName);
            }
        };
    }
}

I will then recompile and build the jar and execute it again:

[ec2-user@ip-172-31-41-75 ~]$ mvn clean package
[ec2-user@ip-172-31-41-75 ~]$ java -jar SpringBootTutorial-0.0.1-SNAPSHOT.jar

 

This will produce a large output with the names of all beans defined in this Spring Application Context.

Spring Boot is now up and running!

Step 6: our first rest controller

In the file src/main/java/jtrainer/education/springboot/HelloController.java write the following class:

package jtrainer.education.springboot;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot@aws!";
    }
}

 

Then recompile, build and execute:

[ec2-user@ip-172-31-41-75 ~]$ mvn clean package
[ec2-user@ip-172-31-41-75 ~]$ java -jar SpringBootTutorial-0.0.1-SNAPSHOT.jar

 

Then from another shell type the following command:

[ec2-user@ip-172-31-41-75 ~]$ wget localhost:8080

 

Which will output:

--2018-12-29 14:09:58--  http://localhost:8080/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 
Length: 27 [text/plain]
Saving to: ‘index.html’
100%[======================================>] 27          --.-K/s   in 0s      
2018-12-29 14:09:58 (5.12 MB/s) - ‘index.html’ saved [27/27]

Where index.html content is

Greetings from Spring Boot@aws!

Step 7: is this a service deployed on AWS?

Add a new rule in your security group as follows:

The type in your browser:

http://ec2-3-81-1-223.compute-1.amazonaws.com:8080/

you will see the message:

Greetings from Spring Boot@aws!

You can now access the service in HelloController from anywhere in the world!

References

[1] – https://spring.io/guides/gs/spring-boot/

[2] – https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-installing-spring-boot.html

[3] – https://en.wikipedia.org/wiki/Systemd