How-to guide / Operate a cluster
Bind a cluster definition to its Kubernetes cluster
kix deploy <cluster> --context <ctx> applies the build to whatever API
server the context points at. Two commands run one after the other, or a
default context that still points at the last cluster you worked on, are
enough to deploy an example or test definition onto a live cluster.
Binding a definition closes that gap. The definition names the Kubernetes cluster it belongs to, and Kix refuses to apply it anywhere else. Bind every definition that deploys to a live cluster.
Find the Kubernetes cluster’s ID
Section titled “Find the Kubernetes cluster’s ID”Kix identifies a Kubernetes cluster by the UID of its kube-system Namespace.
That Namespace is created when the cluster is bootstrapped and cannot be
deleted, so its UID stays the same for the life of the cluster, and every
cluster has its own: a kind cluster, a k3s cluster, an EKS cluster, and a
vCluster all differ.
kix status prints it in its header:
1$ kix status production --context prod2Kubernetes cluster ID: 4d7b1c2e-9f3a-4b8e-a1c5-6d2e8f0a9b3c3...kubectl gives the same value:
1$ kubectl --context prod get namespace kube-system -o jsonpath='{.metadata.uid}'24d7b1c2e-9f3a-4b8e-a1c5-6d2e8f0a9b3cThe UID is known as soon as the cluster exists, so you can bind a definition before its first deploy.
Name it in the definition
Section titled “Name it in the definition”Set cluster.kubernetes.uid in a cluster module:
1{ kix, packages }:2kix.buildCluster {3 name = "production";4 modules = [5 kix.flavors.gke6 {7 cluster.kubernetes.uid = "4d7b1c2e-9f3a-4b8e-a1c5-6d2e8f0a9b3c";8 # ...9 }10 ];11}The value must be a lowercase UUID; anything else fails evaluation. Several Kix clusters on one Kubernetes cluster all set the same UID.
What happens after
Section titled “What happens after”The build carries the UID, and the Activation record of each deploy of a
bound build carries it as the label kix.run/kubernetes-uid.
Every command that applies a build of the definition checks the binding after
it connects and before it reads or writes anything else: kix deploy in all
its modes (including --plan, --bundle, --current-activation, and
--operator), kix plan, kix gc (soft and --hard), and the operator when
it picks up a Queued record.
- The definition names another Kubernetes cluster. The command stops and names both IDs and the context. Nothing is applied.
- The definition names none, but a bound definition has deployed here. Once an Activation record that carries the label has finished a deploy on the Kubernetes cluster, that cluster accepts only definitions that name it. A record whose deploy never finished, such as one the operator refused because its build names another Kubernetes cluster, does not lock it. The command stops and tells you which Kix cluster bound it and which UID to add. This is what keeps an unbound example from landing on a live cluster. If the records carry a UID that is not this cluster’s (they were restored from another cluster), the message says so and names the live UID instead.
- The definition names none and nothing on the cluster is bound. The command runs as before.
kix rollback applies a build this Kix cluster deployed here before, so it
refuses only a target that names another Kubernetes cluster. A target built
before the definition set cluster.kubernetes.uid rolls back as usual.
Read-only commands such as kix diff and kix status do not check the
binding. The operator marks a refused record Failed with the message and
emits a Warning event on it.
The check reads the kube-system Namespace. An account that may not get
namespaces stops with a message that names that permission. The operator
leaves such a record Queued, emits a Warning event, and retries once its
service account is granted get on namespaces.
Check the Kubernetes version too
Section titled “Check the Kubernetes version too”A definition may also set k8sVersion, the Kubernetes version the build
checks every package’s meta.k8s.minVersion and maxVersion against. The
build carries the value, and kix deploy, kix plan, and the operator
compare it with the version the API server reports, right after the binding
check and before anything is read or written. The comparison is as precise as
you write the value:
k8sVersion = "1.31" matches any 1.31.x, and "1.31.4" matches only
1.31.4. A vendor suffix such as +k3s1 or -gke.1209000 on either side is
ignored. Write the minor version. A value with a patch number stops the
deploy after every patch upgrade, and managed clusters (GKE, EKS, AKS) apply
those on their own.
kix status prints the live version under the cluster ID:
1$ kix status production --context prod2Kubernetes cluster ID: 4d7b1c2e-9f3a-4b8e-a1c5-6d2e8f0a9b3c3Kubernetes version: v1.31.4+k3s14...When the cluster runs another version, kix deploy and kix plan stop, and
the operator marks a Queued record Failed with a Warning event. The message
names both versions and the value to set, written as precisely as the
definition writes it:
1Kubernetes version mismatch for cluster 'production' on context prod (https://prod.example:6443)2 Nothing was applied to the cluster.3
4 `k8sVersion` in 'production': 1.305 Live cluster version: v1.31.4+k3s16
7 Why:8 Package version checks (`meta.k8s.minVersion` and `maxVersion`) ran against Kubernetes 1.30 when 'production' was built, so some packages in this build may not work on v1.31.4+k3s1.9
10 Next:11 - If context prod (https://prod.example:6443) is the right cluster, set `k8sVersion = "1.31";` in 'production's definition and build again (if a Kubernetes upgrade is in progress, make this change after the upgrade finishes).12 - If Kix is connected to the wrong cluster, pass the right `--context` for 'production' and run the command again.After a Kubernetes upgrade, update k8sVersion, build again, and deploy. The
new build runs the package checks against the version the cluster now runs.
A consumer cluster built with kix.interface gets k8sVersion from the
platform’s interface file unless its own definition sets one. The build
records where the value came from, and the message then names the platform
and gives both fixes: export a new interface file from the platform after its
upgrade and build against it, or set k8sVersion in the consumer’s definition,
which overrides the file.
Two things go ahead on a mismatch, so that a Kubernetes upgrade never blocks
them: kix rollback prints one line saying the target build was made for
another version and rolls back anyway, and the operator keeps repairing an
Active record. kix diff does not check. A definition that leaves
k8sVersion unset is not compared, and nothing is printed.
Move a definition to a rebuilt cluster
Section titled “Move a definition to a rebuilt cluster”When a Kubernetes cluster is recreated, its kube-system UID changes. Read
the new ID as above and update cluster.kubernetes.uid in the definition. The
commit records the move.
Lift a binding
Section titled “Lift a binding”The lock on a Kubernetes cluster lives in the kix.run/kubernetes-uid label
of its Activation records, so it outlives the Kix cluster that set it. When a
bound Kix cluster is removed and its records stay behind, an unbound
definition is still refused. To let unbound definitions deploy again, remove
the label from every record that carries it:
1kubectl label activations.kix.run -l kix.run/kubernetes-uid kix.run/kubernetes-uid-Deleting the dead Kix cluster’s records also lifts the lock. Do not delete an
Active or Degraded record while the operator runs: its finalizer tears
down everything that record deployed.
A later deploy of a bound definition writes the label again and locks the cluster again.