Node configuration management module (shturval-node-config)

About the service

ParameterDescription
GUI nameNode Configuration Management Module
System name (identifier)shturval-node-config
Cluster AssignmentApplying node configuration (NCI, NodeConfigItem) to cluster machines: NTP, Container Runtime (including registries), sysctl, Kubelet, trusted certificates, etc.
CriticalYes
Public repository— (component of the Shturval platform)
Dependenciesshturval-node-config-crds, shturval-networking
Namespaceshturval-node-config-system

Configuring NCI in the GUI, selectors, priorities and examples of sections can be found on the Node Configuration (NCI) page.

The service status can be viewed in the GUI: Services and repositories → Installed services → Node configuration management module (shturval-node-config) → Manage.

Screenshots of the service page

Shturval-node-config service in the list of installed services Service management page shturval-node-config

Contents

  1. Review
  2. Architecture
  3. Installation/deployment
  4. Diagnostics
  5. Lifecycle
  6. Operator and Daemon Parameters
  7. CRD/API reference

1. Review

shturval-node-config ensures that the Kubernetes cluster node configuration is delivered and applied. The operator manages configuration objects and secure reboot, and the std daemon applies the configuration to the node, signals the need for a reboot, and completes it when authorized by the operator.

Place in the chain of command

The administrator creates and modifies NodeConfigItem objects, the operator maps them to the NodeConfig of specific nodes, and the std daemon on each node receives the resulting configuration, applies it, and reflects the state back to NodeConfig.status. If necessary, the operator performs a cordon/drain and only then allows the node to reboot.

End-to-end sequence (NCI → NodeConfig → daemon, drain, reboot, CSR) in the diagram below; Detailed steps are in §5.

Illustration: Place in the chain of command

End-to-end scenario (E2E)

2. Architecture

In the working environment, the controller is represented by two SSCs:

  • shturval-node-config-crds — CRD chart of the group node.shturval.tech; (see the screenshot below)
  • shturval-node-config — the main chart of the module. (see the screenshot below)

General architecture diagram, see the illustration below.

Architecture <code>shturval-node-config</code>

shturval-node-config service

The operator manages the configuration of cluster nodes through the CRDs NodeConfig and NodeConfigItem, orchestrates drains and reboots, and automatically approves the CSR for kubelet-serving.

Key Features

CharacteristicsDescription
API Groupnode.shturval.tech
API Versionv1beta2
Deployment Namespaceshturval-node-config-system
Scope of resourcesClusterfor NodeConfig and NodeConfigItem
Leader electionControlled by flag --leader-elect, identifier nci.shturval.tech
MetricsThe address is set by --metrics-bind-address, disabled by default (0)
health probe:8081
WebhookMutating webhook, path /mutate-node-shturval-tech-nodeconfig
HTTP/2Disabled by default
Default Replicas2

Components

ComponentType KubernetesPurpose
NodeConfigReconcilerControllerControls node drainage and reboot
NodeConfigItemReconcilerControllerDistributes patterns NodeConfigItem to NodeConfig
NodeReconcilerControllerSupports matching Node ↔ NodeConfig
NodeCSRReconcilerControllerAutomatically approves CSR kubelet-serving
NodeConfigItemAdmitterMutatingWebhookAdds service annotations to NodeConfigItem

Demon std

std (shturvald) runs on every Linux node in the cluster. It applies the merged node configuration, monitors the local init config, synchronizes the state with the NodeConfig object, and reboots only after permission from the operator.

Key Features

CharacteristicsDescription
Binary namestd
systemd unit nameshturvald.service
Unit file path/etc/systemd/system/shturvald.service
Required RightsRunning with root access only
kubeconfig default/etc/kubernetes/kubelet.conf
Local init NCI/etc/std/nci.json
NCI Cache/etc/std/caches/
Status of applied NCIs/etc/std/applied/
United config/etc/std/merged.json
Working directory/etc/std/

Main tasks

