The operator and StyrminDeployment
The operator and StyrminDeployment
Styrmin uses two Kubernetes mechanisms that you see when you inspect a cluster: custom resources and operators. You do not need to know them to use Styrmin.
Custom resources and operators
Kubernetes stores the objects that should exist, and controllers create them. You apply a YAML file that declares a Deployment with three pods, and the built-in Deployment controller creates the pods.
A custom resource is an object of a type that you define, not a built-in Kubernetes type.
An operator is the controller for a custom resource type. It watches the objects of that type and changes the cluster to match them.
The StyrminDeployment resource
Styrmin defines one custom resource type, StyrminDeployment. The
cluster has one StyrminDeployment object for each deployment that you
create in Styrmin.
A StyrminDeployment contains:
- the driver and the driver version that the deployment uses;
- the application version it runs;
- the fully resolved context: every value that the driver needs to render its Helm chart (see The context for the full structure);
- the lifecycle phase it should be in (running, stopped, deleting).
It describes the deployment in a form that Kubernetes can read.
What the operator does with it
The operator runs inside the agent (see Architecture).
It watches every StyrminDeployment in the cluster and runs these
phases:
- Render: turn the context into Kubernetes resources: a
HelmReleasefor the application's chart, services, network policies, and secrets. - Apply: write those resources into the cluster. The built-in
Kubernetes controllers, and FluxCD for the
HelmRelease, then create the objects they describe. - Wait: wait until the resources are ready. Mark the
StyrminDeploymentas ready when FluxCD reports the new release ready, or as failed when FluxCD stops retrying (see When a rollout is ready or failed). - Check periodically: compare the cluster with the declared state again. When something differs (a pod was deleted, a ConfigMap was edited), restore it.
When the StyrminDeployment changes, because the server recorded a
change to the deployment (see Reconciliation), the
operator runs the phases again.
Why Styrmin uses a custom resource
The server does not call the Kubernetes API to deploy an application. It goes through a custom resource, for two reasons:
- The cluster stores the desired state. When the agent restarts, or
the server stops, the
StyrminDeploymentstays in the cluster. The operator continues from it. - Kubernetes tools can read it.
kubectl, GitOps tools, and monitoring tools that read custom resources show Styrmin deployments without a Styrmin integration.
The HelmRelease
Each deployment also has a second custom resource, a HelmRelease.
Its type comes from FluxCD, an open-source toolkit
that the agent chart installs, not from Styrmin.
A HelmRelease declares a Helm chart, its version, and its values.
FluxCD's operator, the helm-controller, watches HelmRelease objects
and runs helm install and helm upgrade for them. Styrmin does not run
Helm itself.
The agent chart installs FluxCD's helm-controller v1.6.5 and source-controller v1.9.6, the controllers of Flux v2.9.6. They support Kubernetes 1.34.1 and later. helm-controller runs with the defaults of Helm v4:
- A deployment created since the agent runs helm-controller with these
defaults is installed with server-side apply. helm-controller is the
field manager and forces field ownership conflicts, so at each upgrade
a field that the chart sets returns to the chart's value, even when
kubectlor a driver action changed it. The upgrade does not fail. - A deployment created before, when helm-controller ran with the
UseHelm3Defaultsfeature gate, keeps client-side apply: an upgrade uses the apply method recorded in the release's newest revision, and a rollback uses the method of the revision it returns to. From its next upgrade, the two items below apply to it too. - Installs and upgrades wait for the chart's resources with kstatus,
FluxCD's library that computes the status of each Kubernetes resource.
The wait ends before the HelmRelease timeout when kstatus reports a
resource as failed, for example a
Jobthat failed or aDeploymentpast itsprogressDeadlineSeconds. ADeploymentwhose pods cannot pull their image, or whose container exits immediately, is not failed for kstatus, so FluxCD waits the full timeout. - Helm hooks of the driver's chart, the resources annotated
helm.sh/hook, receive Styrmin's post-render patches, as the chart's other resources do: Styrmin's labels, the deployment's scheduling settings and, on aDeploymentorStatefulSetthat matches a component, the component overrides. See Step 1 of Create a driver for which patches reach which hooks.
To go back to Helm v3's behavior, downgrade the agent chart to a
release that runs helm-controller with the UseHelm3Defaults gate.
Deployments created while the gate was off keep server-side apply on
their later upgrades and rollbacks, because they keep the apply method
of their previous release.
Helm installs the CRDs in a chart's crds/ directory only when they do
not exist yet, and never updates them. The agent chart therefore applies
its CRDs (FluxCD's, kopf's and the StyrminDeployment CRD) in a Job
before each helm install and helm upgrade. When a new CRD no longer
lists a version that the cluster stored objects in, the Job deletes and
creates that CRD again if it has no objects, and fails the install or
upgrade if it has some. To apply the CRDs yourself, set
styrmin-agent.crdUpgrade.enabled to false and apply the files of
helm/agent/crds/ before each upgrade.
Inside the cluster, the work passes through these components:
StyrminDeployment ──► Styrmin operator ──► HelmRelease ──► FluxCD helm-controller ──► Helm install / upgrade
(Styrmin CRD) (in the agent) (FluxCD CRD) (separate pod)
Each operator handles different work:
- Styrmin's operator handles the driver, the version, the resolved context, the ingress of the environment, and when to run lifecycle actions.
- FluxCD's helm-controller handles Helm: it renders the chart, templates the values, orders the install and upgrade steps, and rolls back a failed release.
To list both:
kubectl get styrmindeployments -A
kubectl get helmreleases -A
In short, FluxCD creates the application's resources from the
HelmRelease, and Styrmin creates and updates the HelmRelease from your
deployment.
When a rollout is ready or failed
Each time the operator starts a rollout, for a new StyrminDeployment or
a changed spec, it:
- sets the
HelmReleaseReadycondition toUnknownwith the reasonProgressing, so that theTruevalue of the previous rollout does not count for the new one; - removes the
HelmReleaseRemediatedcondition of the previous rollout; - records the start of the rollout in
status.rolloutStartedAt; - writes the application instance it is rolling out to
status.instanceId, even when the handler fails. The status reporter sends this value to the server (see Status reporting).
While the phase is CreatingHelmRelease, the operator reads the
HelmRelease once per timer run (every 60 seconds by default). It uses only
FluxCD's conditions for the HelmRelease's current generation: a condition
counts when its observedGeneration, or the HelmRelease's
status.lastAttemptedGeneration when the condition has none, equals the
HelmRelease's metadata.generation. A Ready=True left from the previous
release therefore does not make the new release ready.
- When FluxCD reports the current generation
Ready=True, the operator setsHelmReleaseReadytoTruewith the reasonFluxReady, creates the services and moves theStyrminDeploymenttoReady. - Until then,
HelmReleaseReadycopies the status and transition time of FluxCD'sReadycondition. WhenReadyisFalse, the reason and message come from FluxCD'sReleased=Falsecondition when there is one, which names the failed install or upgrade, and fromReadyotherwise. - When FluxCD's
Remediatedcondition isTruefor the current generation, FluxCD has rolled back or uninstalled the failed release. The operator copies that condition intoHelmReleaseRemediated. This is not a final failure: Styrmin's HelmReleases allow FluxCD 3 retries for an install or an upgrade, and a later attempt can succeed. - When FluxCD's
Stalledcondition isTruefor the current generation, FluxCD has used all its retries. The operator sets the phaseFailedand the display stateError. The message of theStyrminDeploymentis the message of FluxCD'sStalledcondition followed by the failure message, in the form<Stalled message>: <failure message>. TheReadycondition becomesFalsewith the reason of FluxCD'sStalledcondition, for exampleRetriesExceeded.
The operator writes a condition only when its content changed. An internal
deployment, such as the per-environment ingress controller, also counts as
ready as soon as one of its pods is ready, even when FluxCD reports
Stalled.
Styrmin's HelmReleases give FluxCD
STYRMIN_OPERATOR_HELM_RELEASE_TIMEOUT seconds (300, that is 5 minutes,
by default) for each install or upgrade. At that timeout FluxCD fails the
attempt and rolls the release back, which removes its pods.
The readiness timeout applies when FluxCD never reports Stalled. When a rollout has not reached Ready or Failed within
STYRMIN_OPERATOR_DEPLOYMENT_READY_TIMEOUT seconds (270 by default), the
operator sets the display state Error with the message "Deployment did
not reach Ready within" followed by the timeout and the phase. The phase
does not change, so the rollout can still become Ready later. Keep the
readiness timeout below the HelmRelease timeout, as the defaults are: a
failed rollout then reports the pods that are not ready and why before
FluxCD's rollback removes them. This holds while FluxCD waits the full
HelmRelease timeout, which it does for pods that cannot pull their image
(see the kstatus item in
The HelmRelease). The
timeout counts from status.rolloutStartedAt, or from the creation time of the
StyrminDeployment when that field is missing. When the operator restarts and resumes a
rollout, it keeps the recorded start, so a restart does not extend the
timeout. When a step of the rollout fails with a temporary error, for
example when the operator cannot create a network policy, kopf runs the
create or update handler again STYRMIN_OPERATOR_RETRY_DELAY seconds
(30 by default) later, and the handler records a new start.
FluxCD can also report the release Ready after it rolled back the
failed upgrade. This happens when a change only adds or changes
post-render overrides, such as an image override
or environment variables: after the rollback, FluxCD compares only the
chart and the values with the HelmRelease, finds the rolled-back release
in sync, and reports it as a successful upgrade, although the override
was never applied. The operator recognizes this state from FluxCD's
release history: FluxCD counted a failed install or upgrade for the
current generation of the HelmRelease, and the newest release is
deployed right after a failed one, so it is the rollback. The
operator then sets the phase Failed and the display state Error,
with the message
FluxCD rolled the release back after release v<N> failed followed by
the failure message when it recorded one, and the
Ready condition becomes False with the reason RolledBack. The
HelmRelease step and the task stay failed. A retry by FluxCD that
succeeds creates a newer release after the rollback, so the deployment
still reaches Ready.
FluxCD can also report a change in sync after it failed to apply it.
When an install or upgrade fails before Helm stores a release, for
example because a post-render patch fails, helm-controller v1.6.5 still
records the new post-renderers as applied. On its next run it compares
only the chart and the values, reports the previous release Ready, and
does not try again. The operator recognizes this state: FluxCD counted a
failure for the current generation of the HelmRelease
(status.failures), and FluxCD's newest InstallFailed or
UpgradeFailed event of the HelmRelease is newer than its newest
release. The operator then sets the phase Failed and the display state
Error. When the event names a component container that the chart does
not render, the message is the COMPONENT_CONTAINER_NOT_FOUND message
(see Image overrides). Otherwise it is
FluxCD reported the release in sync after the change failed: followed
by FluxCD's message, and the Ready condition becomes False with the
reason of the event.
A StyrminDeployment that keeps its finalizer
The agents of Styrmin v0.1.48 and earlier can keep the
styrmin.io/kopf-finalizer finalizer of a StyrminDeployment that is
being deleted (#446,
#461). This happens
when the deployment's namespace is deleted while the operator's timer
is checking that StyrminDeployment. Kubernetes does not delete the
namespace while the custom resource in it keeps a finalizer, so the
namespace stays Terminating.
You see one of these symptoms:
- A restore fails in its "Delete the namespace" step with
Namespace <namespace> was not deleted within 600s. - An application deployment stays
DELETING. - A dedicated ingress controller stays
DELETING. See A controller that staysDELETINGfor the steps that follow the recovery.
To read the version of the agent that runs on a cluster, list the image of the agent pod. The image tag is the Styrmin version:
kubectl get pods -A -l app.kubernetes.io/name=styrmin-agent \
-o jsonpath='{range .items[*]}{.spec.containers[0].image}{"\n"}{end}'
Check whether the finalizer or slow pods block the namespace
A namespace also stays Terminating while its pods stop, and a pod can
need longer than 600 seconds. The
Neo4j Helm chart,
for example, gives the Neo4j pod up to 3600 seconds to stop. List the
pods and the finalizers of the StyrminDeployment in the namespace:
kubectl get pods -n <namespace>
kubectl get styrmindeployments -n <namespace> \
-o jsonpath='{range .items[*]}{.metadata.name}{": "}{.metadata.finalizers}{"\n"}{end}'
- No pods are left, and the
StyrminDeploymentstill listsstyrmin.io/kopf-finalizer: the operator kept the finalizer. Follow the recovery steps below. - Pods are still
Terminating: Kubernetes deletes the namespace once they stop.
In both cases, a restore that failed with Namespace <namespace> was not deleted within 600s does not continue. Start it again after the
namespace is deleted.
Remove the finalizer
-
Upgrade the agent to a Styrmin release later than v0.1.48. When the new agent starts, it removes the finalizer that the earlier agent kept, and Kubernetes finishes deleting the namespace. You do not need to add an annotation.
-
Until you upgrade, add any annotation to the
StyrminDeployment. The operator then checks the custom resource again and removes the finalizer:kubectl annotate styrmindeployments.styrmin.io <name> -n <namespace> \
styrmin.io/retry-delete="$(date +%s)" --overwrite
When an agent later than v0.1.48 keeps the finalizer
The agent keeps the finalizer only until the timer's current check of
the StyrminDeployment ends, which normally takes a few seconds. The
agent log then shows this line once:
StyrminDeployment <namespace>/<name> keeps its finalizer until its timer run ends
If the finalizer stays for more than a few minutes, the timer's check does not end, for example because a request to the Kubernetes API does not return. Restart the agent: the new agent removes the finalizer when it starts. Restarting the agent also stops the tasks it runs at that moment, such as a backup or a restore. Then open an issue and attach the agent log.
What this means in practice
- Styrmin's operator reconciles
StyrminDeploymentobjects. To debug a deployment, start with itsStyrminDeployment. - FluxCD reconciles
HelmReleaseobjects. If a chart fails to install, FluxCD's reason is copied into theStyrminDeployment'sHelmReleaseReadycondition, and the deployment page shows it on the step "Install or upgrade the HelmRelease". - Because the operator checks the cluster periodically, a pod that you delete is created again.
Next steps
- Reconciliation: when the server updates the
StyrminDeployment. - Status reporting: how the agent reports the cluster state to the server.