This post was originally published in jaxenter.
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 account and Docker.
- A GitHub account and Git.
Let’s get everything ready. First, fork the repository with the demo 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 solution. Our CI/CD workflow will:
- Download Java dependencies.
- Build the application JAR.
- Run the tests and Jmeter benchmarks. And, if all goes well…
- Create a Docker image and push it to Docker Hub.
But first, open your browser at Semaphore 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:
The repository has a sample CI/CD workflow. Choose “I will use the existing configuration”.
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:
$ 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.
Click on the Edit Workflow button to view the recently-released Workflow Builder UI.
Each pipeline has a name and an agent. The agent is the virtual machine type that powers the jobs. Semaphore offers several machine types, we’ll use the free e1-standard-2 model with an Ubuntu 18.04.
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:
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:
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 to clone the repository and cache to store and retrieve the Java dependencies.
The second block has two test jobs. The commands that we define in the prologue run before each job in the block:
checkout
cache restore
mvn -q test-compile -Dmaven.test.skip=true
The third block starts the application and runs the benchmarks:
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 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:
Continuous Delivery
Next to the benchmark block we find the a promotion 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.
The demo includes a Dockerfile to package the application into a Docker image:
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
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.
After a couple of minutes the workflow should be complete and you should have a new Docker image with your application in Docker Hub:
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:
$ docker pull YOUR_DOCKER_USER/semaphore-demo-java-spring:latest
And start it in your machine:
$ docker run -it -p 8080:8080 YOUR_DOCKER_USER/semaphore-demo-java-spring
You can create a user with a POST request:
$ 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:
$ 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
Next Stop: Kubernetes
See you in Part 2 to learn how to deploy the image to any Kubernetes:
Kubernetes and Java Spring Boot [Part.2: Continuous Deployment]
Tomas Fernandez for Semaphore ・ Jan 30 '20
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!
Top comments (0)