Kubernetes Backend Discovery and Automated Binding
Abstract
This RFC defines automated backend discovery and binding for Prism pattern runners deployed in Kubernetes environments. It enables pattern runners to automatically discover and connect to existing backend services (NATS, Redis, PostgreSQL, Kafka) that are already deployed via Helm charts or other means, without manual configuration of connection details.
The approach leverages Kubernetes-native service discovery, integrates with the prism-operator, and builds upon RFC-039's backend configuration registry to provide a seamless developer experience for deploying pattern runners on top of existing infrastructure.
Motivation
The Developer Experience Problem
Organizations often have standard backend deployments managed by platform teams:
# Platform team has already deployed these via Helm
helm install redis bitnami/redis -n infrastructure
helm install postgresql bitnami/postgresql -n infrastructure
helm install nats nats/nats -n infrastructure
helm install kafka bitnami/kafka -n infrastructure
When developers want to deploy Prism pattern runners, they currently need to:
- Manually discover connection details from each Helm release
- Copy-paste connection strings into pattern runner configs
- Manage secrets for authentication separately
- Update configs when backends change (e.g., password rotation)
- Repeat for every pattern runner in every namespace
This is tedious, error-prone, and doesn't scale.
What We Want Instead
Developers should be able to deploy pattern runners with minimal configuration:
apiVersion: prism.io/v1alpha1
kind: PrismPattern
metadata:
name: order-consumer
spec:
pattern: consumer
image: ghcr.io/prism/consumer-runner:latest
# Automatic discovery: Just reference the service name!
backendBindings:
message_source:
discover:
service: kafka.infrastructure.svc.cluster.local
port: 9092
state_store:
discover:
service: redis-master.infrastructure.svc.cluster.local
port: 6379
Or even better, with pre-registered backends:
apiVersion: prism.io/v1alpha1
kind: PrismPattern
metadata:
name: order-consumer
spec:
pattern: consumer
image: ghcr.io/prism/consumer-runner:latest
# Reference pre-registered backends
backendBindings:
message_source:
backendRef: kafka-production
state_store:
backendRef: redis-production
Current State vs. Desired State
| Aspect | Current (Manual) | Desired (Automated) |
|---|---|---|
| Backend Discovery | Developer manually finds service DNS | Automatic via K8s service discovery |
| Connection Config | Copy-paste from Helm release notes | Auto-generated from service metadata |
| Secret Management | Manual secret creation and mounting | Automatic secret discovery and binding |
| Backend Updates | Update every pattern runner config | Update once in backend registry |
| New Pattern Deployment | 10-15 minutes of config hunting | 30 seconds |
| Operational Complexity | High (N pattern configs to update) | Low (1 backend config to update) |
Goals
- Automatic Service Discovery: Pattern runners discover backend services using Kubernetes DNS and service metadata
- Helm Chart Integration: Detect and bind to backends deployed via standard Helm charts (Bitnami, official charts)
- Backend Registry Integration: Integrate with RFC-039's backend configuration registry for centralized management
- Secret Auto-Discovery: Automatically discover and mount secrets created by Helm charts
- Operator-Driven: Leverage prism-operator to orchestrate discovery and binding
- Zero-Config Deployment: Pattern runners work out-of-box with default Helm chart deployments
- Local-First Testing: Support local development with kind/Docker Desktop (ADR-004)
Non-Goals
- Cross-Cluster Discovery: Initial implementation focuses on same-cluster backends
- Dynamic Backend Switching: Pattern runners bind at startup; runtime changes require restart
- Backend Health Monitoring: Focus on discovery/binding, not runtime health checks
- Custom Resource Provisioning: This RFC assumes backends already exist; provisioning is separate
- Cloud-Managed Services: Initial focus on self-hosted K8s services; cloud services (RDS, ElastiCache) are future work
Design
Approach 1: Kubernetes-Native Service Discovery with PrismBackend CRD
Philosophy: Leverage Kubernetes-native primitives (Services, Secrets, ConfigMaps) and extend with a lightweight CRD for Prism-specific metadata.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Infrastructure Namespace │ │
│ │ │ │
│ │ Helm Release: redis │ │
│ │ ├─ Service: redis-master (6379) │ │
│ │ ├─ Service: redis-replicas (6379) │ │
│ │ └─ Secret: redis (password) │ │
│ │ │ │
│ │ Helm Release: postgresql │ │
│ │ ├─ Service: postgresql (5432) │ │
│ │ └─ Secret: postgresql (username, password) │ │
│ │ │ │
│ │ Helm Release: kafka │ │
│ │ ├─ Service: kafka (9092) │ │
│ │ └─ ConfigMap: kafka (bootstrap servers) │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ K8s Service Discovery │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Prism System Namespace │ │
│ │ │ │
│ │ PrismBackend CRDs (created by admin/operator) │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ apiVersion: prism.io/v1alpha1 │ │ │
│ │ │ kind: PrismBackend │ │ │
│ │ │ metadata: │ │ │
│ │ │ name: redis-production │ │ │
│ │ │ spec: │ │ │
│ │ │ type: redis │ │ │
│ │ │ discovery: │ │ │
│ │ │ service: │ │ │
│ │ │ name: redis-master │ │ │
│ │ │ namespace: infrastructure │ │ │
│ │ │ port: 6379 │ │ │
│ │ │ secret: │ │ │
│ │ │ name: redis │ │ │
│ │ │ namespace: infrastructure │ │ │
│ │ │ passwordKey: redis-password │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Prism Operator (reconciles PrismBackend) │ │
│ │ ├─ Validates service exists │ │
│ │ ├─ Resolves DNS to ClusterIP │ │
│ │ ├─ Validates secret exists │ │
│ │ └─ Syncs to admin backend registry │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Admin Sync │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Prism Admin Control Plane │ │
│ │ │ │
│ │ Backend Registry (from RFC-039) │ │
│ │ { │ │
│ │ "redis-production": { │ │
│ │ type: REDIS, │ │
│ │ config: { │ │
│ │ host: "redis-master.infrastructure.svc", │ │
│ │ port: 6379, │ │
│ │ password: <from-secret> │ │
│ │ } │ │
│ │ } │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ GetBackend RPC │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Application Namespace │ │
│ │ │ │
│ │ PrismPattern: order-consumer │ │
│ │ ├─ Deployment: consumer-runner pods │ │
│ │ │ └─ Fetches backend config at startup │ │
│ │ │ admin.GetBackend("redis-production") │ │
│ │ └─ Connects to redis-master.infrastructure.svc:6379 │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────────────── ────────────────────────────────┘
PrismBackend CRD Definition
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: prismbackends.prism.io
spec:
group: prism.io
names:
kind: PrismBackend
plural: prismbackends
singular: prismbackend
shortNames: [pbackend]
scope: Cluster
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [type, discovery]
properties:
# Backend type
type:
type: string
enum: [kafka, nats, redis, postgresql, mysql, mongodb]
# Service discovery configuration
discovery:
type: object
required: [service]
properties:
# Kubernetes service to discover
service:
type: object
required: [name]
properties:
name:
type: string
description: "Service name (e.g., redis-master)"
namespace:
type: string
description: "Service namespace (defaults to same as PrismBackend)"
port:
type: integer
description: "Service port (defaults to backend type default)"
# Secret discovery
secret:
type: object
properties:
name:
type: string
description: "Secret name (e.g., redis)"
namespace:
type: string
description: "Secret namespace"
usernameKey:
type: string
description: "Key in secret for username"
passwordKey:
type: string
description: "Key in secret for password"
# ConfigMap discovery (for complex configs)
configMap:
type: object
properties:
name:
type: string
namespace:
type: string
key:
type: string
description: "Key in ConfigMap for config data"
# Backend-specific overrides
config:
type: object
x-kubernetes-preserve-unknown-fields: true
# Capabilities (from RFC-039)
capabilities:
type: array
items:
type: string
# Metadata
metadata:
type: object
additionalProperties:
type: string
status:
type: object
properties:
phase:
type: string
enum: [Pending, Discovered, Error]
resolvedEndpoint:
type: string
description: "Resolved service endpoint (e.g., redis-master.infrastructure.svc:6379)"
conditions:
type: array
items:
type: object
properties:
type:
type: string
status:
type: string
message:
type: string
lastTransitionTime:
type: string
format: date-time
Example: Discovering Bitnami Redis
Step 1: Platform team deploys Redis via Helm
helm install redis bitnami/redis \
--namespace infrastructure \
--create-namespace \
--set auth.enabled=true \
--set auth.password=supersecret
This creates:
- Service:
redis-master(6379) - Service:
redis-replicas(6379) - Secret:
rediswith keyredis-password
Step 2: Platform team creates PrismBackend CRD
apiVersion: prism.io/v1alpha1
kind: PrismBackend
metadata:
name: redis-production
spec:
type: redis
discovery:
# Discover master service
service:
name: redis-master
namespace: infrastructure
port: 6379
# Discover password secret
secret:
name: redis
namespace: infrastructure
passwordKey: redis-password
# Backend capabilities
capabilities:
- keyvalue_basic
- keyvalue_scan
- keyvalue_ttl
- keyvalue_transactional
- pubsub_basic
metadata:
helm_release: redis
helm_chart: bitnami/redis
helm_version: "19.0.0"
region: us-west-2
env: production
Step 3: Operator reconciles PrismBackend
func (r *PrismBackendReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var backend prismv1alpha1.PrismBackend
if err := r.Get(ctx, req.NamespacedName, &backend); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 1. Validate service exists
svcName := backend.Spec.Discovery.Service.Name
svcNamespace := backend.Spec.Discovery.Service.Namespace
if svcNamespace == "" {
svcNamespace = backend.Namespace
}
var service corev1.Service
if err := r.Get(ctx, client.ObjectKey{Name: svcName, Namespace: svcNamespace}, &service); err != nil {
return r.updateStatus(ctx, &backend, "Error", fmt.Sprintf("Service not found: %v", err))
}
// 2. Resolve service endpoint
endpoint := fmt.Sprintf("%s.%s.svc.cluster.local:%d",
svcName, svcNamespace, backend.Spec.Discovery.Service.Port)
// 3. Validate secret exists (if specified)
var password string
if backend.Spec.Discovery.Secret != nil {
secretName := backend.Spec.Discovery.Secret.Name
secretNamespace := backend.Spec.Discovery.Secret.Namespace
if secretNamespace == "" {
secretNamespace = svcNamespace
}
var secret corev1.Secret
if err := r.Get(ctx, client.ObjectKey{Name: secretName, Namespace: secretNamespace}, &secret); err != nil {
return r.updateStatus(ctx, &backend, "Error", fmt.Sprintf("Secret not found: %v", err))
}
passwordKey := backend.Spec.Discovery.Secret.PasswordKey
if passwordKey == "" {
passwordKey = "password"
}
password = string(secret.Data[passwordKey])
}
// 4. Build Backend configuration (from RFC-039)
backendConfig := &adminpb.Backend{
Name: backend.Name,
Type: r.mapBackendType(backend.Spec.Type),
Config: &adminpb.BackendConfig{
Config: &adminpb.BackendConfig_Redis{
Redis: &adminpb.RedisConfig{
Host: fmt.Sprintf("%s.%s.svc.cluster.local", svcName, svcNamespace),
Port: backend.Spec.Discovery.Service.Port,
Password: password,
TlsEnabled: false,
},
},
},
Capabilities: backend.Spec.Capabilities,
Metadata: backend.Spec.Metadata,
}
// 5. Register backend with admin control plane
if err := r.syncToAdmin(ctx, backendConfig); err != nil {
return r.updateStatus(ctx, &backend, "Error", fmt.Sprintf("Admin sync failed: %v", err))
}
// 6. Update status
return r.updateStatus(ctx, &backend, "Discovered", endpoint)
}
Step 4: Developer deploys pattern runner
apiVersion: prism.io/v1alpha1
kind: PrismPattern
metadata:
name: order-consumer
namespace: applications
spec:
pattern: consumer
image: ghcr.io/prism/consumer-runner:latest
replicas: 2
# Backend bindings using discovered backends
backendBindings:
message_source:
backendRef: kafka-production
state_store:
backendRef: redis-production
dead_letter_queue:
backendRef: kafka-production
autoscaling:
enabled: true
scaler: keda
minReplicas: 2
maxReplicas: 50
triggers:
- type: kafka
metadata:
# KEDA automatically uses kafka-production connection details
lagThreshold: "1000"
Step 5: Pattern runner fetches backend config at startup
// In consumer-runner startup
func (r *ConsumerRunner) Start(ctx context.Context, config *PatternConfig) error {
// Fetch backend configs from admin
backendName := config.BackendBindings["state_store"] // "redis-production"
backend, err := r.adminClient.GetBackend(ctx, backendName)
if err != nil {
return fmt.Errorf("failed to fetch backend %s: %w", backendName, err)
}
// Backend config is fully resolved with connection details
redisConfig := backend.Config.GetRedis()
log.Printf("Connecting to Redis at %s:%d", redisConfig.Host, redisConfig.Port)
// Connect using resolved config
redisClient := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", redisConfig.Host, redisConfig.Port),
Password: redisConfig.Password,
DB: int(redisConfig.Database),
})
r.stateStore = redisClient
return nil
}
Approach 2: Helm-Aware Auto-Discovery
Philosophy: Automatically discover backends from Helm releases, eliminating the need for manual PrismBackend creation.
Architecture
┌──────────────────────────────── ─────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ Helm Releases (any namespace) │
│ ├─ redis (bitnami/redis) │
│ ├─ postgresql (bitnami/postgresql) │
│ ├─ kafka (bitnami/kafka) │
│ └─ nats (nats/nats) │
│ │ │
│ │ Helm List │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Prism Backend Discovery Controller │ │
│ │ (runs as part of prism-operator) │ │
│ │ │ │
│ │ Periodically: │ │
│ │ 1. helm list --all-namespaces │ │
│ │ 2. Detect known charts (bitnami/redis, etc.) │ │
│ │ 3. Extract service names and secrets │ │
│ │ 4. Auto-create PrismBackend CRDs │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Creates │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Auto-Discovered PrismBackend CRDs │ │
│ │ (labeled: prism.io/auto-discovered: "true") │ │
│ │ │ │
│ │ redis-infrastructure │ │
│ │ postgresql-infrastructure │ │
│ │ kafka-infrastructure │ │
│ │ nats-infrastructure │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Helm Chart Detection Patterns
// Known Helm chart patterns for auto-discovery
type HelmChartPattern struct {
ChartName string
BackendType string
ServicePattern string // Template for service name
PortDefaults map[string]int32 // Default ports
SecretPattern string // Template for secret name
SecretPasswordKey string // Key in secret for password
Capabilities []string // Backend capabilities
}
var knownCharts = []HelmChartPattern{
{
ChartName: "bitnami/redis",
BackendType: "redis",
ServicePattern: "{{.Release}}-master",
PortDefaults: map[string]int32{"redis": 6379},
SecretPattern: "{{.Release}}",
SecretPasswordKey: "redis-password",
Capabilities: []string{"keyvalue_basic", "keyvalue_scan", "keyvalue_ttl", "pubsub_basic"},
},
{
ChartName: "bitnami/postgresql",
BackendType: "postgresql",
ServicePattern: "{{.Release}}-postgresql",
PortDefaults: map[string]int32{"postgresql": 5432},
SecretPattern: "{{.Release}}-postgresql",
SecretPasswordKey: "postgres-password",
Capabilities: []string{"keyvalue_basic", "keyvalue_scan", "keyvalue_transactional", "sql_basic"},
},
{
ChartName: "bitnami/kafka",
BackendType: "kafka",
ServicePattern: "{{.Release}}",
PortDefaults: map[string]int32{"kafka": 9092},
SecretPattern: "", // Bitnami Kafka may not use secrets by default
SecretPasswordKey: "",
Capabilities: []string{"pubsub_basic", "pubsub_persistent", "stream_basic"},
},
{
ChartName: "nats/nats",
BackendType: "nats",
ServicePattern: "{{.Release}}",
PortDefaults: map[string]int32{"client": 4222, "monitoring": 8222},
SecretPattern: "{{.Release}}-auth",
SecretPasswordKey: "password",
Capabilities: []string{"pubsub_basic", "pubsub_persistent", "stream_basic"},
},
}
// Auto-discovery controller
func (c *BackendDiscoveryController) discoverFromHelm(ctx context.Context) error {
// 1. List all Helm releases
releases, err := c.listHelmReleases(ctx)
if err != nil {
return fmt.Errorf("failed to list Helm releases: %w", err)
}
// 2. Match releases against known patterns
for _, release := range releases {
pattern := c.matchHelmChart(release.Chart)
if pattern == nil {
continue // Unknown chart, skip
}
// 3. Extract service and secret names
serviceName := c.renderTemplate(pattern.ServicePattern, release)
secretName := c.renderTemplate(pattern.SecretPattern, release)
// 4. Check if PrismBackend already exists
backendName := fmt.Sprintf("%s-%s", release.Name, release.Namespace)
if c.backendExists(ctx, backendName) {
continue // Already discovered
}
// 5. Create PrismBackend CRD
backend := &prismv1alpha1.PrismBackend{
ObjectMeta: metav1.ObjectMeta{
Name: backendName,
Labels: map[string]string{
"prism.io/auto-discovered": "true",
"prism.io/helm-release": release.Name,
"prism.io/helm-chart": release.Chart,
},
},
Spec: prismv1alpha1.PrismBackendSpec{
Type: pattern.BackendType,
Discovery: prismv1alpha1.DiscoverySpec{
Service: prismv1alpha1.ServiceDiscovery{
Name: serviceName,
Namespace: release.Namespace,
Port: pattern.PortDefaults[pattern.BackendType],
},
Secret: &prismv1alpha1.SecretDiscovery{
Name: secretName,
Namespace: release.Namespace,
PasswordKey: pattern.SecretPasswordKey,
},
},
Capabilities: pattern.Capabilities,
Metadata: map[string]string{
"helm_release": release.Name,
"helm_chart": release.Chart,
"helm_version": release.Version,
"namespace": release.Namespace,
},
},
}
if err := c.Create(ctx, backend); err != nil {
log.Printf("Failed to create auto-discovered backend %s: %v", backendName, err)
} else {
log.Printf("✅ Auto-discovered backend: %s (from Helm release %s/%s)",
backendName, release.Namespace, release.Name)
}
}
return nil
}
Developer Experience
Platform team deploys backends:
# Deploy standard backends
helm install redis bitnami/redis -n infrastructure
helm install postgresql bitnami/postgresql -n infrastructure
helm install kafka bitnami/kafka -n infrastructure
Auto-discovery runs automatically (no manual steps needed):
# Prism operator auto-discovers and creates:
# - redis-infrastructure
# - postgresql-infrastructure
# - kafka-infrastructure
kubectl get prismbackend
# NAME TYPE STATUS AGE
# redis-infrastructure redis Discovered 1m
# postgresql-infrastructure postgresql Discovered 1m
# kafka-infrastructure kafka Discovered 1m
Developer deploys pattern runner:
apiVersion: prism.io/v1alpha1
kind: PrismPattern
metadata:
name: order-consumer
spec:
pattern: consumer
image: ghcr.io/prism/consumer-runner:latest
# Use auto-discovered backends
backendBindings:
message_source:
backendRef: kafka-infrastructure
state_store:
backendRef: redis-infrastructure
Approach 3: Sidecar-Based Service Discovery
Philosophy: Use a sidecar container to perform service discovery and generate config, keeping pattern runners decoupled from Kubernetes APIs.
Architecture
┌────────────────────────────────────────────────────────────┐
│ Pattern Runner Pod │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Init Container: prism-backend-resolver │ │
│ │ │ │
│ │ 1. Reads backend bindings from pod annotations │ │
│ │ 2. Queries K8s API for services and secrets │ │
│ │ 3. Resolves DNS names and ports │ │
│ │ 4. Fetches secrets for auth │ │
│ │ 5. Writes resolved config to shared volume │ │
│ │ /config/backends.json │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ (shared volume) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Main Container: consumer-runner │ │
│ │ │ │
│ │ 1. Reads /config/backends.json │ │
│ │ 2. Parses backend configs │ │
│ │ 3. Connects to backends │ │
│ │ 4. Starts processing │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
Generated Pattern Runner Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-consumer
spec:
template:
metadata:
annotations:
# Backend bindings encoded as annotations
prism.io/backend-bindings: |
message_source:
service: kafka.infrastructure.svc.cluster.local:9092
secret: kafka-credentials
state_store:
service: redis-master.infrastructure.svc.cluster.local:6379
secret: redis
spec:
# Init container resolves backends
initContainers:
- name: backend-resolver
image: ghcr.io/prism/backend-resolver:latest
volumeMounts:
- name: backend-config
mountPath: /config
env:
- name: BACKEND_BINDINGS
valueFrom:
fieldRef:
fieldPath: metadata.annotations['prism.io/backend-bindings']
# Main pattern runner container
containers:
- name: consumer-runner
image: ghcr.io/prism/consumer-runner:latest
volumeMounts:
- name: backend-config
mountPath: /config
env:
- name: BACKEND_CONFIG_PATH
value: /config/backends.json
volumes:
- name: backend-config
emptyDir: {}
Resolved Backend Config
The init container writes /config/backends.json:
{
"message_source": {
"type": "kafka",
"config": {
"brokers": ["kafka.infrastructure.svc.cluster.local:9092"],
"security_protocol": "PLAINTEXT"
},
"capabilities": ["pubsub_basic", "pubsub_persistent"]
},
"state_store": {
"type": "redis",
"config": {
"host": "redis-master.infrastructure.svc.cluster.local",
"port": 6379,
"password": "supersecret",
"database": 0
},
"capabilities": ["keyvalue_basic", "keyvalue_scan", "keyvalue_ttl"]
}
}
Pattern runner reads this config at startup:
func (r *ConsumerRunner) Start(ctx context.Context) error {
// Read resolved backend config from sidecar
configPath := os.Getenv("BACKEND_CONFIG_PATH")
if configPath == "" {
configPath = "/config/backends.json"
}
configData, err := os.ReadFile(configPath)
if err != nil {
return fmt.Errorf("failed to read backend config: %w", err)
}
var backends map[string]BackendConfig
if err := json.Unmarshal(configData, &backends); err != nil {
return fmt.Errorf("failed to parse backend config: %w", err)
}
// Connect to backends
for slotName, backendConfig := range backends {
if err := r.bindSlot(slotName, backendConfig); err != nil {
return fmt.Errorf("failed to bind slot %s: %w", slotName, err)
}
}
return nil
}
Approach 4: Service Catalog / Crossplane Integration
Philosophy: Leverage existing Kubernetes service binding standards (Service Catalog, Crossplane) for maximum ecosystem compatibility.
Architecture Using Crossplane
# Crossplane Composition for Prism Backend
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: prism-redis-backend
spec:
compositeTypeRef:
apiVersion: prism.crossplane.io/v1alpha1
kind: XPrismBackend
resources:
# 1. Provision Redis (if not exists)
- name: redis-instance
base:
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
spec:
forProvider:
manifest:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
spec:
chart:
spec:
chart: redis
version: "19.0.0"
sourceRef:
kind: HelmRepository
name: bitnami
# 2. Create service binding
- name: backend-binding
base:
apiVersion: servicebinding.io/v1alpha3
kind: ServiceBinding
spec:
service:
apiVersion: v1
kind: Service
name: redis-master
workload:
apiVersion: prism.io/v1alpha1
kind: PrismPattern
# Developer creates backend claim
apiVersion: prism.crossplane.io/v1alpha1
kind: PrismBackend
metadata:
name: redis-production
spec:
parameters:
type: redis
highAvailability: true
memorySize: 4Gi
compositionSelector:
matchLabels:
provider: bitnami
Benefits:
- Standard K8s service binding (servicebinding.io)
- Can provision AND bind backends
- Works with cloud-managed services (RDS, ElastiCache)
- Ecosystem compatibility
Trade-offs:
- Requires Crossplane installation
- More complex setup
- May be overkill for simple use cases
Comparison of Approaches
| Aspect | Approach 1: CRD | Approach 2: Helm-Aware | Approach 3: Sidecar | Approach 4: Crossplane |
|---|---|---|---|---|
| Complexity | Medium | Medium | Low | High |
| K8s Native | ✅ Yes | ✅ Yes | ⚠️ Init container | ✅ Yes |
| Auto-Discovery | ❌ Manual CRD | ✅ Automatic | ⚠️ Annotations | ✅ Automatic |
| Flexibility | ✅ High | ⚠️ Helm-only | ✅ High | ✅ Very High |
| Admin Control | ✅ Centralized | ✅ Centralized | ❌ Decentralized | ✅ Centralized |
| Secret Management | ✅ K8s secrets | ✅ K8s secrets | ✅ K8s secrets | ✅ K8s secrets |
| Ecosystem Integration | ⚠️ Custom | ⚠️ Custom | ⚠️ Custom | ✅ Standard |
| Maintenance | Medium | Medium | Low | High |
| Local Testing | ✅ kind/Docker Desktop | ✅ kind/Docker Desktop | ✅ kind/Docker Desktop | ⚠️ Requires Crossplane |
| Cloud Services | ⚠️ Future work | ⚠️ Future work | ⚠️ Future work | ✅ Native |
Recommended Approach: Hybrid (Approach 1 + 2)
Combine PrismBackend CRD with Helm auto-discovery for best of both worlds:
- PrismBackend CRD provides the core abstraction (Approach 1)
- Helm auto-discovery creates PrismBackend CRDs automatically (Approach 2)
- Manual CRD creation supported for custom backends
- Admin sync to RFC-039 backend registry
Implementation Architecture
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ Helm Releases │
│ ├─ redis (bitnami) │
│ ├─ postgresql (bitnami) │
│ └─ kafka (bitnami) │
│ │ │
│ ▼ (auto-discovery) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Prism Operator │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Helm Discovery Controller │ │ │
│ │ │ - Detects Helm releases │ │ │
│ │ │ - Creates PrismBackend CRDs │ │ │
│ │ │ - Labels: auto-discovered: true │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ PrismBackend Controller │ │ │
│ │ │ - Validates service/secret exists │ │ │
│ │ │ - Resolves DNS endpoints │ │ │
│ │ │ - Syncs to admin backend registry │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ PrismPattern Controller │ │ │
│ │ │ - Creates Deployment │ │ │
│ │ │ - Injects backend bindings as env vars │ │ │
│ │ │ - Sets up KEDA triggers with backend info │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └────────────────────────────────── ────────────────────┘ │
│ │
│ PrismBackend CRDs │
│ ├─ redis-infrastructure (auto) │
│ ├─ postgresql-infrastructure (auto) │
│ ├─ kafka-infrastructure (auto) │
│ └─ custom-memcached (manual) │
│ │ │
│ ▼ (synced to) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Prism Admin Control Plane │ │
│ │ - Backend Registry (RFC-039) │ │