Kubernetes Discovery
DDS discovery in Kubernetes using DNS-based service resolution.
Overview
Standard DDS uses UDP multicast for discovery, which is typically not available in Kubernetes environments. HDDS provides K8s Discovery - a zero-dependency solution using Kubernetes Headless Services and DNS resolution.
How It Works
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pod A │ │ Pod B │ │ Pod C │ │
│ │ HDDS │ │ HDDS │ │ HDDS │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Headless Service│ │
│ │ hdds-discovery │ │
│ │ clusterIP: None │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ CoreDNS │ │
│ │ A/AAAA records │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
- Each pod queries DNS:
hdds-discovery.default.svc.cluster.local - CoreDNS returns A/AAAA records for all pods matching the service selector
- HDDS registers each pod IP as a static peer
- Standard RTPS discovery (SPDP/SEDP) proceeds over unicast UDP
Kubernetes Setup
Headless Service
Create a Headless Service (clusterIP: None) for your DDS pods:
apiVersion: v1
kind: Service
metadata:
name: hdds-discovery
namespace: default
spec:
clusterIP: None # Required: makes it a headless service
selector:
app: my-dds-app
ports:
- name: dds-spdp
port: 7400
protocol: UDP
- name: dds-user
port: 7411
protocol: UDP
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dds-app
spec:
replicas: 3
selector:
matchLabels:
app: my-dds-app
template:
metadata:
labels:
app: my-dds-app
spec:
containers:
- name: app
image: my-dds-app:latest
env:
# Optional: expose pod info to application
- name: HDDS_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HDDS_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: HDDS_K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- containerPort: 7400
protocol: UDP
- containerPort: 7411
protocol: UDP
Rust API
Basic Usage
use hdds::{Participant, TransportMode};
let participant = Participant::builder("my-app")
.with_transport(TransportMode::UdpMulticast)
.with_k8s_discovery("hdds-discovery", "default")
.build()?;
Custom Configuration
use hdds::{Participant, TransportMode};
use hdds::discovery::K8sDiscoveryConfig;
use std::time::Duration;
let k8s_config = K8sDiscoveryConfig::new("hdds-discovery", "production")
.with_port(7411)
.with_poll_interval(Duration::from_secs(10));
let participant = Participant::builder("my-app")
.with_transport(TransportMode::UdpMulticast)
.with_k8s_discovery_config(k8s_config)
.build()?;
Manual Discovery
For advanced use cases:
use hdds::discovery::{K8sDiscovery, K8sDiscoveryConfig};
let discovery = K8sDiscovery::with_service("hdds-discovery", "default");
// One-shot resolution
let peers = discovery.resolve_peers();
for peer in peers {
println!("Found peer: {}", peer);
}
// Background polling with callback
let discovery = K8sDiscovery::with_service("hdds-discovery", "default")
.on_peer_discovered(|addr| {
println!("New peer discovered: {}", addr);
});
let handle = discovery.start();
// ... later
handle.stop();
Helper Functions
use hdds::discovery::k8s::{resolve_k8s_service, get_pod_ip, get_pod_name};
// One-shot DNS resolution
let peers = resolve_k8s_service("hdds-discovery", "default", 7411);
// Get pod info from downward API
if let Some(ip) = get_pod_ip() {
println!("My pod IP: {}", ip);
}
if let Some(name) = get_pod_name() {
println!("My pod name: {}", name);
}
Environment Variables
| Variable | Default | Description |
|---|---|---|
HDDS_K8S_SERVICE | hdds-discovery | Kubernetes service name |
HDDS_K8S_NAMESPACE | default | Kubernetes namespace |
HDDS_K8S_PORT | 7411 | DDS port for discovered peers |
HDDS_K8S_POLL_INTERVAL_MS | 5000 | DNS poll interval (ms) |
HDDS_POD_IP | - | Pod IP (from downward API) |
HDDS_POD_NAME | - | Pod name (from downward API) |
Feature Flag
K8s Discovery requires the k8s feature:
[dependencies]
hdds = { path = "../hdds/crates/hdds", features = ["k8s"] }
Architecture Notes
Zero Dependencies
K8s Discovery uses only std::net::ToSocketAddrs for DNS resolution - no external crates required.
Background Polling
- Dedicated thread:
hdds-k8s-discovery - Polls DNS at configurable interval (default: 5s)
- New peers trigger
register_static_peer()on DiscoveryFsm - Peer removal handled by DDS lease mechanism (not DNS)
DNS Name Format
{service}.{namespace}.svc.{cluster_domain}
Example: hdds-discovery.default.svc.cluster.local
Troubleshooting
No Peers Discovered
-
Check Headless Service exists:
kubectl get svc hdds-discovery -o yaml
# Verify: clusterIP: None -
Check pods are selected:
kubectl get endpoints hdds-discovery
# Should list pod IPs -
Test DNS resolution from a pod:
kubectl exec -it <pod> -- nslookup hdds-discovery.default.svc.cluster.local -
Enable debug logging:
RUST_LOG=hdds::discovery=debug ./my-app
Peers Found But No Communication
-
Check UDP ports are open:
kubectl exec -it <pod> -- netstat -ulnp | grep 74 -
Check Network Policy allows UDP:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dds
spec:
podSelector:
matchLabels:
app: my-dds-app
ingress:
- ports:
- protocol: UDP
port: 7400
- protocol: UDP
port: 7411
Slow Discovery
- Reduce poll interval:
HDDS_K8S_POLL_INTERVAL_MS=1000 - Check CoreDNS performance
- Consider using StatefulSet for stable pod names
Best Practices
- Use dedicated namespace for DDS workloads
- Set resource limits on DDS pods
- Use StatefulSet for predictable pod identities
- Monitor DNS resolution latency
- Configure appropriate poll interval based on pod churn rate
Example: Complete K8s Deployment
---
apiVersion: v1
kind: Namespace
metadata:
name: dds-system
---
apiVersion: v1
kind: Service
metadata:
name: hdds-discovery
namespace: dds-system
spec:
clusterIP: None
selector:
app: sensor-network
ports:
- name: spdp
port: 7400
protocol: UDP
- name: user
port: 7411
protocol: UDP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sensor-node
namespace: dds-system
spec:
serviceName: hdds-discovery
replicas: 5
selector:
matchLabels:
app: sensor-network
template:
metadata:
labels:
app: sensor-network
spec:
containers:
- name: sensor
image: hdds-sensor:1.0
env:
- name: HDDS_K8S_SERVICE
value: hdds-discovery
- name: HDDS_K8S_NAMESPACE
value: dds-system
- name: HDDS_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports:
- containerPort: 7400
protocol: UDP
- containerPort: 7411
protocol: UDP