Migrating assets between Flux Kustomizations
Prerequisites for following along: the
kubectl
tool, thefluxcli
tool, a Kubernetes cluster with FluxCD installed, and at least 2 Kustomizations.
A foreword on GitOps - a portmanteau of the words Git, the version control tool, and Operations - which describes a design paradigm by which a system is managed by looking at a Git repository as a source of truth. To make changes in the system, this is first reflected in the git repository, before being applied by a tool.
Some reasons for choosing this as a deployment mechanism are:
- a single source of truth means it’s consistent and reproducible
- eliminates configuration drift: if someone changes something manually, the operator service puts it back to exactly how it is in git next time it runs
- version history: you can look back through the commits and see how something has evolved over time
- rollback: equally, if something goes wrong, previous commits contain all the config to which you can simply roll back
One such tool, FluxCD, orFlux
, is an open-source tool that continually checks and updates Kubernetes resources against a provided configuration in the git repository. This is referred to at the top level as aKustomization
. This could include any native Kubernetes object, such as aDeployment
orService
, or a Custom Resource such as anExternalSecret
(or any thing else really, as is the nature of Custom Resource Definitions.) A commonly deployed object is the or FluxCD Custom Resource, aHelmRelease
. This takes a YAML manifest with some details about a [Helm] chart and deploys it, which in turn creates and manages a set of Kubernetes resources, such as a simple app with a deployment and a service.
This is what our example HelmRelease would look like when applied under theapps
Kustomization:
apps # the top level Kustomization)
└── my-simple-app # a HelmRelease object
└── simple-app # a Helm deployment of a Helm Chart
├── service.yaml # the Kubernetes Service object
└── deployment.yaml # the Kubernetes Deployment object
My personal homelab setup is actually a small Kubernetes cluster (K3s, actually) which has Flux checking a GitHub repository and reconciling any changes, such as infrastructure changes, new apps, changes to images used, and more. I have multiple levels of Kustomizations:
- a
core
Kustomization containing the absolute essentials to first hit the cluster - this contains the monitoring stack Prometheus, the storage management system Longhorn, the networking system metallb to provide my cluster with some inter-node and cluster networking - all very much basic blocks that are crucial to the cluster itself being operable - an
infra[structure]
Kustomization containing some augmentation services, such ascert-manager
to provide SSL Certificates,external-dns
to manage DNS records,external-secrets
to allow the abstraction of sensitive information to a cloud service, and anoauth2-proxy
to restrict my personal-only services to be access only from inside my network. In other words, all very useful services that add a lot of functionality, but not important enough to the running of the cluster that they were put into thecore
Kustomization. This Kustomization relies on thecore
Kustomization being applied successfully in order to start applying. - finally, an
apps
Kustomization. This depends on the previous Kustomizations both being applied successfully, and applies sequentially aftercore
andinfra
. It deploys this website, for one, along with a Homer dashboard for quick access to some linked services.
Let’s say I made a mistake (I know, right?), and I want to move something so it’s controlled by a different Kustomization? Well, I could just move it from one part of the git repo to the new place, but remember the chained dependencies I’ve created, requiring thatcore
applies beforeinfra
does, andinfra
beforeapps
? If I move a service up the “chain” of Kustomizations, e.g. fromapps
toinfra
, Flux will see that everything it wants to make already exists, and it will fail to apply. If I move the service down the chain, e.g. fromcore
toinfra
, Flux will applycore
, deleting the app*, and then create it in theinfra
Kustomization.
*This only happens if the Prune option is enabled at the top level - see below for more info⌗
But what if it’s a critical or stateful service, and I absolutely can’t have it being deleted by Flux? There’s git got to be a better way… And there is! This method leverages the fact that a key label in the created resource Prune
is optional in the Kustomization, as well asthat denotes ownership.
CORRECTION: So, I had a suspicion but had to confirm this first: you can actually ignore all the Prune
commands for the purposes of moving a resource out of a Kustomization. The reason being that the crucial element in Flux’s control of a resource is the label, and we can change this as described below. As long as the label is changed before the changes are merged, Flux will cease to control that resource, and so will not remove it if/when the Kustomization is removed. Many Bothans died to bring us this information.
It is worth noting that if you don’t pause the Kustomizations, then as soon as you change the label on the resource, Flux will try to bring up another resource as it believes it no longer has a version of it. Equally, the new Kustomization will immediately detect it, but consider it to be out of config, and promptly delete it.
Just to touch on
Prune
- which is named so because having it enabled will “prune” anything that’s not in the current configuration - you don’t have to have this enabled all the time. However, leaving it off and having Flux in create-only mode means you have to clean up any old created resources by hand, which can get messy and/or time-consuming. It’s also teeechnically a violation of the GitOps principles, as it means that what’s in git is not being mirrored by what’s deployed.
I’ll walk through an example where I move an InfluxDBHelmRelease
from theapps
to theinfra
Kustomization.
1. Suspend continuous deployment⌗
So, first things first, let’s pause the process both Kustomizations that we’re working with - otherwise as soon as we make a change, Flux will change it straight back to what’s in git.
❯ flux suspend kustomization infra
► suspending kustomization infra in flux-system namespace
✔ kustomization suspended
# N.B.: ks is short for kustomization!
❯ flux suspend ks apps
► suspending kustomization apps in flux-system namespace
✔ kustomization suspended
2. Turn off Prune, if it’s enabled You can disregard this on the basis of the above corrective Edit⌗
Let’s edit the Kustomization directly, but you could also use a patch file.
❯ kubectl edit kustomization apps
...
spec:
prune: true --> false
...
3. Change ownership of the object you’re moving⌗
Let’s edit the HelmRelease directly again, altbough - again - you could use a patch file for this, and it would certainly save time if you’re doing it a lot. Although, if you were, that would raise some questions about why you’re doing it a lot, right?
Running kubectl edit helmrelease influxdb
opens the server-side version of this object and displays the metadata and specification. We’re interested in the first label we can see here, kustomize.toolkit.fluxcd.io/name: apps
.
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
generation: 6
labels:
kustomize.toolkit.fluxcd.io/name: apps
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: influxdb
namespace: assistant
I can just change this from apps
to infra
, like this:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
generation: 6
labels:
kustomize.toolkit.fluxcd.io/name: infra
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: influxdb
namespace: assistant
Now I’ll save and close this editor and head back to the PR to merge my changes.
4. Make the necessary changes in git⌗
If you made a merge request or pull request, well done! You’re better than most of us. Everyone else can just commit straight to prod
branch like the degenerates we are. Either way, as long as it’s in git and Flux has the latest code, it doesn’t matter. As long as the deployment controller is paused, this won’t go to the cluster until we manually resume things.
5.Resume the Kustomizations⌗
Now that we’ve done the the tweaks to the resource to change the Kustomization that owns the HelmRelease (according to the label), we can resume these without fear that anything will break.
❯ flux resume ks apps
► resuming kustomization apps in flux-system namespace
✔ kustomization resumed
◎ waiting for Kustomization reconciliation
✔ Kustomization apps reconciliation completed
✔ applied revision main@sha1:be6960451c8f928f68ab1b8c8c09e6ac6cdd0a48
...
► resuming kustomization infra in flux-system namespace
✔ kustomization resumed
◎ waiting for Kustomization reconciliation
✔ Kustomization infra reconciliation completed
✔ applied revision main@sha1:be6960451c8f928f68ab1b8c8c09e6ac6cdd0a48
6. Watch as everything works flawlessly⌗
That’s literally it! Congrats, you’ve moved a resource between Kustomizations.
7. Turn Prune back on⌗
If you turned Prune
off in the git repo, don’t forget to turn it back on so that your cluster is perfectly GitOps-y. Otherwise, it’ll set itself back next time it reconciles.