ComponentPurpose
DaemonBasic Workcycle, Signal Processing and Queues
NodeReaderReading NodeConfig and NCI from the cluster
NodeWriterRecording status and conditions in NodeConfig
WorkQueuePeriodic Synchronization Queue
fsnotifyChange tracking /etc/std/nci.json
configsyncerNCI synchronization from cluster to local cache

Objects in the cluster

ObjectsTypePurpose
Operator Main ProcessDeploymentExecutes controllers and webhook
ServiceAccountServiceAccountOperator Process Account
PermissionsClusterRole / ClusterRoleBindingAccess to CRD, Node, CSR and related objects
Webhook endpointMutating webhookAnnotates NodeConfigItem with CREATE and UPDATE
ConfigMap nci-controller-paramsConfigMapPasses required environment variables to webhook

External dependencies

SystemPurpose
Kubernetes APIRead and update Node, NodeConfig, NodeConfigItem, CSR
Cluster nodesCordon, drain, uncordon and reboot coordination
cert-managerIssuing TLS webhook certificates with the appropriate deployment option

Demon shurvald (std)

The daemon is deployed directly on a Linux node and runs as a system service. It uses local files in /etc/std/, kubeconfig kubelet and sync queue to apply the configuration and exchange state with the operator.

Demon Circuits

Logical architecture of the std daemon on the node, see the illustration below.

Logical architecture of the daemon <code>std</code> on the node

Processing flow: configuration, application, interaction with NodeConfig:

Illustration: Demon Circuits

Processing flow: configuration, application, interaction with <code>NodeConfig</code>

Components on a node

ComponentPurpose
shturvald.servicePermanently running the daemon as a systemd unit
/etc/std/nci.jsonLocal start NCI before connecting to the cluster
/etc/std/caches/NCI cache obtained from cluster
/etc/std/applied/Local confirmation of already applied NCIs
/etc/std/merged.jsonFinal merged configuration
/etc/kubernetes/kubelet.confAccessing the cluster API after joining a node

Operator-daemon communication

DirectionMechanism
Operator → daemonFields NodeConfig.spec, flags rebootallowed and list nodeconfigitems
Demon → operatorFields NodeConfig.status, condition ConfigApplied, annotation node.shturval.tech/initchecksum
Reboot CoordinationThrough rebootrequired, rebootscheduled, rebootallowed and state ncistate

3. Installation / deployment

shturval-node-config is a critical platform service. The activation and life cycle of the controller in the cluster is provided by cluster-manager (platform service); ShturvalServiceConfig (SSC) objects for associated charts are created and maintained as part of the platform configuration, rather than as an arbitrary manual installation of a Helm chart by an administrator.

The std agent (shturvald) is installed by the platform on nodes automatically as part of the node preparation process (bootstrap). No separate manual installation of the agent is required.

CRDs, controllers and resource ownership:

Illustration: 3. Installation / deployment

CRD, controllers and resource ownership


4. Diagnostics

shturval-node-config service

Quick checks

kubectl get nc
kubectl get nci

The operator publishes the following printable columns for NodeConfig:

ColumnDescription
APPLY GROUPReboot Coordination Team
READYAll NCIs applied, no reboot required
REBOOT REQUIREDThe daemon requested a reboot
REBOOT SCHEDULEDThe operator has queued the node
REBOOT ALLOWEDThe operator allowed the reboot

The following columns are available for NodeConfigItem:

ColumnDescription
PRIORITYMerger priority
APPLIEDRatio UpdatedNodes/SelectedNodes
READYAll selected NodeConfig updated
SELECTORMatch selector NodeConfig

Conditions of the NodeConfig resource

ConditionWhat does True meanWhat does False mean / what to check
NodeDrainedDrain completed, not required or drain timeout exceededThe drain is still running or ended with an error
RebootScheduledThe node is queued for rebootSlots are busy or there is a scheduling conflict
ConfigAppliedConfiguration successfully appliedConfiguration still in use, failed, checksum discrepancy detected, merge failed, or awaiting permission to reboot

Note: NodeDrained only appears with rebootscheduled=true, and RebootScheduled only appears with rebootrequired=true.

