Deploying a Java Springboot App to an AWS Amazon Linux 2 EC2 Instance
Tags: aws, java, devops, springboot
Published: 2020-12-27 18:16:30

Hey there, in this post, I'm going to show you step by step how to deploy a Java SpringBoot Application to an AWS EC2 Instance with the new Amazon Linux 2 AMI. 
I recently saw that AWS had stopped its original Amazon Linux AMI and had difficulty finding a way to set it all up quickly. So in this post I am going to consolidate the steps I got over the net and show the entire process of creating a simple Spring boot App, a new EC2 Instance, configuring your Instance and then deploying your code as a WAR file with Apache Tomcat.

Let's get started ! 

# Section 1: Create and Test your Spring boot app locally

You can download the SpringBoot app from my [Github repo](https://github.com/markbdsouza/SpringBootHelloWorld) and test it out or quickly create your own SpringBoot app 

### Generate the SpringBoot App using Spring Initializr
We will create our app using [Spring Initializr](https://start.spring.io/)
We'll be using a Maven Java 8 Project and Packaging would be WAR(Web application ARchive). Also make sure you select Spring Web as a dependency.
![Select Spring Web](https://dev-to-uploads.s3.amazonaws.com/i/9emo6agluh73z64zwj9r.JPG)
It should look something like this after the basic configuration.
![spring initializr configuration](https://dev-to-uploads.s3.amazonaws.com/i/z4f4c5k3udoyj6rpubnz.JPG)
Go ahead hit Generate and extract the zip file.  

### Create a Rest Controller for your App
To keep things simple, we are going to have a simple endpoint which returns a String - 'Hello World'.
Open the unzipped application up with your Java IDE and create a new file (alongside your HelloWorldApplication class under the src/main folder) which will hold our controller. 
I've pasted the code for a basic controller which is the entry point for a HTTP request. We create one class with the RestController annotation and will specify one function within the controller with the annotation @GetMapping. 
So when we send a GET request to http://localhost:8080, we would get a String response of "Hello World"
```
package com.markbdsouza.helloworld.controller;

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

@RestController
public class Controller {
    @GetMapping()
    public String fetchHelloWorld() {
        return "Hello World";
    }
}
```
It's as simple as that. Now once you've created this file. Make sure you do a `mvn clean install` on your command line and build your project. This will also create your WAR file which we will ultimately deploy to our EC2 Instance.

This is a screenshot of the folder structure for reference.
![intellij](https://dev-to-uploads.s3.amazonaws.com/i/cdu6gt7vkhaqpijxg7p7.jpg)
 
### Test it out on your Machine
Once your application is up and running, you can test it out locally by hitting localhost:8080 on your browser to see 'Hello World' on your web page
![localhost testing](https://dev-to-uploads.s3.amazonaws.com/i/q9w18tye3btku9dtua9o.JPG)

Now that we've set it all up and it is working fine, lets start deploying it.


# Section 2: Create your AWS EC2 Instance (Virtual Machine)

### Create your Instance
Create (if you don't have one) and Login to your AWS account. Please note that if you are new to AWS and are within your first year of using it, all of this should be part of the free tier. Post which you would be charged a minor fee. So once done with this tutorial, please terminate anything you create on AWS. 

Once logged in, type EC2 in the AWS services search bar and open it up. The EC2 Service basically lets us create and access new virtual machines in just a few seconds. These machines are called Instances. 
On the top right of your screen you would also see which Region you are in. (Side note: The instances you create are local to your region.) 

![EC2 Home](https://dev-to-uploads.s3.amazonaws.com/i/15x3ves6u574fohl1dz0.jpg) To create a new instance, on the left panel, click on Instances>Instances. This will show you the instances you have on your account. Click on "Launch instances" on the main page to start creating your new instance

![EC2 Step 1 Choose AMI](https://dev-to-uploads.s3.amazonaws.com/i/vs8uj4ew9mppmazhdgfh.JPG) For the AMI(Amazon Machine Image - basically the OS and configuration of the instance we will use) choose the default Amazon Linux 2 AMI and click Select.

![EC2 Step 2 Choose Instance Type](https://dev-to-uploads.s3.amazonaws.com/i/2whov2xw0zk0uh1wpx0z.JPG) The t2.micro is part of the Free tier, select and click Next.

![EC2 Step 3 Configure Instance](https://dev-to-uploads.s3.amazonaws.com/i/iv89b5tr9jpcpf37xhhi.JPG) Keep clicking Next until Step 6. Configure Security Groups

![EC2 Step 4 Add Storage](https://dev-to-uploads.s3.amazonaws.com/i/suw7ljz54mkzt53ynn36.JPG)

![EC2 Step 5 Add Tags](https://dev-to-uploads.s3.amazonaws.com/i/4iuzboam04rl62s5ujzm.JPG)

![EC2 Step 6 Configure Security Group](https://dev-to-uploads.s3.amazonaws.com/i/iv0z5yjbzar5tnosf2gh.JPG) Security group decides who can access your instance. By default the SSH 22 will help us connect to the instance from our system. You can change the source to Anywhere if not set. 
Here we will also add a new security group so we can access port 8080
* Type: Custom TCP Rule
* Protocol: TCP
* Port Range: 8080
* Source: Anywhere

Once done, click on Review and Launch

![EC2 Step 7 Review](https://dev-to-uploads.s3.amazonaws.com/i/2f0x5ghgzgi96kgn0n1y.JPG) 
![EC2 Instance Key Pair](https://dev-to-uploads.s3.amazonaws.com/i/iqfkdtjf4swwnj7ec4r2.JPG)Make sure you download your Key Pair and keep it handy. You will be connecting to your instance using the .pem file that is unique. You will not be able to download this again...
And you're done !! 

![EC2 Instance Launched](https://dev-to-uploads.s3.amazonaws.com/i/8ed3r98jitjz5xllkhhy.JPG)
It could take maybe half a minute for your instance to launch completely. 
Again, once you are done doing your own testing, make sure you TERMINATE your instance from the EC2 summary screen to avoid any unnecessary charges. 


# Section 3: Connect to and configure your Amazon Linux 2 EC2 Instance

###  Connect to your EC2 Instance 
Using the pem file, we will now SSH into our instance. What you will need 
1. Location and name of the key pair created
My path is : C:\Users\madsouza\Desktop\AWS\HelloWorldKeyPair.pem
2. Public IP address of the Instance
You can get this from your AWS EC2 Dashboard by clicking on your EC2 Instance
![EC2 Public IPv4](https://dev-to-uploads.s3.amazonaws.com/i/cwidi1bfrhu9ac5y6xb0.jpg)
For me it is 13.233.111.77

Using the .pem file, we will now SSH into the created EC2 Instance
Run the below command to ssh into your ec2 instance.
```
ssh -i C:\Users\madsouza\Desktop\AWS\HelloWorldKeyPair.pem ec2-user@13.233.111.77
```
where 'ec2-user' is the user with which we will be logging into the system.

![SSH Into EC2 Part 1](https://dev-to-uploads.s3.amazonaws.com/i/tlw6rv9k5xk7cbqpg9mg.jpg) Accept any prompts that might come up and you should be connected to your instance 
 
### Install Java

By default java is not installed on the Amazon Linux 2 AMI. You can check this by doing -
```
java -version
```
Now, to install java 8 onto the machine, go ahead and hit -
```
sudo yum install java-1.8.0
```
You will be asked to confirm the installation 
![EC2 Java Installation](https://dev-to-uploads.s3.amazonaws.com/i/2eoxfulk49k4hrwbzrm0.jpg) In just another second or two, java is installed on your EC2 Instance
`java -version` again would give you the java version installed
![EC2 Java Version](https://dev-to-uploads.s3.amazonaws.com/i/5hogyw8i0wqwcxky612b.jpg)

To install Java 11 instead of 8 use the command -
```
sudo amazon-linux-extras install java-openjdk11
``` 

### Create user and group for tomcat
Now before installing tomcat, lets create a new group and user -
```
sudo groupadd --system tomcat 
sudo useradd -d /usr/share/tomcat -r -s /bin/false -g tomcat tomcat
```
To confirm -
```
[ec2-user@ip-172-31-44-151 ~]$ getent passwd tomcat
tomcat:x:995:993::/usr/share/tomcat:/bin/false
[ec2-user@ip-172-31-44-151 ~]$ getent group tomcat
tomcat:x:993:
```

### Install and Start Apache Tomcat 9
At the time of writing this post, tomcat9 is at v9.0.41. Download the latest version using wget command -
```
export VER="9.0.41"
wget https://archive.apache.org/dist/tomcat/tomcat-9/v${VER}/bin/apache-tomcat-${VER}.tar.gz
```
Now extract the file -
```
sudo tar xvf apache-tomcat-${VER}.tar.gz -C /usr/share/
```
We will create a link to the folder to make it easier -
```
sudo ln -s /usr/share/apache-tomcat-$VER/ /usr/share/tomcat
```
Now we need to provide access to the tomcat user that we created -
```
sudo chown -R tomcat:tomcat /usr/share/tomcat
sudo chown -R tomcat:tomcat /usr/share/apache-tomcat-$VER/ 
```
You can confirm what has been done by navigating to the folder and displaying the files -
```
cd  /usr/share
ls -lrt
```
![EC2 Verify tomcat rights](https://dev-to-uploads.s3.amazonaws.com/i/540a4sja9cvn9ynaxcp0.jpg)

Now create a Tomcat Systemd service -
```
sudo tee /etc/systemd/system/tomcat.service<<EOF
[Unit]
Description=Tomcat Server
After=syslog.target network.target

[Service]
Type=forking
User=tomcat
Group=tomcat

Environment=JAVA_HOME=/usr/lib/jvm/jre
Environment='JAVA_OPTS=-Djava.awt.headless=true'
Environment=CATALINA_HOME=/usr/share/tomcat
Environment=CATALINA_BASE=/usr/share/tomcat
Environment=CATALINA_PID=/usr/share/tomcat/temp/tomcat.pid
Environment='CATALINA_OPTS=-Xms512M -Xmx1024M'
ExecStart=/usr/share/tomcat/bin/catalina.sh start
ExecStop=/usr/share/tomcat/bin/catalina.sh stop

[Install]
WantedBy=multi-user.target
EOF
```

Now time to enable & start up our tomcat service -
```
sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat
```
Check the status of your tomcat server anytime with the below command -
```
[ec2-user@ip-172-31-44-151 share]$ systemctl status tomcat
● tomcat.service - Tomcat Server
   Loaded: loaded (/etc/systemd/system/tomcat.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-12-27 13:50:01 UTC; 16s ago
 Main PID: 5903 (java)
   CGroup: /system.slice/tomcat.service
           └─5903 /usr/lib/jvm/jre/bin/java -Djava.util.logging.config.file=/usr/share/tomcat/conf/logging.properties...

Dec 27 13:50:01 ip-172-31-44-151.ap-south-1.compute.internal systemd[1]: Starting Tomcat Server...
Dec 27 13:50:01 ip-172-31-44-151.ap-south-1.compute.internal systemd[1]: Started Tomcat Server.
```
Other useful commands to stop and restart tomcat are -
```
sudo systemctl stop tomcat
sudo systemctl restart tomcat
```

### Confirm from the GUI that tomcat is running
Tomcat by default runs on port 8080. So now go on to your AWS EC2 Instance Summary Screen and get your Public IPv4 DNS
![EC2 Public IPv4 DNS](https://dev-to-uploads.s3.amazonaws.com/i/pg0rv0pdl9gmsjq2fp79.jpg) For me it is http://ec2-13-233-111-77.ap-south-1.compute.amazonaws.com/
To access your tomcat UI add :8080 to the url
http://ec2-13-233-111-77.ap-south-1.compute.amazonaws.com:8080/
You should see it loaded successfully 
![Tomcat 9 Server](https://dev-to-uploads.s3.amazonaws.com/i/238xwmjds6jhah8zvtum.jpg)

###  Create Tomcat Admin User
Now we need to configure tomcat for an admin user so we can upload our WAR File.
```
sudo vim /usr/share/tomcat/conf/tomcat-users.xml
```
This will open up tomcat-users xml file. Press the key `i` to enter Insert mode. After the opening tag <tomcat-users> go ahead and paste the below few lines and change the password.
```
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="admin" password="TomcatP@sSw0rD" fullName="Administrator" roles="admin-gui,manager-gui"/>
```
![Tomcat Users xml Updated](https://dev-to-uploads.s3.amazonaws.com/i/msr765p5pllh14wdpbqy.jpg)
 
Once entered press Escape to exit Insert mode and type in `wq!`

### Update webapps manager 
We additionally need to make a small change to the below file otherwise you might see a 403/401 error navigating to the tomcat webapps admin page.
```
sudo su
vi /usr/share/tomcat/webapps/manager/META-INF/context.xml
```
Press the key `i` to enter Insert mode. Then comment out the valve tag in the xml.
To comment it out change the below line.
```
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
```
to 
```
<!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->
```
![context xml update](https://dev-to-uploads.s3.amazonaws.com/i/mzlicogwznayemo3cgcb.jpg)
 
Once entered press Escape to exit Insert mode and type in `wq!` to save and quit.

### Login to webapps manager 
You can navigate to the manager page by using the hyper link 'manager webapp' on your tomcat home page ![Tomcat Home Page](https://dev-to-uploads.s3.amazonaws.com/i/zkik32mjhe4ghpyhk9s9.jpg)
Or you can add /manager/html to the end of the <Public IPv4 DNS>:8080 
In my case it would be http://ec2-13-233-111-77.ap-south-1.compute.amazonaws.com:8080/manager/html
On navigating you would be prompted for the username and password. 
This would be username="admin" password="TomcatP@sSw0rD" - the same that we configured earlier.
![tomcat webapp](https://dev-to-uploads.s3.amazonaws.com/i/t2i2w51mxyt3vkuvtf8o.jpg)

Once entered you would see your manager page.
![tomcat webapp page](https://dev-to-uploads.s3.amazonaws.com/i/c7i31q60cj9hlnw2gjg6.jpg)
 

### Update minimum size of the WAR file (OPTIONAL)
If your WAR size is above 50 MB or you foresee it getting over 50MB, you would need to make a minor change to update the minimum size.
```
sudo su
vi /usr/share/tomcat/webapps/manager/WEB-INF/web.xml
```
Press `i` to get into INSERT mode and update the below section -
```
 <multipart-config>
      <!-- 50MB max -->
      <max-file-size>52428800</max-file-size>
      <max-request-size>52428800</max-request-size>
      <file-size-threshold>0</file-size-threshold>
    </multipart-config>
```
to -
```
 <multipart-config>
      <!-- 250MB max -->
      <max-file-size>262144000</max-file-size>
      <max-request-size>262144000</max-request-size>
      <file-size-threshold>0</file-size-threshold>
    </multipart-config>
```

### Upload WAR File
Scroll down to the 'WAR file to deploy' section and upload your .war file. This can be found under the Target folder of your project. 
![tomcat WAR deploy](https://dev-to-uploads.s3.amazonaws.com/i/n9ns1bk6d3rfflonywuc.jpg)
Select your file and click Deploy
Once deployed, you would see a new app under Applications.

![tomcat updated application](https://dev-to-uploads.s3.amazonaws.com/i/osx8kcgffm42gc2wrz3g.jpg)

### Test your application
By clicking on the application loaded or appending the WAR file name to the end of the address, you would be able to 
http://ec2-13-233-111-77.ap-south-1.compute.amazonaws.com:8080/hello-world-0.0.1-SNAPSHOT/

![Spring production test](https://dev-to-uploads.s3.amazonaws.com/i/rba9sv5vo8ws2lquh8or.jpg)
 
Hello World !!!! 
 
There you have it, in almost no time you created your own Spring Boot web application and deployed it to your own EC2 Instance.