Kubernetes CI/CD with GitHub, GitHub Actions and Argo CD

Gabriel Boie
7 min readJan 28, 2022
St. Moritz, Snow Polo

Considering the GitOps best practices of separating your Application Source Code from the Application’s K8s Configuration in two Git(Hub) repositories, this is a quick guide on configuring CI/CD (Continuous Deployment) with GitHub Actions (CI) and pull based deployment via Argo CD.

This workflow can be easily converted to Continuous Delivery by modifying the GitHub Actions. Continuous Deployment is ideal for lower environments (i.e. Development) and can be triggered by a PR merge, push or even a simple commit to the application source code repository.

CI/CD Flow

When triggered by a source code modification event, the GitHub Action Workflow 1 will spring into action and build, compile, and test your application and then package it in a Docker container and publish it to a container repository. The Workflow 1 passes the Docker image name and tag as input and triggers the Workflow 2, which will update the K8s deployment manifest and commit and push the changes to the Application/K8s Configuration repository. Argo CD will be pulling this change and update the application on the K8s cluster.

Pretty straightforward, let’s do it…

Definitions

1. Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications.

2. Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.

3. GitHub Actions Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you’d like, including CI/CD, and combine actions in a completely customized workflow.

4. Continuous Delivery vs. Continuous Deployment

This Atlassian chart best depicts the Continuous Delivery vs Continuous Deployment setup:

Atlassian’s thousand words picture depicting CD vs CD

Read more: https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment

Getting Started

Assumptions: You already have access to a Kuberntes cluster via kubectl. Either a local K8s cluster, such as Docker-Desktop, Minikube, other K8s implementations (i.e. Ubuntu, Rancher, etc.) or a remote cluster.

  1. Installing Argo CD

kubectl create namespace argocd

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Forward the Argo CD UI port to your local machine:

kubectl port-forward svc/argocd-server -n argocd 8080:443

You should be able to access ArgoCD’s API server in your local browser at http://localhost:8080

To login, use username admin and the password output by the following command:

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=”{.data.password}” | base64 -d; echo

You should be able to see something like this:

Argo CD UI

Official Documentation at https://argo-cd.readthedocs.io/en/stable/getting_started/

Setting up GitHub Repositories

The fastest way of doing this is by cloning my two repositories…

For the Application Source Code repository, we will be using a basic Node application that will be packaged in a Docker container and published to DockerHub.

https://github.com/gboie/node-app.git

For the Application/K8s Configuration repository, we will use a simple deployment and service K8s manifest. There are benefits to using Kustomize or Helm, which are supported by Argo CD, but we are keeping this simple and relying on the manifest files.

https://github.com/gboie/argocd-node-app-config.git

Configuring Argo CD

This can be done in a few different ways: using the Argo CD UI, using Argo CD CLI, or using the included K8s manifest file from my repo. Assuming you cloned the Application/K8s Configuration repository, we will go with the lattermost option here:

cd argocd-node-app-config

NOTE: Update repoUrl in application.yaml to match your own argocd-node-app-config git repo path. Self Healing feature is enabled (selfHeal: true) and it will revert any manual changes to the cluster, always reseting to the K8s configuration from the GitHub K8s configuration repository (argocd-node-app-config)

kubectl apply -f application.yaml

You will see our application configuration repository was added to Argo CD:

Drilling down into the application, you can see the node-app service details:

To test your node application in action:

kubectl get svc -n node-app

Forward the cluster port to your localhost:

kubectl port-forward svc/node-app-service 3000:3000 -n node-app

And test via browser:

Setting up GitHub Actions Workflow 1 and 2

I included the two workflows in the git repos, and beside setting your DockerHub credentials as secrets in GitHub Actions, there are no changes required. To reiterate, Workflow 1 is the CI flow and it should be triggered by a developer checkin, push or pull request. For the sake of simplicity and so you can easily test, this is set to manual on demand run, so you don’t spend your time modifying the application code but can instead focus on understanding the CI/CD and ArgoDC.

For the Workflow 1 to work, you will need to create 3 GitHub Actions Secrets:

DocherHub user and password and a GitHub Actions PAT (Personal Access Token)

CI/CD In Motion

Workflow 1 acts as the CI flow, resides on the Application git repository, and is designed to trigger on code updates initiated by developers; it will build the Docker container and push it to the DockerHub in this scenario. In addition, this flow will trigger Workflow 2 and pass the newly built image tag.

I modified the code replacing “World” with “Argo CD” and committed and pushed my changes. I will trigger the Workflow 1 from GitHub Actions UI:

Workflow 2 is the CD flow, resides on the K8s Configuration git repository, and is initiated at the end of Workflow 1 completion; it will take the latest built image tag form Workflow 1 and update the K8s manifest file with it, which in turn will wake up ArgoCD and redeploy the latest application.

You can double check if the Workflow 2 was triggered and built successfully by visiting the Actions tab in your argocd-node-app-config repository:

At this point, the last step in the flow is triggered when Argo CD will check/detect (by default every 3 minutes) and pull the K8s configuration repository changes (in this case the container image tag was updated) and will deploy to your cluster.

CI/CD Flow

The default rolling update will take place, and you can see below the new version being deployed first and then the containers running the old version being terminated after.

You can test the new version of the application by simply refreshing the http://localhost:3000

Deployment Strategies, Rollback and Security Considerations

Argo CD deployment strategies aka Rollouts include blue-green and canary among others; more details can be found here https://argoproj.github.io/argo-rollouts/concepts/

Argo CD Rollbacks can be easily triggered from the UI or CLI, but the idea of GitOps is to rely on Git and revert your changes and let the CI/Argo CD do its magic.

This simple example does not touch the security aspect of the setup, but one nice thing about Argo CD and this overall design is that you have the ability to manage your K8s cluster access via Git. You can limit in GutHub who can commit to the repo (application and K8s config repos), who can review and merge pull requests, etc. You can further separate responsibilities with developers having access only to the application source code repository, while the DevOps team is managing the K8s config repository. This can entirely eliminate the need to create K8s cluster level user resources, roles, etc.

There is a lot to cover here — will touch on scaling strategies, multiple cluster and multiple environments on a part 2.

--

--