Demon std

Quick checks

systemctl status shturvald
std node status
std node info

If the daemon has not yet joined the cluster, an additional guideline is the presence of the /etc/std/nci.json and /etc/kubernetes/kubelet.conf files.

Demon states

StateDescription
WorkingConfiguration in progress
DoneConfiguration successfully applied
UnreconcilableThe configuration cannot be brought to the required state
DegradedAn error not directly related to the configuration has been reported

Annotations affecting diagnostics

AnnotationPurpose
node.shturval.tech/initchecksumChecksum /etc/std/nci.json to detect discrepancies between local init NCI and state in cluster

Typical operating situations

SymptomPossible reasonAction
ConfigApplied=False with reason ChecksumMismatchLocal /etc/std/nci.json does not match checksum written in NodeConfigCheck local init NCI and expected cluster configuration
RebootScheduled=False with reason SlotsOccupiedAnother reboot is already in progress at applygroupWait until the slot becomes free and the operator tries again
NodeDrained=False with reason DrainFailedThe operator was unable to complete the drainCheck blocking workloads on the node and repeat processing
The demon does not enter operating mode for a long timeMissing /etc/std/nci.jsonPrepare local init NCI
The daemon does not start synchronizing with the clusterMissing /etc/kubernetes/kubelet.confFinish joining the node to the cluster
std ends immediately after startThe process is not running as rootRun the daemon as root or via systemd unit

If, after reconciling the local init NCI and the expected cluster configuration, you need to re-run the configuration application, remove the node.shturval.tech/initchecksum annotation written to NodeConfig:

kubectl get nodeconfig <node-name> -o jsonpath='{.status}'
kubectl annotate nodeconfig <node-name> node.shturval.tech/initchecksum-

5. Life cycle

shturval-node-config service

Complete scenario on one diagram: see also §1 - place in the chain.

Node Circuit → NodeConfig

ActorAction
NodeReconcilerFor each Node supports NodeConfig with the same name
NodeReconcilerCopies node labels to NodeConfig, assigns applygroup (workers or controlplane)
NodeReconcilerFor a single Control Plane node, sets donotdrain=true
NodeReconcilerWhen deleted, Node deletes the associated NodeConfig

Matching Node and NodeConfig, see the illustration below.

Reconcile <code>Node</code> and <code>NodeConfig</code>

Outline NodeConfigItem → NodeConfig

ActorAction
AdministratorCreates or updates NodeConfigItem
NodeConfigItemReconcilerFinds all NodeConfig matching nodeconfigselector
NodeConfigItemReconcilerAdds the NCI name to the spec.nodeconfigitems selected NodeConfig
NodeConfigItemReconcilerUpdates status.selectednodeconfigs, status.updatednodeconfigs, status.applied, status.ready
NodeConfigItemReconcilerWhen deleted, removes the NCI name from the corresponding NodeConfig and removes the finalizer

Matching NodeConfigItem and selected NodeConfig, see the illustration below.

Reconcile <code>NodeConfigItem</code> and selected <code>NodeConfig</code>

Application and reload circuit

StepActorAction
1DemonApplies the configuration and, if necessary, sets ncistate=updatepending
2DemonSets rebootrequired=true
3OperatorEvaluates drain policy based on node labels and spec.donotdrain
4OperatorSchedules a reboot within applygroup; one node is allowed in a group at a time
5OperatorPerforms cordon and drain, after which it sets rebootallowed=true
6DemonPerforms a reboot or reapply without a physical reboot
7DemonAfter the node returns, sets ncistate=applied
8OperatorClears the reboot flags, performs uncordon and returns ready=true

Application and reload reconcile flow for NodeConfig, see the illustration below.

Reconcile <code>NodeConfig</code>

Agreement NodeConfig: drain:

Negotiation NodeConfig: reboot queue:

Agreement NodeConfig: conditions:

Drain Policy

