From GitOps to Continuous Delivery: Using FluxCD to Automate Kubernetes Deployments

Introduction

In the previous post, we’ve introduced the concept of GitOps and looked at how to use Flux to manage your Kubernetes cluster using GitOps. If you are new to GitOps and Flux, I recommend that you read the previous post before continuing with this one.

In this post, we’ll be looking at how to use FluxCD to perform continuous delivery in your Kubernetes cluster.

Prerequisites

To follow along with this post, you’ll need:

  • A Kubernetes cluster. You can use kind to create a local cluster for testing.
  • kubectl installed and configured to connect to your cluster.
  • Flux CLI installed.

Bootstrapping Flux

We will use the same repository that we created in the previous post, but we will bootstrap Flux with a different set of components. We will be using the --components-extra flag to add the image-reflector-controller and image-automation-controller components to the default set of components. These components will allow us to automate the deployment of our workloads.

  • image-reflector-controller scans image repositories and reflects the image metadata in Kubernetes resources.
  • image-automation-controller updates YAML files based on the latest images scanned, and commits the changes to a given Git repository.

image-update-automation

If you use bootstrap command more than once in the same cluster, Flux will overwrite the existing manifests in the repository with the new ones.

flux bootstrap github \
  --components-extra=image-reflector-controller,image-automation-controller \
  --owner=$GITHUB_USER \
  --repository=intro-to-gitops-with-flux-demo \
  --branch=master \
  --path=./clusters/my-cluster \
  --read-write-key \ # This flag is required for the write access to the repository.
  --personal

flux-bootstrap

After bootstrap is complete, Flux will update our manifests in the repository by adding the image-reflector-controller and image-automation-controller components.

If you bootstrap Flux for the first time, you will also see the deployment and service for the blog application that we created in the previous post.

k get svc,deploy

Creating Manifests for Image Update Automation

As we’ve seen in the diagram above, Flux will need a few resources to perform image update automation. These resources are:

  • image-repository resource defines the container registry to scan.
  • image-policy resource defines semver range to use when filtering tags.
  • image-update-automation resource defines the required configuration to commit the changes to the Git repository and perform image updates.
  • git-repository resource defines the Git repository to commit the changes. (This resource is created by Flux during bootstrap.)

We will use flux create command to create these resources instead of creating them manually. We will also use the --export flag to export the manifests to a file instead of applying them to the cluster.

flux create image repository blog-image-repository \
  --image=ghcr.io/mharikmert/blog \
  --interval=1m \
  --export > ./clusters/my-cluster/blog/image-repository.yaml
flux create image policy blog-image-policy \
  --image-ref=blog-image-repository \
  --select-semver="0.0.x" \
  --export > ./clusters/my-cluster/blog/image-policy.yaml

flux create image update automation image-update-automation \
  --git-repo-ref=flux-system \
  --git-repo-path="./clusters/my-cluster" \
  --checkout-branch=master \
  --push-branch=master \
  --author-name=fluxcdbot \
  --author-email=[email protected] \
  --commit-template="{{range .Updated.Images}}{{println .}}{{end}}" \
  --export > ./clusters/my-cluster/flux-system/image-update-automation.yaml

flux-create-manifests

After creating the manifests, the folder structure should look like this:

├── clusters
│   └── my-cluster
│       ├── blog
│       │   ├── blog-image-repository.yaml
│       │   ├── blog-image-policy.yaml
│       │   └── blog.yaml
│       └── flux-system
│           ├── gotk-components.yaml
│           ├── gotk-sync.yaml
│           ├── kustomization.yaml
│           └── image-update-automation.yaml
└── README.md

Since we are using the same folder for the image update automation with flux components, we need to add the image-update-automation.yaml to the kustomization.yaml file as a resource. If we used the same folder with the blog application, we wouldn’t need to do this. We do so because we will be using the same image-update-automation for other applications as well.

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - gotk-components.yaml
  - gotk-sync.yaml
  - image-update-automation.yaml

Now, our manifests are ready to be applied to the cluster. As the last step, we will add an image policy marker to the deployment manifest of our blog application. This marker will be used by the image-automation-controller to update the image tag of the deployment.

Image policy markers are used to refer to an image policy resource. There are three types of markers:

  • {"$imagepolicy": "<policy-namespace>:<policy-name>"}
  • {"$imagepolicy": "<policy-namespace>:<policy-name>:tag"}
  • {"$imagepolicy": "<policy-namespace>:<policy-name>:name"}

These markers are placed inline in the target YAML, as a comment. The “Setter” strategy refers to kyaml setters which Flux can find and replace during reconciliation, when directed to do so by an image-update-automation.

We will use the first type of marker in our deployment manifest.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog-deploy
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
        - name: blog
          image: ghcr.io/mharikmert/blog:v0.0.15 # {"$imagepolicy": "flux-system:blog-image-policy"}
---
apiVersion: v1
kind: Service
metadata:
  name: blog-svc
  namespace: default
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: blog

Committing the Manifests

Now, we are ready to commit the manifests and push them to the Git repository. To review our changes,

  • Created blog-image-repository.yaml and blog-image-policy.yaml files under clusters/my-cluster/blog folder.
  • Created image-update-automation.yaml file under clusters/my-cluster/flux-system folder.
  • Added image-update-automation.yaml file to the kustomization.yaml file under clusters/my-cluster/flux-system folder.
  • Added an image policy marker to the deployment manifest blog.yaml under clusters/my-cluster/blog folder.
git add .
git commit -m "Add image repository & image policy & image update automation"
git push

Reconciling the Manifests

After pushing the manifests to the Git repository, Flux will reconcile them and apply to the cluster. If everything goes well, we should see the following resources created in the cluster.

kubectl get imagerepositories,imagepolicies,imageupdateautomations -n flux-system

k-get-resources-created

We can see the image tags of the image repository scanned by the image-reflector-controller in the imagerepository resource.

If our setup for the continuous delivery works correctly, image-automation-controller will update the image tag of the deployment to the latest version depending on the policy we defined.

kubectl get deploy -o wide --watch

reconcile

Conclusion

In this post, we’ve seen how to set up a GitOps continuos delivery workflow with FluxCD and automate deployments. GitOps is a powerful approach to manage and automate Kubernetes deployments. By using a tool like FluxCD, it becomes even easier to implement GitOps and achieve continuous delivery. Overall, GitOps and FluxCD are powerful tools that can help you streamline your deployment process and improve the reliability of your Kubernetes deployments.

You can also check out my continuous delivery workflow that runs for multiple applications in multiple subdomains of mharikmert.dev in the same cluster here.

Resources

Further Reading