Beginner's CI/CD for Kubernetes with GitHub Actions
The world of CI/CD tools is overwhelming. When starting out as a Kubernetes developer, do you even need to know so much? Isn't there a quick way to setup a pipeline and get going, focus on building your amazing piece of software.
The world of CI/CD tools is overwhelming. I mean look at the CI/CD section in CNCF landscape. It's huge for a beginner and new tools are added every few months. When starting out as a Kubernetes developer, do you even need to know so much? Isn't there a quick way to setup a pipeline and get going, focus on building your amazing piece of software. We will have a look at such a setup today.
What should you already have?
- A GitHub repository
- Dockerfile for your application
- A Kubernetes cluster with public API connectivity
Setting Up the Kubernetes Resources
We need to create a deployment to run our application pods. Here's a sample blog service:
The main things to notice here are:
imagePullPolicy: Always
which means that the cluster will always pull the image from remote registry for creating new pods, even if the image with same tag is available locally.- The image tag
latest
is a generic one, not a specific one like a version or commit id etc.
The idea here is that we will push the new versions of our software with the same tag on the container registry. For deployment, we will just require a deployment restart.
GitHub Action to Trigger the Deployment
Now we need to create a GitHub Action that builds our image and triggers a restart on Kubernetes.
Let's complete the build section. I will be pushing to Docker hub but you can use any registry with proper authentication. Look for authentication actions in the marketplace for your registry or use the docker/login-action.
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Generate docker image name
id: image_tag
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- 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@v2
- name: Build and push orchestration service
uses: docker/build-push-action@v4
with:
context: .
tags: |
${{ env.REGISTRY_URL }}/${{ env.SERVICE_NAME }}:${{ steps.image_tag.outputs.sha_short }}
${{ env.REGISTRY_URL }}/${{ env.SERVICE_NAME }}:latest
push: true
We are tagging the image with both the commit ID and the latest
tag.
Now for the deploy part, we need to run kubectl
commands but first we need to authenticate with our Kubernetes cluster.
deploy:
needs: [ build ]
runs-on: ubuntu-latest
steps:
- name: Install Kubectl
uses: azure/setup-kubectl@v3
- name: Setup Cluster Authentication
run: cat ${{ secrets.KUBECONFIG }} > /tmp/kubeconfig
- name: Deploying ${{ env.SERVICE_NAME }}
run: kubectl --kubeconfig=/tmp/kubeconfig rollout restart deployment ${{ env.SERVICE_NAME }}
There are many ways to authenticate. In this case the Kubeconfig file with authentication info is pulled from a secret saved in GitHub Actions.
The complete action looks like this
name: Deploy Goblog
on:
push:
branches:
- main
env:
REGISTRY_URL: docker.io/maytanthegeek
SERVICE_NAME: goblog
SERVICE_TAG: latest
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Generate docker image name
id: image_tag
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- 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@v2
- name: Build and push orchestration service
uses: docker/build-push-action@v4
with:
context: .
tags: |
${{ env.REGISTRY_URL }}/${{ env.SERVICE_NAME }}:${{ steps.image_tag.outputs.sha_short }}
${{ env.REGISTRY_URL }}/${{ env.SERVICE_NAME }}:latest
push: true
deploy:
needs: [ build ]
runs-on: ubuntu-latest
steps:
- name: Install Kubectl
uses: azure/setup-kubectl@v3
- name: Setup Cluster Authentication
run: cat ${{ secrets.KUBECONFIG }} > /tmp/kubeconfig
- name: Deploying ${{ env.SERVICE_NAME }}
run: kubectl --kubeconfig=/tmp/kubeconfig rollout restart deployment ${{ env.SERVICE_NAME }}
Parting Words
CI/CD doesn't need to be a complex pipeline. More simple is more reliable. When starting out, a reliable, no brainer CI/CD can take you a long way.
When your requirements grow, you can implement very similar philosophy with GitOps tools like Flux and Argo. They do a very good job of reconciliation. You can easily have versioned deployments with them which we avaided here.
Have more ideas or an implementation of your own? Share it here so that we can learn together.