ConditionBehavior
Node is labeled node.shturval.tech/apply-as-isThe node is not drained, reboot is allowed immediately
Node is labeled node.shturval.tech/disable-drainDrain fails, immediate reboot not allowed
In NodeConfig.spec.donotdrain=trueThe node is not drained, reboot is allowed immediately
No special conditionsCordon and drain are executed, then permission to reboot is issued

Finalizers

ResourceFinalizerPurpose
NodeConfignode.shturval.tech/finalizerClearing the owner reference and completing the deletion correctly
NodeConfigItemnode.shturval.tech/finalizerRemoving NCI reference from related NodeConfig

Demon std

Startup order

StepAction
1Sets up signal processing
2Runs commworker with a period of 60 seconds
3Starts tracking /etc/std/nci.json through fsnotify
4If not present, /etc/std/nci.json goes into a wait loop with polling every 10 seconds
5If nci.json is already there, but kubeconfig is missing, execute NodePrepare()
6Starts the main worker with a period of 60 seconds
7Waiting for /etc/kubernetes/kubelet.conf to appear
8Checks access to the cluster API
9Runs NodeReader and NodeWriter

Configuration application cycle

StepAction
1If present, kubeconfig synchronizes NodeConfig and NCI cache from the cluster
2Counts checksum of local /etc/std/nci.json
3Checks checksum against the annotation node.shturval.tech/initchecksum
4Combines local init NCI and cached NCIs into /etc/std/merged.json
5Applies the resulting configuration
6If the result is NotRequired, marks the NCI as Applied and puts ConfigApplied=True
7If the result is Reboot or DrainOnly, marks NCI as UpdatePending, sets rebootrequired=true and waits for rebootallowed=true

Reboot and reapply

ScriptBehavior
FullRebootThe daemon performs a system reboot after permission from the operator
DrainOnlyAfter permission from the operator, reapplies the configuration without a physical reboot

6. Operator and daemon parameters

shturval-node-config service

Process flags

ParameterDefaultPurpose
--metrics-bind-address0Metrics endpoint address; 0 disables metrics
--health-probe-bind-address:8081health probe address
--leader-electfalseEnabling leader election
--metrics-securetruePublishing metrics over HTTPS
--enable-http2falseEnabling HTTP/2
--debugfalseDebug logging mode

Environment Variables

VariableRequiredDefaultPurpose
namespaceRequired—Namespace operator for forming the ServiceAccount name in webhook
serviceAccountNameRequired—Operator ServiceAccount Name
disable-drain-label-keyOptionalnode.shturval.tech/disable-drainDrain disconnect mark key
apply-as-is-label-keyOptionalnode.shturval.tech/apply-as-isKey mark immediate reboot without drain

Demon std

Global CLI flags

ParameterDefaultPurpose
--kubeconfig/etc/kubernetes/kubelet.confAlternative path to kubeconfig; in production operation the system kubeconfig of the node is used
-v0Logging detail level
--logtostderrfalseOutputting logs to stderr
--stderrthreshold2Minimum log level for stderr

Basic commands

CommandPurpose
std daemonStarting the main daemon loop
std node init -f /etc/std/init.yamlJoining a node to a cluster
std node prepareUsing init NCI locally without connecting to a cluster
std node preinitPreparing init files
std node resetNode Reset
std node statusView configuration application status
std node infoView node information
std versionView binary version

Operating restrictions

LimitConsequence for the administrator
Running std requires root accessThe daemon must be run as root or via a systemd unit
The file /etc/kubernetes/kubelet.conf is fixed as the main kubeconfigChanging this path does not apply to normal operation and requires the release of a new version
Without /etc/std/nci.json, the daemon only waits for the fileBefore putting the node into operation, you need to prepare a local init NCI
Queue synchronization repeats every 60 secondsThere may be a technology delay between changing the configuration and trying again

7. CRD/API reference

Loop API group: node.shturval.tech, version v1beta2.

NodeConfig

NodeConfig is created automatically for each Node and is not intended to be created manually.

Resource characteristics

PropertyDescription
Short namenc
ScopeCluster
DestinationRepresents the configuration and application status for a specific node

spec

