dactor-discover-k8s
Kubernetes-based cluster node discovery for the dactor distributed actor framework.
Overview
This crate provides two Kubernetes-native implementations of dactor's ClusterDiscovery trait:
| Strategy | Mechanism | Best for |
|---|---|---|
KubernetesDiscovery |
Kubernetes API pod listing | Fine-grained control, label-based filtering |
HeadlessServiceDiscovery |
DNS resolution of a headless Service |
Simple setups, no RBAC needed |
How dactor uses discovery
The ClusterDiscovery trait provides a discover() method that returns peer node addresses. The dactor runtime uses this as follows:
- On startup: The runtime calls
discover()to find initial peers and connects viaAdapterCluster::connect(). - Periodic polling: The application calls
discover()periodically (e.g., every 10s) to detect topology changes — pods scaling up/down, rolling restarts. - Diff and reconcile: Compare discovered peers with connected nodes. Connect new peers, disconnect removed ones.
- Events:
ClusterEventEmitterfiresNodeJoined/NodeLeftevents for actor subscriptions.
// Example: periodic discovery loop
loop
Note: PUB4 (planned) will make ClusterDiscovery async with Result return type. A built-in polling/reconciliation loop may be added to dactor core in a future release.
Quick Start
API-based discovery
use ClusterDiscovery;
use KubernetesDiscovery;
let discovery = builder
.namespace
.label_selector
.port
.port_name
.build;
// Sync (calls the K8s API internally via tokio)
let peers: = discovery.discover;
// Or use async directly
// let peers = discovery.discover_async().await?;
Headless service discovery
use ClusterDiscovery;
use HeadlessServiceDiscovery;
let discovery = new;
let peers: = discovery.discover;
Kubernetes Deployment Example
Deployment with labels
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dactor-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-dactor-service
template:
metadata:
labels:
app: my-dactor-service
spec:
containers:
- name: app
image: my-dactor-service:latest
ports:
- name: dactor
containerPort: 9000
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
Headless Service (for DNS-based discovery)
apiVersion: v1
kind: Service
metadata:
name: dactor-cluster
namespace: production
spec:
clusterIP: None # headless
selector:
app: my-dactor-service
ports:
- name: dactor
port: 9000
targetPort: dactor
RBAC (for API-based discovery)
The pod's service account needs permission to list pods:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dactor-pod-reader
namespace: production
rules:
- apiGroups:
resources:
verbs:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dactor-pod-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: default
namespace: production
roleRef:
kind: Role
name: dactor-pod-reader
apiGroup: rbac.authorization.k8s.io
API vs Headless Service Discovery
| Feature | KubernetesDiscovery |
HeadlessServiceDiscovery |
|---|---|---|
| Mechanism | Kubernetes API (pod list) | DNS A/AAAA record lookup |
| RBAC required | Yes (pod list/get) | No |
| Named port resolution | Yes | No |
| Pod phase filtering | Yes (Running only) | Handled by K8s readiness |
| Network dependency | API server | CoreDNS / kube-dns |
| Latency | Higher (API call) | Lower (DNS cache) |
| Granularity | Full pod metadata | IP addresses only |
Environment Variables
| Variable | Description |
|---|---|
DACTOR_POD_IP |
Pod's own IP (preferred) |
POD_IP |
Pod's own IP (fallback) |
The namespace is auto-detected from the mounted service account token at
/var/run/secrets/kubernetes.io/serviceaccount/namespace.
License
MIT