# How to Containerize a Spring Boot App

I am going to show you how to set up a [Spring Boot](https://spring.io/projects/spring-boot) project to run in a [Docker](https://www.docker.com/) container. This will allow you to run your code on any platform where Docker is installed! Anyone will be able to collaborate and run the project by running one line of code. I am going to assume you already have Docker and optionally [Apache Maven](https://maven.apache.org/) installed. Let's get started!

**TLDR:**
Here is a link to my git repo for this post: [Spring Boot Docker Intro](https://github.com/fourgates/blog-docker-spring-boot-container-intro)

## 1. Checkout and (Optionally) Run Spring Boot App
**Note:** 
***If you cannot run the app or don't have Maven installed it's not a big deal. We will get it running in the container! I just typically like to be able to run an app outside of a container before I containerize it. This helps me debug issues if the app does not run as expected in the container.***

First, let's create a new Spring Boot project. Checkout the following Spring Boot starter app:

`git clone https://github.com/spring-guides/gs-spring-boot`

This is a tutorial project, but we can skip to the complete project for the purpose of getting this containerized!

`cd gs-spring-boot/complete/`

Finally (optionally), start the project using Maven:

`mvn spring-boot:run`

Navigate to `localhost:8080` and you should see the following message:


![Screen Shot 2020-12-22 at 1.56.39 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1608663422609/0j1miyPif.png)

## 2. Create a Dockerfile
A `Dockerfile` tells docker how to build an image. We are going to build a `maven` base image (`builder`) so that you can run your code inside of it. It will contain all the dependencies to run your code!

```
### BUILD a maven builder. This will contain all mvn dependencies and act as an abstraction for all mvn goals
FROM maven:3.5.4-jdk-8-alpine as builder

# create app folder for sources
RUN mkdir -p /build
RUN mkdir -p /build/logs

# The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile.
WORKDIR /build
COPY pom.xml /build
#Download all required dependencies into one layer
RUN mvn dependency:resolve
```

## 3. Create docker-compose.yml
Docker Compose is a wrapper around Docker. It lets you encapsulate commands which would be very long and hard to remember. Here we are going to create a `docker-compose` file which will use our `Dockerfile` to build a base image. Subsequently, we will then use this base image to mount our source code into a container and start up the `spring-boot` server!

```
x-config:
  - &MVN_BUILDER "maven/builder:0.0.1"
  - &CONTAINER_NAME "spring-container"
  - &BUILDER_CONTAINER_NAME "maven-builder"
version: "3.7"
services:
  dev:
    image: *MVN_BUILDER
    container_name: *CONTAINER_NAME
    ports:
      - "8080:8080"
    volumes:
      - "./src:/build/src"
      - "./logs:/build/logs"
    command: "mvn spring-boot:run"
  maven:
    build:
      context: .
      dockerfile: Dockerfile
    image: *MVN_BUILDER
    container_name: *CONTAINER_NAME
```

I will cover a few of the terms used in the compose file:

- `dev` and `maven` are two Docker `services`. `dev` is used to run your app during development. `maven` is used for building a base image that contains all of your dependencies. Typically `services` are reserved for a component of an application. e.g. The front-end or the back-end. I typically like to decouple different components. 
- `image` is simply the name of your image.
- `container_name` is simply the name of the container. Docker will generate a unique name if this is omitted. 
- `ports` is where we are mapping a port from inside the container to outside the container. Here we are mapping port `8080` inside the container to port `8080` on our host. 
- `volumes` is where we are mounting our code. In this example, you can see we are mounting our `src` which contains all of our code. We also mount a `logs` folder so that we can persist logs outside of the container.
- `command` is literally the command you want to run when the container starts. Note this will run in the `WORKDIR` we defined in the `Dockerfile`


**Note: ** The `x-config` at the top of the file is used for constants. I like using this pattern because anything that needs to change is on the top of the file. This makes your Docker setup a little easier to maintain over time. If the base image version changes you only need to make an update in one place. I borrowed this pattern from my co-worker [Steven](https://www.linkedin.com/in/steven-beller-115b082b/). Thank you Steven!

## 4. Build a Base Image
Now that we know how to build the image, let's do it!

`docker-compose build maven`

This may take a few minutes, but you should get a success message at the end with the name of your image name. 

## 5. Start Spring Boot
Finally, the moment you have been waiting for!

5

4

3

2

1!

`docker-compose up dev`


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1608663680680/C16WPkkaJ.png)

## Caveats
One thing to note is that the `pom.xml` has been copied into the image and we have "tried" to install all dependencies listed in the `pom`. Therefore, if you need to add any new dependencies you should bump the image version number (`maven/builder:0.0.2`) and rebuild the base image. Don't forget to push the updated image if you are collaborating with others!

Another well-documented issue is that Maven does not always download all the dependencies. The first time you run this new image you can see it goes to download some new dependencies. This can be problematic if you have proprietary maven dependencies or run your container without internet access. However, it is not a blocker, it just requires some proprietary code in your `Dockerfile`! Feel free to comment on any questions about this.

## Conclusion 
That's it! You can now develop code as you would any other Spring Boot app. You don't need to install Maven or a specific version of Java. There are a few things we can do to improve the build time and image size but I will leave that exercise for a future post. You can also push this image to your favorite Docker repo and use it anywhere you would like. Happy Container, Happy Developer!

## Code
Here is a link to my git repo for this post: [Spring Boot Docker Intro](https://github.com/fourgates/blog-docker-spring-boot-container-intro)

## Pro Tip
Use the following `bash` alias to start your app with two characters. 

```
 alias up='docker-compose up dev'
```
