Building your own docker image using GitHub Actions
In this post we are going to see an example on how to build and push a docker image for a Spring Boot Application using GitHub Actions as a centralized CI structure for your own repository.
As a software engineering, at some point of your personal projects development you start to think : “ok, but how can I easily version my application?”
Well, talking about Java, of course you can always save countless .jar archives with different versions on your local machine (which is far away from the best or the most sophisticated solution), or you can try one of that pipelines templates from some cloud provider (using GCP, AWS, Azure cloud services it is always possible to do it, but with some cost associated).
However, what if I told you that you can develop a complete pipeline where you can build your application, upload a .jar file on your artifact registry and not only that, but also build your own docker image, push to your Docker Hub and become available to the whole community to download an test your application, all of this using your GitHub repository structure. Well my friends, these are some of the features which GitHub Actions can bring to us.
About the application
On this example we are going to work with a Spring Boot Application which simulates some Order Manager operations. You can see about the application here on my GitHub repository: Order Manager repository
Below, I am going to show you a brief presentation of this application.
Application architecture
As a common Spring Boot application (based on MVC architecture), we are going to see some main layers:
Controllers (let’s say that is where the magic begins);
Services (Business rules implemented);
Repositories (Interacts with Database);
And on this case we have a special layer to handle exceptions and errors that is called as Controller Advice (As much as I find this guy super interesting, we can talk about him on another post).
The database management system used will be Postgresql and it could be some local or public server like the ones that you can create on EphantSQL.
For more information about the business rules and endpoints documentation you can see on README section on the repository (README link).
Package, build and deploy artifact
Once that you have all set for your application, it is time to build your pipeline using GitHub Action.
GitHub action pipelines will be configured based on yml files which will be stored on ./.github/workflows.
First of all, on yml file, we configure the pipeline’s name and configuration on which event our pipeline will be triggered. In this case, it will be every push on main branch with any change on pom.xml file.
name: ci
on:
push:
branches:
- 'main'
paths:
- '**pom.xml'
Pipeline diagrams
After name and trigger configured, we are going to configure our jobs.
Below you can see some pipeline diagrams explaining the steps for building our maven application based on our jobs configuration.
Here we have two main steps, generating your application artifact and building your docker image:
Build and upload artifact;
On build step we just configure the pipeline to execute mvn package in order to generate all the executable package of application;
Once we have build, we configure the artifact upload using mvn deploy command.
build_and_upload_artifact:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Publish to GitHub Packages Apache Maven
run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml
env:
GITHUB_TOKEN: ${{ github.token }}
NOTE: Before the steps of build and upload the artifact it is important to note that we have a step of java setup (Set Up JDK 11) in order to have an appropriate environment to build the application.
Image build and push
First of all we need to login on Docker Hub in order to push the image some steps later. On Docker Hub login we need to configure username and token. As we are handling some sensitive data, I recommend to store this kind of data as a secret environment on your GitHub project;
After that, we configure the docker build and push. On this step, we are going to use the buildX which is a docker CLI plugin with some pre-configurations. Here some important configurations to have on this step:
build-args: Here we have all the environments variable configured in your Dockerfile. Again, it is important to configure these variables as secrets on your repository, as we are working with database credentials for example (super sensitive data);
file: Path to your Dockerfile based on source folder of your repository;
push: flag that indicates if image will be pushed or not;
tags: configured tag for your created image.
image_build_and_push:
needs: build_and_upload_artifact
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
build-args: |
SPRING_DATASOURCE_URL=${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME=${{ secrets.SPRING_DATASOURCE_USERNAME }}
SPRING_DATASOURCE_PASSWORD=${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_MAIL_USERNAME=${{ secrets.SPRING_MAIL_USERNAME }}
SPRING_MAIL_PASSWORD=${{ secrets.SPRING_MAIL_PASSWORD }}
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/order-manager:latest
NOTE: As we configure the environment variables in our build and push job, in our Dockerfile it is necessary to declare the same variables in order to have them configured in your image built. Below, it is possible to see Dokerfile example with all the variables declared as docker image layers.
FROM alpine:3.19.0
RUN apk update \
&& apk upgrade \
&& apk add openjdk8 \
&& mkdir /orderSystem
ARG SPRING_DATASOURCE_URL
ENV SPRING_DATASOURCE_URL ${SPRING_DATASOURCE_URL}
ARG SPRING_DATASOURCE_USERNAME
ENV SPRING_DATASOURCE_USERNAME ${SPRING_DATASOURCE_USERNAME}
ARG SPRING_DATASOURCE_PASSWORD
ENV SPRING_DATASOURCE_PASSWORD ${SPRING_DATASOURCE_PASSWORD}
ARG SPRING_MAIL_USERNAME
ENV SPRING_MAIL_USERNAME ${SPRING_MAIL_USERNAME}
ARG SPRING_MAIL_PASSWORD
ENV SPRING_MAIL_PASSWORD ${SPRING_MAIL_PASSWORD}
COPY target/*.jar /orderSystem/order_manager.jar
EXPOSE 8080/tcp
CMD ["sh"]
ENTRYPOINT ["java", "-jar", "orderSystem/order_manager.jar"]
Testing your application using Docker Hub image
As we have our pipeline working and completed, we can see the final result checking our GitHub Packages and our Docker Hub Images.
Cool! Let’s pull the image and test it locally. In order to pull it, just make sure that you have Docker installed on your environment and execute:
docker pull bevilacqua96/order-manager:latest
Once you have the image locally, let’s run and test it!
docker run -p 127.0.0.1:32275:8080/tcp bevilacqua96/order-manager:latest
To test it, we are going to use a simple endpoint to list Items of our Order Manager system: GET http://localhost:32275/order-system/items
That is it, up and running :)
References and final comments
That is some of the features and the power of GitHub Actions for build your pipelines and workflows for your personal projects. If you have something to add or collaborate with the project, please leave your comment, I am always looking for improvements and learning.
https://github.com/docker/build-push-action;
https://github.com/features/actions;
https://docs.github.com/pt/actions;
https://docs.docker.com/;
https://medium.com/@alexander.volminger/ci-cd-for-java-maven-using-github-actions-d009a7cb4b8f;
https://docs.github.com/en/actions/publishing-packages/publishing-java-packages-with-maven.