Skip to content

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.

  1. A Discovery Handler starts and registers with the Agent over the registration socket (RegisterDiscoveryHandler, discovery-utils/proto/discovery.proto:8). The Agent receiver is run_registration_server, spawned at agent/src/main.rs:49.
  2. The user applies a Configuration. The Agent's Configuration controller reconciles it in reconcile at agent/src/util/discovery_configuration_controller.rs:80, adding a finalizer if missing at agent/src/util/discovery_configuration_controller.rs:99.
  3. On the first reconcile there is no request yet, so the controller calls new_request and returns an empty list; the None branch starts at agent/src/util/discovery_configuration_controller.rs:134 and returns vec![] at agent/src/util/discovery_configuration_controller.rs:145.
  4. The request queries each registered DH over gRPC. query at agent/src/discovery_handler_manager/discovery_handler_registry.rs:309 builds a DiscoverRequest at agent/src/discovery_handler_manager/discovery_handler_registry.rs:314, resolving discovery properties from ConfigMaps and Secrets via solve_discovery_properties at agent/src/discovery_handler_manager/discovery_handler_registry.rs:322.
  5. Device lists from the DH are watched by watch_devices at agent/src/discovery_handler_manager/discovery_handler_registry.rs:249, deduplicated by CDI fully qualified name at agent/src/discovery_handler_manager/discovery_handler_registry.rs:285, then pushed to the device plugin layer with send_replace at agent/src/discovery_handler_manager/discovery_handler_registry.rs:288.
  6. On a later reconcile get_request hits, and get_instances at agent/src/discovery_handler_manager/discovery_handler_registry.rs:186 converts each endpoint's latest devices into Instances via device_to_instance at agent/src/discovery_handler_manager/discovery_handler_registry.rs:213. The Instance name is format!("{}-{}", self.key, dev.device_hash()) at agent/src/discovery_handler_manager/discovery_handler_registry.rs:239.
  7. 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 at agent/src/util/discovery_configuration_controller.rs:149, and applies the current ones via server side apply at agent/src/util/discovery_configuration_controller.rs:164.
  8. The Controller watches Instance events at controller/src/main.rs:48 and, depending on the InstanceAction, deploys a broker Pod per node up to capacity or a Job, and creates the Instance and Configuration Services. The behavior table is documented at controller/src/util/instance_action.rs:32.
  9. A successful reconcile requeues after 600 seconds (SUCCESS_REQUEUE at agent/src/util/discovery_configuration_controller.rs:38); a failure backs off exponentially from 500 ms in error_policy at agent/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 DiscoveryHandler gRPC service at discovery-utils/proto/discovery.proto:32 can 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 normal kubectl and RBAC.
  • Brokers: the workload Akri schedules per device is an arbitrary PodSpec or JobSpec supplied in the Configuration (BrokerSpec at shared/src/akri/configuration.rs:90).