Skip to main content
Version: 0.8.4

Controller Tuning

kro has two main reconciliation loops: the RGD reconciler that processes ResourceGraphDefinitions, and the dynamic controller that manages instances. This page explains both and their tuning options.

RGD Reconciler​

The RGD reconciler watches ResourceGraphDefinition resources. When you create or update an RGD, it:

  1. Validates the schema and resource templates (see Static Type Checking)
  2. Creates or updates the generated CRD
  3. Registers the instance handler with the dynamic controller
SettingDefaultDescription
config.resourceGraphDefinitionConcurrentReconciles1Parallel RGD reconciles

Increase this if you're creating many RGDs simultaneously:

config:
resourceGraphDefinitionConcurrentReconciles: 3

Dynamic Controller​

The dynamic controller is a custom architecture designed for managing multiple resource types at runtime. Unlike traditional controllers that watch fixed resources, it adapts dynamically - when you create an RGD, it registers new watches without requiring restarts.

Architecture​

+----------------------------------------------------------+
| Dynamic Controller |
| |
| +--------------+ +--------------+ +--------------+ |
| | Informer | | Informer | | Informer | |
| | (WebApp) | | (Deployment) | | (Service) | |
| +------+-------+ +------+-------+ +------+-------+ |
| | | | |
| +-----------------+-----------------+ |
| | |
| v |
| +----------------+ |
| | Shared Queue | |
| +-------+--------+ |
| | |
| +--------------+--------------+ |
| | | | |
| v v v |
| +--------+ +--------+ +--------+ |
| |Worker 1| |Worker 2| |Worker N| |
| +--------+ +--------+ +--------+ |
+----------------------------------------------------------+

The controller is designed around a few core principles:

  • Single shared queue - All resource events flow through one rate-limited queue, preventing any single RGD from overwhelming the system
  • Lazy informers - Informers are created on-demand when an RGD is registered and stopped when deregistered
  • Parent-child watches - The controller watches both instances (parent) and their managed resources (children). Child events trigger parent reconciliation via labels
  • Metadata-only watches - The dynamic controller only fetches metadata, reducing memory overhead
note

kro is in active development. This architecture may evolve - for example, the shared queue could be replaced with per-RGD queues in future versions.

Concurrency​

SettingDefaultDescription
config.dynamicControllerConcurrentReconciles1Workers processing instances
config:
dynamicControllerConcurrentReconciles: 10

More workers increase throughput but also increase concurrent API server load.

Resync and Retries​

SettingDefaultDescription
config.dynamicControllerDefaultResyncPeriod36000Seconds between full resyncs (10 hours)
config.dynamicControllerDefaultQueueMaxRetries20Retries before dropping an item

The resync period triggers reconciliation for all resources periodically, even without changes. This catches any drift that might have been missed.

Rate Limiting​

The queue uses a combined rate limiter with two strategies:

  1. Exponential backoff - Failed items are requeued with increasing delays
  2. Bucket rate limiter - Limits overall event processing rate

These settings are only available via command-line flags:

FlagDefaultDescription
--dynamic-controller-rate-limiter-min-delay200msInitial retry delay
--dynamic-controller-rate-limiter-max-delay1000sMaximum retry delay
--dynamic-controller-rate-limiter-rate-limit10Events per second
--dynamic-controller-rate-limiter-burst-limit100Burst capacity

API Server Communication​

These settings control how kro communicates with the Kubernetes API server:

SettingDefaultDescription
config.clientQps100Maximum queries per second
config.clientBurst150Burst requests before throttling

Increase for larger clusters:

config:
clientQps: 200
clientBurst: 300

Brought to you with ♥ by SIG Cloud Provider