Spring Boot CI/CD on Kubernetes using Terraform, Ansible and GitHub: Part 11
Part 11 Deploying Spring Boot Application with Continuous deployment
This is part of a series of articles that creates a project to implement automated provisioning of cloud infrastructure in order to deploy a Spring Boot application to a Kubernetes cluster using CI/CD. In this part we use GitHub Actions to deploy our Spring Boot Application when we update it.
Follow from the start — Introduction
In this article we look at how you can use GitHub Actions to automate the deployment of your Docker image to your Kubernetes cluster. Whenever you push changes to your application, the CI pipeline will compile and build the Docker image and push it to Docker Hub.
You can find the files described in this article in the .github
folder of this repository:
https://github.com/MartinHodges/Quick-Queue-Application/tree/part11
This article shows you how to extend the CI pipeline to include Continuous Deployment (CD). The CD pipeline breaks down into two parts:
- The creation of the Kubernetes manifest files
- The automatic deployment of your application
We will tackle these over two articles.
Extending CI Actions to include CD preparation
In the previous article we saw how GitHub Actions can build a Continuous Integration (CI) pipeline. We will now extend that pipeline to include Continuous Deployment (CD).
In your application, edit the .github/workflows/build.yml
Action file. Extend it with the following:
- name: Copy k8s config
run: |
mkdir /tmp/k8s
cp k8s/* /tmp/k8s
- name: Fetch k8s repo
uses: actions/checkout@v4
with:
repository: ${{ vars.USERNAME_GITHUB }}/Quick-Queue-Deployment
ref: main
token: ${{secrets.my_pat}}
- name: Update
run: |
mkdir -p qqapp
cp /tmp/k8s/* qqapp
- name: Update version
uses: jacobtomlinson/gha-find-replace@v3
with:
find: "image: ${{ vars.USERNAME_GITHUB }}/qqapp:.*"
replace: "image: ${{ vars.USERNAME_GITHUB }}/qqapp:${{github.run_number}}"
include: "qqapp/deployment.yml"
- name: Push to deployment repo
run: |
git config --global user.name ${{ vars.USERNAME_GITHUB }}
git config --global user.email ${{ vars.EMAIL }}
git remote set-url origin https://x-access-token:${{ secrets.my_pat }}@github.com/${{ vars.USERNAME_GITHUB }}/K8s-Deployment.git
git add .
git commit -am "Build ${{github.run_number}}"
git push
This is the Continuous Deployment pipeline.
The way this works is that it creates a Kubernetes deployment repository which is used to signal the need for the a new Docker Hub image to be deployed.
In part 2 of this series you were asked to create an empty GitHub repository called K8s-Deployment
. This is the repository that will hold your deployment configuration for Kubernetes.
Looking at the different sections of this Action, we see how this works.
- name: Copy k8s config
run: |
mkdir /tmp/k8s
cp k8s/* /tmp/k8s
First we create a temporary folder called /tmp/k8s
on the GitHub Actions VM. We copy the /k8s
folder from the root of our project into the temporary folder. This folder holds our deployment.yml
and service.yml
manifest files for deploying our application.
- name: Fetch k8s repo
uses: actions/checkout@v4
with:
repository: ${{ vars.USERNAME_GITHUB }}/Quick-Queue-Deployment
ref: main
token: ${{secrets.my_pat}}
We now fetch our existing Quick-Queue-Deployment
repository. In order to access this repository you will need to create a GitHub Personal Access Token for that repository and store it in the my_pat
GitHub secret. We will come back to that later. At the same time we will add a new GitHub variable (USERNAME_GITHUB
) that contains the username of our Quick-Queue-Deployment
repository.
- name: Update
run: |
mkdir -p qqapp
cp /tmp/k8s/* qqapp
We now have the contents of the repository but it could be blank (as it will be the first time you do this). We create the path for our application (qqapp
). We do this in case we want to use this deployment repository for multiple applications. We then copy in the Kubernetes manifest files we copied into the /tmp/k8s
folder earlier.
- name: Update version
uses: jacobtomlinson/gha-find-replace@v3
with:
find: "image: ${{ vars.USERNAME_GITHUB }}/qqapp:.*"
replace: "image: ${{ vars.USERNAME_GITHUB }}/qqapp:${{github.run_number}}"
include: "qqapp/deployment.yml"
Now we have copied over the manifest files from our project, we now want to update the version of the image we are to deploy. We are using the GitHub variable github.run_number
as the version. This does not guarantee our versions will be contiguous and monotonically increasing but will ensure they are increasing.
The jacobtomlinson/gha-find-replace@v3
Action is used to replace the line that defines our image version with one that includes our latest version. This is found in the deployment.yml
manifest file.
- name: Push to deployment repo
run: |
git config --global user.name ${{ vars.USERNAME_GITHUB }}
git config --global user.email ${{ vars.EMAIL }}
git remote set-url origin https://x-access-token:${{ secrets.my_pat }}@github.com/${{ vars.USERNAME_GITHUB }}/K8s-Deployment.git
git add .
git commit -am "Build ${{github.run_number}}"
git push
In the final section, we push our updated Kubernetes deployment project back to our K8s-Deployment
repository with a commit message that includes the version number.
GitHub Secrets and Variables
You should never store credentials in your code repository. Anyone who has access to the code then has access to your credentials.
GitHub caters for this by allowing you to define secrets and variables for your repository. They are not copied if your repository is cloned, keeping them safe.
In the previous article I showed you how to create a secret. Variables are created the same way but you select the Variables
tab. Whilst secrets are hidden, variables are not. You need to create the following:
Secrets:
- MY_PAT: This must be a personal access token created for the account that will hold your
Quick-Queue-Deployment
repository
Variables
- USERNAME_GITHUB: The username of your account used in the
Quick-Queue-Deployment
repository - EMAIL: The email address of your account used in the
Quick-Queue-Deployment
repository
To generate a Personal Access Token (PAT) for your GitHub account, log in and go to your account. Select Settings
. At the bottom of the menu on the lefthand side, find <> Developer Settings
. Select Personal access tokens
and then Fine-grained tokens
.
Click Generate new token
and give it a name (eg: Access for qqapp CI/CD
). Select a token expiry period. Note that GitHub does not allow more than a year for an access token. Add a description (although you only need one PAT for this purpose, the likelihood is that you will create more and then forget which token has been created for what purpose).
Select Only select repositories
and select your Quick-Queue-Deployment
repository. Under Repository permissions
, select the following for this PAT:
Click Generate token
and then copy the generated token into your MY_PAT
GitHub secret. As GitHub secrets are repository based, you will need to do that for each deployment repository you create. Whilst it is a little less secure, this is why you may want to use a single deployment repository for all related applications.
Testing
You now have the first half of your CD pipeline. Now, when you commit and push a change to your main branch, you should see:
- Your application is compiled
- It turned into a Docker image
- Docker image pushed to Docker Hub
- Your manifest files updated
- The updated files pushed to your deployment repository
Congratulations. You now have the first part of CD pipeline in place.
Summary
In this article we created the first part of your Continuous Deployment (CD) pipline using GitHub Actions. We extended the CI Action to now include the preparation of our manifest files within the Quick-Queue-Deployment
repository ready for our automatic deployment.
Next we will automate the deployment of this Kubenetes configuration using ArgoCD.
Previous — Updating Spring Boot Application with Continuous Integration