FluxCD: Continuous Deployment with GitOps

This post explains why I land on FluxCD GitOps for my project. Let’s star

Background

In the Korthweb project, I landed on Istio for the Ingress Gateway technology. I first attempted to expand the orthanc Helm Chart to bring Istio as dependency (sub-chart). One of the external chart for Istio gateway needs to be referenced multiple times (for ingress and egress). However, it cannot even be used as dependency (sub-chart) because of this issue. Istio didn’t re-introduce Helm Chart as a supported deployment until September 2021. So I’m not too confident about it.

This leads to a second thought of the “Big Helm Chart” approach to deploy all tiers in Korthweb. Helm is based on templating, and having two layers of charts brings complexity. I do need to bring a number of Helm charts together, but not necessarily by another Helm Chart.

The Kubernetes documentation also mentions Kustomize as an alternative to Helm. Below is a quick exploration of it.

Kustomize

A challenge with managing declarative object is to organize numerous manifest files to maintain consistency and readability. Kustomize is a standalone tool to customize Kubernetes objects through a Kustomization file. This page provides a good list of fields that can be used:

  • vars and replacements: copy fields from one source into any number of specified targets.
  • namePrefix, namespace, nameSuffix: customize namespace and names.
  • configMapGenerator, secretGenerator, and generatorOptions: create configuration entries from literal, files, or environment variables.
  • resources: indicates another kustomization directory (e.g. as base)
  • patches (also called overlays) add or override fields on resources.
  • patchesStrategicMerge: modifies values in known (loaded) resources
  • images: modifies the name, tags and/or digest for images, without creating patches.
  • openapi: use Kubernetes OpenAPI data to get merge key and patch strategy information about resource types.

The base -> overlay pattern ensures readability as well as portability. Typical use pattern is create one base kustomization, and several overlay kustomizations each representing an environment, such as dev, qa and production. Here is a good tutorial.

Helm and Kustomize are two approaches to deploy Kubernetes workload. Both tackle the challenge of managing many YAML declarations. Helm is template driven, and is commonly used by application developers as a means of packaging application releases while orchestrating the dependencies. Kustomize follows a base-overlay pattern, and is more commonly used by cluster operators for re-using manifests across multiple environments (e.g. dev, staging and prod).

In my use case, my deployment needs both. I need third party Helm chart to configure components such as PostgreSQL for HA. I also need Kustomize for the orthanc workload. With the need for both, there should be a higher level deployment technology that ingrate with both mechanisms.

GitOps

GitOps is originally brought up by Weaveworks in 2017. It is a methodology to deploy workload continuously, using a Git repository as source of truth. The point of GitOps is not about tooling, or specific platform, but rather to ensure the workload state matches the declaration in repository. For example, you can implement GitOps with Ansible for VM environment.

When it comes to managing Kubernetes workload, a GitOps tool must handle the challenge with managing many YAML declarations. Therefore most GitOps tools seek to support many well-adopted mechanisms such as Helm and Kustomize as discussed above, instead of simply taking an enormous amount of raw YAML declarations. The amount of deployment mechanisms supported, is a key indicator of how powerful a GitOps tool is.

The most notable tools are ArgoCD and FluxCD. Both are currently CNCF incubating projects. ArgoCD is powerful with many tools supported, such as Kustomize, Helm, Ksonnet, Jsonnet, etc. It also contains a user interface and aims to manage an entire deployment workflow. On the other hand, FluxCD has controllers mainly for Kustomize and Helm, with a jsonnet extension from third party. This post draws a comparison of them (along with JenkinsX) based on earlier versions (from mid 2020). There is even a standard (OpenGitOps) in CNCF landscape but still at Sandbox level.

For Korthweb project, I chose FluxCD. It is simple, and provides just enough types of controller for what I do.

FluxCD

The diagram below illustrates the components:

Git Repository
Git Rep…
flux-system
flux-s…
Kustomize:
infrastructure
Kustom…
Kustomize:
dependency
Kustom…
Kustomize:
application
Kustom…
Kustomize:
dev
Kustom…
flux-system
namespace
flux-sys…
application
namespace
dev
applicat…
flux-system
custom resource
controllers
flux-syst…
reconcile
reconcile
Text is not SVG – cannot display

The configurations starts with a bootstrapping process, which creates directory in Git repository (if not exist), and installs flux-system components in the target Kubernetes cluster. The sync process starts as soon as bootstrapping is completed. The process in charge of syncing declarations to target cluster, confusingly, is also called Kustomization. Therefore there are two Kustomizations. According to the FAQ on FluxCD website:

There are two Kustomization types. the kustomization.kustomize.toolkit.fluxcd.io is a Kubernetes custom resource while kustomization.kustomize.config.k8s.io is the type used to configure a Kustomize overlay. The kustomization.kustomize.toolkit.fluxcd.io object refers to a kustomization.yaml file path inside a Git repository or Bucket source.

Inside of the Git repository, with a kustomization.kustomize.toolkit.fluxcd.io obejct, the flux-system points to Kustomization file (representing kustomization.kustomize.config.k8s.io object) at root level. The kustomization.yaml file organizes resources in the same directory. A kustomize directory may also reference other kustomize directory, forming a hierarchy.

Implementation

FluxCD has a command “flux check –pre” to check the prerequisite, such as kubectl. The code is stored in the GitOps directory of Korthweb repository.

To configure deployment, we need to first create a personal access token. For GitHub, here is the instruction. Export the token to environment variable, and launch bootstrapping:

$ export GITHUB_TOKEN=xxx_yyy55555XXXodr7ABBBB234CCccw
$ flux bootstrap github \
      --owner=digihunch \
      --repository=korthweb \
      --branch=main \
      --personal \
      --path=gitops/environment/dev

A deploy key is configured during the bootstrapping process. As soon as bootstrapping is completed, the sync (aka kustomization, or reconciliation) has started, which can be monitored using:

flux get kustomizations --watch

Running this command without –watch switch returns the overview of all kustomizations. Once reconciliation is completed, it should display something like:

NAME          	READY	MESSAGE                                                        	REVISION                                     	SUSPENDED
application   	True 	Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	False
dependency    	True 	Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	False
flux-system   	True 	Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	False
infrastructure	True 	Applied revision: main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	main/98f3c771d4ab23f5cb5fd8c7aee325f6490000c7	False

FluxCD introduced a number of CRDS. For example, to check configured source repository, check gitrepositories CRD:

kubectl get gitrepositories -n flux-system

To check the detail of one kustomization (e.g. infrastructure), check kusomization CRD in flux-system namespace:

kubectl -n flux-system get kustomizations flux-system -o yaml | less

Other commonly used custom resources include:

  • helmcharts
  • helmreleases
  • helmrepositories

A Helm release can be created imperatively, for example:

$ flux create source helm istio \
     --interval=1h \
     --url=https://istio-release.storage.googleapis.com/charts
$ flux create helmrelease istio-base \
     --interval=1h \
     --release-name=istio-base \
     --target-namespace=istio-system \
     --create-target-namespace=true \
     --source=HelmRepository/istio \
     --chart=base \
     --chart-version="1.12.0" 


$ flux create helmrelease istiod \
     --interval=1h \
     --release-name=istiod \
     --target-namespace=istio-system \
     --source=HelmRepository/istio \
     --chart=istiod \
     --chart-version="1.12.0" \
     --values=istiod-values.yaml 

However, in a GitOps approach, they should be stored as code (use –export to export declaration). For example, the infrastructure kustomization keeps HelmReleases for installing Istio and PostgreSQL. This kustomization is referenced by a Flux Kustomization from higher level.

You can also manually reconcile one of the kustomizations (or other CRDs) with flux reconcile command, for example:

flux reconcile kustomization dependency

Since Nov 2021, FluxCD (0.20) supports reconciliation based on server-side apply. This increases performance and help address issues such as applying large config map, which is the equivalent of adding –server-side flag to the kubectl apply command.

Limitation

Troubleshooting the FluxCD repo can be involving and counter-intuitive. I had to commit a lot of changes to the repo because it serves as source of truth. Even though the commits can be made to a branch, it still involves a lot of code pushes. Traditionally I commit a change after testing. In the GitOps workflow, I commit a change then to test.

More flexible troubleshooting options are still to be desired. For example, there is no way to run one (FluxCD’s) Kustomization object at a time (and disable the rest), unless you remove their YAML files from the repo.

The next limitation is the ordering of resources in Kustomization. Arguably this is a limitation from Kustomize, instead of FluxCD’s. For example, I need the following manifest to be executed:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}

The resource is a CRD that needs to be first installed using Helm chart (cert-manager). However there’s no way to control the sequence (Helm release executed first before declaration using CRD). Although there’s some workaround, it is not convenient. Alternatively, we can separate the resource creation and CRD creation into separate kustomization objects with dependency relationship, as suggested in this example.

Summary

Deployment in Kubernetes can get complicated with a lot of manifests. Helm, Kustomize and the like are means to handle the complexity due to numerous manifests. GitOps tools such as FlexCD brings these tools under a single framework, and more importantly, implements the idea of using Git repository as source of truth for continuous deployment.