Architecture
Big picture
Akri has five moving parts: two Custom Resource Definitions (CRDs), a set of Discovery Handlers, an Agent that runs on every node, and a Controller. A user writes a Configuration CRD to say what to look for. Discovery Handlers (DHs) know how to find devices for a specific protocol. The Agent drives discovery on each node and writes one Instance CRD per discovered device. The Controller watches Instances and deploys broker workloads plus Services.
Components
Configuration CRD
The Configuration is the user facing declaration of what to discover. The type is ConfigurationSpec at shared/src/akri/configuration.rs:114, registered under group akri.sh version v0 at shared/src/akri/configuration.rs:106. It carries discovery_handler (which DH to use plus its details), capacity (how many nodes may schedule workloads for a found device), an optional broker_spec, optional instance_service_spec and configuration_service_spec, and broker_properties. A broker is either a Pod or a Job, modeled by the BrokerSpec enum at shared/src/akri/configuration.rs:90.
Instance CRD
Each Instance represents one discovered device. The type is InstanceSpec at shared/src/akri/instance.rs:54, with short name akrii declared at shared/src/akri/instance.rs:27. Key fields are cdi_name at shared/src/akri/instance.rs:59, shared at shared/src/akri/instance.rs:76, nodes at shared/src/akri/instance.rs:81, and device_usage at shared/src/akri/instance.rs:90. The device_usage map tracks capacity slots: each slot maps to a node name that has claimed it, or to an empty string when free.
Discovery Handlers
A Discovery Handler is a process that knows one protocol (ONVIF for cameras, udev for local devices, OPC UA, or debug-echo for testing). DHs talk to the Agent over gRPC. The protocol is defined in discovery-utils/proto/discovery.proto. The Agent advertises a Registration service at discovery-utils/proto/discovery.proto:7; a DH calls RegisterDiscoveryHandler at discovery-utils/proto/discovery.proto:8, passing its name, an endpoint that is either a Unix Domain Socket (UDS) or NETWORK address (the EndpointType enum at discovery-utils/proto/discovery.proto:19), and a shared flag at discovery-utils/proto/discovery.proto:26. The Agent then calls the DH back through the DiscoveryHandler service at discovery-utils/proto/discovery.proto:32, whose Discover RPC returns a stream DiscoverResponse at discovery-utils/proto/discovery.proto:33 (server streaming, so the DH can push device list updates).
Agent
The Agent is a DaemonSet; its entry point is main at agent/src/main.rs:23. On startup it builds the DH registry with new_registry at agent/src/main.rs:42, starts the registration server with run_registration_server at agent/src/main.rs:49, creates an InMemoryManager at agent/src/main.rs:58 and a DevicePluginManager at agent/src/main.rs:61, starts the device plugin manager with start_dpm at agent/src/main.rs:69, starts a slot reclaimer with start_reclaimer at agent/src/main.rs:75, and finally starts the Configuration controller with start_controller at agent/src/main.rs:89.
Controller
The Controller's entry point is main at controller/src/main.rs:21. It spawns handle_existing_instances at controller/src/main.rs:42, do_instance_watch at controller/src/main.rs:48, a NodeWatcher at controller/src/main.rs:56, and a BrokerPodWatcher at controller/src/main.rs:63. Based on each Instance event it deploys or removes broker workloads. The action types are the InstanceAction enum at controller/src/util/instance_action.rs:46.
How a request flows
This traces one device from discovery to a running broker.
- A Discovery Handler starts and registers with the Agent over the registration socket (
RegisterDiscoveryHandler,discovery-utils/proto/discovery.proto:8). The Agent receiver isrun_registration_server, spawned atagent/src/main.rs:49. - The user applies a Configuration. The Agent's Configuration controller reconciles it in
reconcileatagent/src/util/discovery_configuration_controller.rs:80, adding a finalizer if missing atagent/src/util/discovery_configuration_controller.rs:99. - On the first reconcile there is no request yet, so the controller calls
new_requestand returns an empty list; the None branch starts atagent/src/util/discovery_configuration_controller.rs:134and returnsvec![]atagent/src/util/discovery_configuration_controller.rs:145. - The request queries each registered DH over gRPC.
queryatagent/src/discovery_handler_manager/discovery_handler_registry.rs:309builds aDiscoverRequestatagent/src/discovery_handler_manager/discovery_handler_registry.rs:314, resolving discovery properties from ConfigMaps and Secrets viasolve_discovery_propertiesatagent/src/discovery_handler_manager/discovery_handler_registry.rs:322. - Device lists from the DH are watched by
watch_devicesatagent/src/discovery_handler_manager/discovery_handler_registry.rs:249, deduplicated by CDI fully qualified name atagent/src/discovery_handler_manager/discovery_handler_registry.rs:285, then pushed to the device plugin layer withsend_replaceatagent/src/discovery_handler_manager/discovery_handler_registry.rs:288. - On a later reconcile
get_requesthits, andget_instancesatagent/src/discovery_handler_manager/discovery_handler_registry.rs:186converts each endpoint's latest devices into Instances viadevice_to_instanceatagent/src/discovery_handler_manager/discovery_handler_registry.rs:213. The Instance name isformat!("{}-{}", self.key, dev.device_hash())atagent/src/discovery_handler_manager/discovery_handler_registry.rs:239. - The reconcile stamps the local node name and an owner reference onto each Instance at
agent/src/util/discovery_configuration_controller.rs:127, deletes Instances for devices that vanished atagent/src/util/discovery_configuration_controller.rs:149, and applies the current ones via server side apply atagent/src/util/discovery_configuration_controller.rs:164. - The Controller watches Instance events at
controller/src/main.rs:48and, depending on theInstanceAction, deploys a broker Pod per node up tocapacityor a Job, and creates the Instance and Configuration Services. The behavior table is documented atcontroller/src/util/instance_action.rs:32. - A successful reconcile requeues after 600 seconds (
SUCCESS_REQUEUEatagent/src/util/discovery_configuration_controller.rs:38); a failure backs off exponentially from 500 ms inerror_policyatagent/src/util/discovery_configuration_controller.rs:176.
Key design decisions
The shared flag on a device decides whether it is one resource across nodes or one per node, and the only mechanism behind that is how the Instance name is hashed (see Internals). A shared device hashes its id alone, so every node resolves it to the same Instance; a local device folds the node name into the hash, so the same id becomes a distinct Instance per node. Capacity is enforced through the device_usage slot map on InstanceSpec (shared/src/akri/instance.rs:90).
Device injection was moved off the device plugin Allocate response and onto the Container Device Interface (CDI) v0.6.0 schema; the schema module records the spec URL at agent/src/device_manager/cdi.rs:1.
Extension points
- Discovery Handlers: any process implementing the
DiscoveryHandlergRPC service atdiscovery-utils/proto/discovery.proto:32can register and feed devices to the Agent, so new protocols need no Agent changes. - CRDs: Configuration and Instance are standard Kubernetes CRDs under group
akri.sh(shared/src/akri/configuration.rs:106), so they integrate with normalkubectland RBAC. - Brokers: the workload Akri schedules per device is an arbitrary PodSpec or JobSpec supplied in the Configuration (
BrokerSpecatshared/src/akri/configuration.rs:90).