Spring Boot CI/CD on Kubernetes using Terraform, Ansible and GitHub: Part 11

Martin Hodges
5 min readNov 7, 2023

--

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:

  1. The creation of the Kubernetes manifest files
  2. 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:

  1. Your application is compiled
  2. It turned into a Docker image
  3. Docker image pushed to Docker Hub
  4. Your manifest files updated
  5. 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.

Series Introduction

Previous — Updating Spring Boot Application with Continuous Integration

Next — Finishing your Continuous Deployment (CD) pipeline

--

--