FieldTypeRequiredDescription
applygroupstringYesReboot Coordination Group: workers or controlplane
donotdrainboolNoDisables draining before reboot
nodeconfigitems[]stringNoList of NodeConfigItem names to be applied to the node

status

FieldTypeDescription
ncistate[]NodeConfigItemReferenceApplication status of each NCI: updatepending, scheduled, applied, orphaned
rebootrequiredboolThe demon reported the need to reboot
rebootscheduledboolThe operator has queued the node for reboot
rebootallowedboolThe operator allowed the reboot
updateinitnodeboolNode selected first for Kubernetes upgrade
readyboolAll NCIs applied, no reboot required
lastdrainstartedmetav1.TimeLast drain start time
conditions[]metav1.ConditionConditionsapplication and reboot

Conditions

ConditionValue TrueValue False
NodeDrainedDrain completed, not required or timed outThe drain is still running or ended with an error
RebootScheduledReboot plannedSlots are busy or there is a scheduling conflict
ConfigAppliedConfiguration appliedApplying in progress, an error has occurred, a checksum discrepancy has been detected, a merge error has occurred, or awaiting permission to reload

Example

apiVersion: node.shturval.tech/v1beta2
kind: NodeConfig
metadata:
  name: node-1
  labels:
    kubernetes.io/os: linux
    node.shturval.tech/applygroup: workers
spec:
  applygroup: workers
  donotdrain: false
  nodeconfigitems:
    - packages-basic-install
    - kubelet-basic

NodeConfigItem

NodeConfigItem is created by the administrator and specifies a reusable configuration template for a group of nodes selected by tags.

Resource characteristics

PropertyDescription
Short namenci
ScopeCluster
DestinationConfiguration template that applies to set NodeConfig by selector

spec

FieldTypeRequiredDescription
nodeconfigselectormap[string]stringYesSelector NodeConfig
priorityuintNoConfiguration merge priority
kubeversionstringNoKubernetes version for upgrade according to the init-node first scheme
repositoriesReposNoOS package repositories
packagesPackagesNoOS Packages
filesFilesNoFiles and directories
ntp*NTPNoNTP servers and time zone
certificatesCertificatesNoCA/TLS OS certificates
runtimecfg*ContainerRuntimeConfigNocontainerd configuration
kernelargumentsKernelArgumentsNoKernel options
kernelmodulesKernelModulesNoKernel Modules
sysctl*SysctlNosysctl options
systemdunitsSystemdUnitsNoSystemd unit management
usersUsersNoOS users
swap*SwapNoSwap management
iptables*IptablesNoiptables settings
selinux*SelinuxNoSELinux mode
kubecerts*KubeCertsNoKubernetes certificate rotation
kubelet*KubeletNokubelet options
kubeapi*KubeAPINokube-apiserver options, including OIDC and authenticationConfiguration.jwt

status

FieldTypeDescription
selectednodeconfigs[]stringList of NodeConfig matching selector
updatednodeconfigs[]stringList of NodeConfig on which NCI has already been applied
readyboolAll selected NodeConfig are updated and the list is not empty
appliedstringFormat string UpdatedNodes/SelectedNodes

Examples

Minimal example of installing packages:

apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
  name: packages-basic-install
spec:
  nodeconfigselector:
    node-role.kubernetes.io/worker: ""
  packages:
    - name: chrony
      state: present
    - name: curl
      state: present

Example of kubelet setup:

apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
  name: kubelet-basic
spec:
  nodeconfigselector:
    node-role.kubernetes.io/worker: ""
  kubelet:
    cpuManagerPolicy: none
    memoryManagerPolicy: None
    maxPods: 110
    hairpinMode: promiscuous-bridge

OIDC and AuthenticationConfiguration

kube-apiserver can admit users with a JWT token from an external OIDC provider.

Previously, OIDC on kube-apiserver was set with startup flags: --oidc-issuer-url, --oidc-client-id, and the other --oidc-* flags. That allowed only one provider.

