Docker and Java Spring Boot [Part.1: Continuous Integration]
Tags: java, docker, devops, microservices
Published: 2020-01-22 18:12:07

*This post was originally published in [jaxenter](https://jaxenter.com/cicd-microservices-docker-162408.html).*

In this tutorial, we will learn how Continuous Integration and Delivery can help us to test and prepare a Java application for Docker.


A Continuous Integration (CI) setup will test our code on every update. The practice creates a strong feedback loop that reveals errors as soon as they are introduced. Consequently, we can spend more of our time coding features rather than hunting bugs.

We will use Docker for packaging since it’s supported universally across all cloud providers. Furthermore, Docker is a requirement for more advanced deployments such as Kubernetes. In the second part of the tutorial, we’ll learn your we can use Continuous Deployment (also CD) to deploy new versions to Kubernetes at the push of a button.

## Getting Ready

Here’s a list of the things you’ll need to get started.

- Your favorite code IDE and the Java SDK.
- A [Docker Hub](https://hub.docker.com/) account and Docker.
- A [GitHub](https://github.com) account and [Git](https://git-scm.com).

Let’s get everything ready. First, fork the [repository with the demo](https://github.com/semaphoreci-demos/semaphore-demo-java-spring) and clone it to your machine.

The application is built in Java Spring Boot and it exposes some API endpoints. The project includes tests, benchmarks and everything needed to create the Docker image.

## Continuous Integration

We’ll use Semaphore as our [Continuous Integration](https://semaphoreci.com/continuous-integration) solution. Our CI/CD workflow will:

1. Download Java dependencies.
2. Build the application JAR.
3. Run the tests and [Jmeter](https://jmeter.apache.org/) benchmarks. And, if all goes well…
4. Create a Docker image and push it to Docker Hub.

But first, open your browser at [Semaphore](https://semaphoreci.com) and sign up with GitHub; that will link up both services. The free account includes 1300 monthly build minutes. Click on the **+** (plus sign) next to **Projects** to add your repository to Semaphore:

![Add your project to Semaphore](https://thepracticaldev.s3.amazonaws.com/i/0b7eijpljjghs4yp0x36.png)

The repository has a sample CI/CD workflow. Choose “I will use the existing configuration”.

![Use existing config](https://thepracticaldev.s3.amazonaws.com/i/wqn9jqwigna2v88qqxia.png)

Semaphore will show the CI/CD pipelines as soon as you make a push to GitHub. You can create an empty file and push it with Git:

```bash
$ touch some_file
$ git add some_file
$ git commit -m "add Semaphore"
$ git push origin master
```

Or do it directly from the GitHub using the **Create New File** button.

![First Run](https://thepracticaldev.s3.amazonaws.com/i/3335gnu6hpuztjitourp.png)

Click on the **Edit Workflow** button to view the recently-released Workflow Builder UI.

![Workflow Builder](https://thepracticaldev.s3.amazonaws.com/i/wre100ctbocx22y64l4f.png)

Each pipeline has a **name** and an **agent**. The agent is the virtual machine type that powers the jobs. Semaphore offers several [machine types](https://docs.semaphoreci.com/article/20-machine-types), we’ll use the free **e1-standard-2** model with an [Ubuntu 18.04](https://docs.semaphoreci.com/article/32-ubuntu-1804-image).

**Jobs** define the commands that give life to the CI/CD process, they are grouped in **blocks**. Click on the “Build” block to view its job:

![Build Block](https://thepracticaldev.s3.amazonaws.com/i/hi67oirsihb9s4x17j00.png)

Jobs in a block run concurrently. Once all jobs in a block are complete, the next block begins.

The first job downloads the dependencies and builds the application JAR without running any tests:

```bash
checkout
cache restore
mvn -q package jmeter:configure -Dmaven.test.skip=true
cache store
```

The block uses some of the Semaphore's toolbox scripts: [checkout](https://docs.semaphoreci.com/article/54-toolbox-reference#checkout) to clone the repository and [cache](https://docs.semaphoreci.com/article/54-toolbox-reference#cache) to store and retrieve the Java dependencies.

The second block has two test jobs. The commands that we define in the [prologue](https://docs.semaphoreci.com/article/50-pipeline-yaml#prologue) run before each job in the block:

```bash
checkout
cache restore
mvn -q test-compile -Dmaven.test.skip=true
```

![Test Block](https://thepracticaldev.s3.amazonaws.com/i/7m7p7lmeb8otp1q31k4g.png)

The third block starts the application and runs the benchmarks:

```bash
java -version
java -jar target/spring-pipeline-demo.jar > /dev/null &
sleep 20
mvn -q jmeter:jmeter
mvn jmeter:results
```

### Store Your Docker Hub Credentials

To securely store passwords, Semaphore provides the [secrets](https://docs.semaphoreci.com/article/66-environment-variables-and-secrets) feature. Create a secret with your Docker Hub username and password. Semaphore will need them to push images into your repository:

- Under **Configuration** click on **Secrets**.
- Press the **Create New Secret** button.
- Create a secret called “dockerhub” with your username and password:

![Create Secret](https://thepracticaldev.s3.amazonaws.com/i/6bv9waxea3qpkg4u929j.png)

### Continuous Delivery

Next to the benchmark block we find the a [promotion](https://docs.semaphoreci.com/guided-tour/deploying-with-promotions/) which connects the CI and the “Dockerize” pipelines together. Promotions connect pipelines to create branching workflows. Check the **Enable automatic promotion** option to start the build automatically.

![Automatic Promotion](https://thepracticaldev.s3.amazonaws.com/i/xxvteu3gsy12vdz9dgnd.png)

The demo includes a [Dockerfile](https://docs.docker.com/engine/reference/builder/) to package the application into a Docker image:

```Dockerfile
FROM openjdk:8-jdk-alpine
ARG ENVIRONMENT
ENV ENVIRONMENT ${ENVIRONMENT}
COPY target/*.jar app.jar
ENTRYPOINT ["java","-Dspring.profiles.active=${ENVIRONMENT}", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
```

The “Dockerize” pipeline is made of one block with a single job which:

- Logins to Docker Hub
- Pulls the latest image
- Builds the new image with the updated code
- Pushes the new image


```bash
mvn -q package -Dmaven.test.skip=true
echo "$DOCKER_PASSWORD" | docker login  --username "$DOCKER_USERNAME" --password-stdin
docker pull "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest || true
docker build --cache-from "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest \
      --build-arg ENVIRONMENT="${ENVIRONMENT}" \
      -t "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest .
docker push "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest
```

### Testing the Image

Click on the **Run the workflow** button and then choose **Start**.

![Start Workflow](https://thepracticaldev.s3.amazonaws.com/i/7d67iz0k19geujsr04an.png)

After a couple of minutes the workflow should be complete and you should have a new Docker image with your application in Docker Hub:

![CI/CD Workflow done](https://thepracticaldev.s3.amazonaws.com/i/lke0iw5phlx5yz12ervf.png)

By now, you should have a ready Docker image in your repository. Let’s give it a go. Pull the newly created image to your machine:

```bash
$ docker pull YOUR_DOCKER_USER/semaphore-demo-java-spring:latest
```

And start it in your machine:

```bash
$ docker run -it -p 8080:8080 YOUR_DOCKER_USER/semaphore-demo-java-spring
```

You can create a user with a POST request:

```bash
$ curl -w "\\n" -X POST \
    -d '{ "email": "wally@example.com", "password": "sekret" }' \
    -H "Content-type: application/json" localhost:8080/users

{"username":"wally@example.com"}
```

With the user created, you can authenticate and see the
secure webpage:

```bash
$ curl -w "\n" --user wally@example.com:sekret localhost:8080/admin/home

<!DOCTYPE HTML>
<html>
<div class="container">
<header>
<h1>
Welcome <span>tom@example.com</span>!
</h1>
</header>
</div>
```

You can also try it with login page at [localhost:8080/admin/home](http://localhost:8080/admin/home)

## Next Stop: Kubernetes


See you in Part 2 to learn how to deploy the image to *any* Kubernetes:

{% link https://dev.to/semaphore/kubernetes-and-java-spring-boot-part-2-continuous-deployment-47l9 %}


## Conclusion

You have set up your first CI/CD pipeline. With this system in place you can work in your code, secure in the feeling that it’s being constantly tested.

Stay tuned for part 2 of the tutorial next week, we’ll see how to do Continuous Deployment to a Kubernetes cluster.

Did you find the post useful? Let me know by ❤️-ing or 🦄-ing below! Do you have any questions or suggestions for other tutorials? Let me know in the comments.

Thank you for reading!