cft

How to build a GitOps workflow with ArgoCD, Kustomize and GitHub Actions

Gain speed and clarity by adopting GitOps for your deployments


user

Emmanuel S

3 years ago | 5 min read

What is GitOps?

The term was first coined by Weaveworks in a popular article from August 2017. The problem it intends to solve was how to efficiently and safely deploy a Kubernetes application. The main tenants of this philosophy are:

  • Use a Git repository as a single source of truth.
  • Any change is made in the form of a Git commit.
  • When the application state differs from the desired state (that is: what’s in Git), a reconciliation loop detect the drift and try to reach this state.

directly from Weaveworks

The reason why GitOps is especially suited to deploy cloud-native applications is that Kubernetes follows the same declarative way of doing things:

  • You submit a bunch of Kubernetes objects through a declarative document as YAML or JSON.
  • Kubernetes Operators process is endlessly evaluating the difference between the submitted objects and their real state in the cluster, adding, modifying, or deleting them as needed.

When you understand the concept, you can apply the GitOps way not only to Kubernetes application but to anythings described with code, for example, code infrastructure.

But what is the difference between GitOps and the final deploy step of my CICD pipeline?

Very often your pipeline is triggered by a change in code (if not, it really should!), so it’s in fact the same starting point as GitOps. Your final pipeline step then run a command like kubectl apply. You so run an imperative command to reach the desired state.

In GitOps, you won’t do this: it’s an external tool that detects the drift in your Git repository and will run these commands for you. You can think of it as a “pulling” way of doing things.

Let’s look into these tools.

What tools are available to implement GitOps?

The most commonly used tools are Flux from Weaveworks and ArgoCD. You may find extensive comparisons of both tools but to sum it up:

  • Flux can only observe one repository at a time, meaning you have generally one flux instance running for each application.
  • ArgoCD may observe multiple repositories, comes with a GUI dashboard, maybe federated with an identity provider: it’s more enterprise-ready.

In this article, we will look to implement a GitOps model using ArgoCD.

Our GitOps workflow

Tools

We will implement a GitOps scenario using:

  • ArgoCD as the GitOps tool
  • GitHub Actions as the CICD pipeline
  • Kustomize to describe application deployments

Starting point

The code is available on GitHub. You will find step by step instructions on how to make it works for you by installing a Minikube cluster, ArgoCD, and set up the required security tokens.

Application

Our application is a simple Go application displaying the good old “Hello World” string.

100% original real life, polite application

The associated Dockerfile is quite simple, building the application then running it in a Linux container

FROM golang:1.14 as build
WORKDIR /build
COPY . .
RUN CGO_ENABLED=0 go build -o hello-gitops cmd/main.go

FROM alpine:3.12
EXPOSE 8080
WORKDIR /app
COPY --from=build /build/hello-gitops .
CMD ["./hello-gitops"]

Code pipeline

Our pipeline use two job:

  • One for running tests, building and pushing the container on Dockerhub
  • The second one will edit the Kustomize patch to bump the expected container tag to the new Docker image and then commit these changes.
name: Go

on:
push:
branches: [ master ]

jobs:
build:
name: Build
runs-on: ubuntu-latest

steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.14

- name: Check out code
uses: actions/checkout@v2

- name: Test
run: |
CGO_ENABLED=0 go test ./...

- name: Build and push Docker image
uses: docker/build-push-action@v1.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: ${{ secrets.DOCKER_USERNAME }}/hello-gitops
tags: ${{ github.sha }}, latest

deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build

steps:
- name: Check out code
uses: actions/checkout@v2

- name: Setup Kustomize
uses: imranismail/setup-kustomize@v1
with:
kustomize-version: "3.6.1"

- name: Update Kubernetes resources
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: |
cd kustomize/base
kustomize edit set image hello-gitops=$DOCKER_USERNAME/hello-gitops:$GITHUB_SHA
cat kustomization.yaml

- name: Commit files
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -am "Bump docker tag"

- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

ArgoCD

ArgoCD must be configured to observe our Git repository. Configuration is rather straightforward and can be done in the included GUI. You need to specify the relative path of the Kustomize patch to use though.

top notch configuration GUI

GitOps magic

Note that at the end of the GitHub Actions pipeline, we don’t run any imperative command to deploy our application, we just changed our container version using Kustomize and auto-pushed these changes into our repository.

If you do any code change, the pipeline is triggered and a new Docker image is pushed, the container version is updated and ArgoCD should catch the change. Everything should be green by then.

greener than grass

Conclusion

GitOps is a powerful and intelligible way of making change to anything. I think it could be considered as the logical continuation of the “* as code” we see everywhere and the massive industry trend to move to more declarative, easier to understand models.

Upvote


user
Created by

Emmanuel S


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles