Reconciliation

Reconciliation is, in plain words, bringing the actual state of a system to a desired state as expressed by the Kubernetes resources. For example, starting as many pods, as it is declared in a deployment, especially when this declaration changes due to resource updates.

Kopf is not an operator, it is a framework to make operators. Therefore, it knows nothing about the desired state or actual state (or any state at all).

Kopf-based operators must implement the checks and reactions to the changes, so that both states are synchronised according to the operator’s concepts.

Kopf only provides a few ways and tools for achieving this easily.

Event-driven reactions

Normally, Kopf triggers the on-creation/on-update/on-deletion handlers every time anything changes on the object, as reported by Kubernetes API. It provides both the current state of the object and a diff list with the last handled state.

The event-driven approach is the best, as it saves system resources (RAM & CPU), and does not trigger any activity when it is not needed and does not consume memory for keeping the object’s last known state permanently in memory.

But it is more difficult to develop, and is not suitable for some cases: e.g., when an external non-Kubernetes system is monitored via its API.

See also

Handlers

Regularly scheduled timers

Timers are triggered on a regular schedule, regardless of whether anything changes or does not change in the resource itself. This can be used to verify both the resource’s body, and the state of other related resources through API calls, and update the original resource’s status/content.

See also

Timers

Permanently running daemons

As a last resort, a developer can implement a background task, which checks the status of the system and reacts when the “actual” state diverts from the “desired” state.

See also

Daemons

What to use when?

As a rule of thumb _(recommended, but not insisted)_, the following guidelines can be used to decide which way of reconciliation to use in which cases:

  • In the first place, try the event-driven approach by watching for the children resources (those belonging to the “actual” state).

    If there are many children resources for one parent resource, store their brief statuses on the parent’s status.children.{id} from every individual child, and react to the changes of status.children in the parent resource.

  • If the “desired” state can be queried with blocking waits (e.g. by running a GET query on a remote job/task/activity via an API, which blocks until the requested condition is reached), then use daemons to poll for the status, and process it as soon as it changes.

  • If the “desired” state is not Kubernetes-related, maybe it is an external system accessed by an API, or if delays in reconciliation are acceptable, then use the timers.

  • Only as the last resort, use the daemons with a while True cycle and explicit sleep.