Since Kubernetes 1.34 the same settings go into an AuthenticationConfiguration file (apiserver.config.k8s.io/v1). The file has a jwt list: each item is a separate provider with its issuer address (issuer.url) and, if needed, its own discovery address (issuer.discoveryURL).

Do not set --oidc-* flags together with the path to this file (--authentication-config): kube-apiserver will not start.

Fields for a single provider in the GUI are on the Node Configuration (NCI) page. Several providers are set in the manifest only.

In NCI:

  • spec.kubeapi.oidc — one provider, same as the GUI. The field is still supported: on merge and apply it becomes one JWT entry.
  • spec.kubeapi.authenticationConfiguration.jwt — a list of providers.

Through the platform API, oidc can list several providers: each item is a separate issuer. An item can have several client IDs (clientid), plus exist and discoveryURL. In the cluster NCI object those items are stored in authenticationConfiguration.jwt.

Kubernetes has supported the configuration file since 1.34. The platform switches a node from --oidc-* flags to the file only during a Kubernetes upgrade to 1.36 or newer (the version comes from the kube-apiserver image). Outside an upgrade the mode does not change. Below 1.36 the flags remain: if the NCI has several JWT entries, the node gets only the first issuer (by issuer.url).

After the switch to the file, the daemon does the following on the node:

  1. Writes the configuration to /etc/kubernetes/auth-config/authentication-config.yaml.
  2. In the kube-apiserver manifest, points to that file with --authentication-config= and attaches the /etc/kubernetes/auth-config/ directory so the API server can read it.
  3. Removes the old --oidc-* flags.

If later the file or arguments on the node no longer match the NCI, the daemon brings them back in line.

If several NCI objects apply to a node, their JWT lists are combined.

  • The same issuer.url in two NCI objects — the description from the NCI with the higher priority is kept.
  • A new issuer.url — the provider is added to the list.
  • exist: false on the entry with that URL — the provider is not in the resulting configuration. If exist is omitted or true, the provider stays.

One NCI cannot list the same issuer.url twice. The CA certificate must be PEM. Otherwise the object is not saved.

jwt[] fieldDescription
existtrue or omitted — the provider must be in the file; false — remove the entry with this issuer.url
issuer.urlToken issuer URL (iss). Same as oidc.issuerurl. Values in one NCI must be unique
issuer.discoveryURLIf set, the OpenID Discovery document is fetched from this address instead of {issuer.url}/.well-known/openid-configuration. Include the full path, including /.well-known/openid-configuration. Must differ from issuer.url
issuer.audiencesAccepted token audiences (aud). Same as oidc.clientid
issuer.certificateAuthorityIssuer CA in PEM. Same content as oidc.cafile (there the certificate is base64)
claimMappings.usernameUsername claim and prefix. Same as oidc.usernameclaim / usernameprefix
claimMappings.groupsGroups claim and prefix. Same as oidc.groupsclaim / groupsprefix

An example with a single oidc field is on the Node Configuration (NCI) page. Several providers:

apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
  name: auth-config-nci
spec:
  nodeconfigselector:
    node-role.kubernetes.io/control-plane: ""
  kubeapi:
    authenticationConfiguration:
      jwt:
        - issuer:
            url: https://auth.example.com
            discoveryURL: https://discovery.example.com/.well-known/openid-configuration
            audiences:
              - backend
          claimMappings:
            username:
              claim: sub
              prefix: "shturval:"
            groups:
              claim: groups
              prefix: "shturval:"
        - issuer:
            url: https://sso.example.com
            audiences:
              - backend
          claimMappings:
            username:
              claim: sub
        - exist: false
          issuer:
            url: https://old-idp.example.com
          claimMappings:
            username:
              claim: sub
  priority: 100

Data the daemon writes to NodeConfig

Annotations

AnnotationPurpose
node.shturval.tech/initchecksumMonitoring the discrepancy between the local init NCI and the state in the cluster

Application states

FieldDescription
WorkingConfiguration applied
DoneConfiguration successfully applied
UnreconcilableThe configuration cannot be brought to the required state
DegradedAn operational error was detected outside of the configuration itself