In a previous post “GitHub Actions Runner on your Kubernetes cluster”, I set up a GitHub Actions runner on my Kubernetes cluster, and used it to automatically deploy my applications whenever the pipeline was triggered.
Although it worked, I soon realized it was not the ideal way to set up this kind of automation. It creates a tight coupling between the GitHub repository and the deployment. It is not a huge problem for some small projects that I develop alone, but I just wanted to do it the right way, so here we go.
Overview
The goal of this post is to set up a complete CI/CD pipeline for a project on GitHub. The pipeline consists of the following steps:
- Define application manifest using Kustomize
- Manage the deployment with ArgoCD
- GitHub Actions: Build and push the Docker image
- ArgoCD Image Updater: Automatically update the image in the deployment
The core of this setup is ArgoCD Image Updater. It is a tool that automatically updates the image in a deployment when a new image is pushed to the container registry. However, to make it work, there are a few prerequisites.
Prerequisites
- A Kubernetes cluster
- A dockerized application to deploy
- ArgoCD installed in the cluster
In addition to above, from the documentation of ArgoCD Image Updater:
- The applications you want container images to be updated must be managed using Argo CD. There is no support for workloads not managed using Argo CD.
- Argo CD Image Updater can only update container images for applications whose manifests are rendered using either Kustomize or Helm and - especially in the case of Helm - the templates need to support specifying the image’s tag (and possibly name) using a parameter (i.e. image.tag).
There are two options here: Kustomize and Helm. Per my understanding, Kustomize is easier to set up, and helm is more geared towards distributing applicationsto customers or other users. So I chose Kustomize for my stuff.
Define application manifest using Kustomize
When using Kustomize, there are three important components to understand:
- Base: The base directory contains the base resources that are common to all environments.
- Overlays: The overlays directory contains the environment-specific resources.
- Kustomization file: The
kustomization.yaml
file found inbase
and eachoverlay
directory.
Basically it serves the purpose of “customizing” the base resources to deploy to different environments. With a kustomization.yml
file, it combines multiple resource yml
files into one application, and it can also deploy additional resources or override existing ones.
An example directory structure:
|
|
I’m not going to show the content of all those yml files here, but let’s go over the kustomization.yml
files.
base/kustomization.yml
|
|
This is a simple example of a kustomization file. It packages together the deployment.yml
and service.yml
files into one application.
overlays/prd/kustomization.yml
|
|
This is an example of an overlay file, which customizes the base resources for the production environment.
Notice that there are additional manifest files here (resources section) that do not exist in the base. This is where you define environment-specific resources such as persistent volumes, ingress, and TLS certificates for HTTPS. \
The namespace and having two replicas are also specific to the production environment.
Another cool feature is the secretGenerator
section. It generates a secret with the specified fields without having to manually encode them in base64.
Deploy
After setting up the application manifest, you can deploy it to your cluster with the following command:
|
|
Don’t forget to push the manifeset files to your git repository, since ArgoCD will watch the repository for changes.
Manage the deployment with ArgoCD
If you’ve used ArgoCD, you probably know how to deploy applications with it. Just create a git application and register the repository URL and path to the manifest files.
For example, I have an application with repository URL as “git@github.com:jywang99/homelab.git” and path as “kube/blog/overlays/prd” (it is the application for this blog!).
Install ArgoCD Image Updater
Installing ArgoCD Image Updater is as simple as this (from the official documentation):
|
|
This deploys a pod called argocd-image-updater
in the argocd
namespace.
Give instructions to ArgoCD Image Updater
ArgoCD Image Updater by itself does not do anything yet. We have to give it some instructions, by means of adding annotations to the applications managed by ArgoCD.
The annotations are added to the ArgoCD application, not the deployment that is managed by ArgoCD.
For example:
|
|
and add these annotations:
|
|
This tells ArgoCD Image Updater to:
- Update the image of the
review
container in the deployment with the image fromjyking99/blog
repository. - Only consider tags that start with
prd-
. - Update the image tag in the
kustomization.yml
file on git repository.
GitHub Actions
Now let’s set up an automatic way to update the image, whenever there is a change in the master branch of our application repository.
For this, I used a slightly modified version of the workflow file from a previous post: GitHub Actions Runner on your Kubernetes cluster.
|
|
Moment of truth
Now, whenever you push a change to the master branch,
- GitHub Actions builds and pushes the new image to the container registry.
- ArgoCD Image Updater detects the new image and updates the image tag in the
kustomization.yml
file. - ArgoCD detects the change in the git repository and applies the new image to the deployment.
In short, any change to the master branch is automatically deployed to the production environment!
Try it out by pushing something to the master branch of your application repository.
Conclusion
Honestly, this setup is a bit overkill for the small personal projects that I work on, especially since I only have one environment (prod). However, having this kind of automated setup just feels good.