Unreleased documentation. Choose your installed release in the version menu. Features described here may be absent from that release.
Deployment diagrams¶
Kubernetes Deployment Architecture¶
graph TB
subgraph "Kubernetes Cluster"
API[Kubernetes API Server]
subgraph "haptic Namespace (release namespace)"
subgraph "Controller Deployment (2 replicas, leader-elected)"
CTRL1[Controller Pod 1<br/>leader]
CTRL2[Controller Pod 2<br/>hot standby]
end
HTPLCFG[HAProxyTemplateConfig CRD<br/>Templates, watched resources, settings]
CREDS[Secret<br/>agent credentials]
CTRL_SVC[Controller Service<br/>ClusterIP<br/>:8080 healthz<br/>:9090 metrics<br/>:9443 webhook]
subgraph "HAProxy Deployment (2+ replicas)"
subgraph "haproxy pod A"
HAP1[HAProxy<br/>:80, :443, :8404]
AG1[HAPTIC agent<br/>:5555]
end
subgraph "haproxy pod B"
HAP2[HAProxy<br/>:80, :443, :8404]
AG2[HAPTIC agent<br/>:5555]
end
end
HAP_SVC[HAProxy Service<br/>NodePort by default<br/>:80 → :80<br/>:443 → :443]
end
subgraph "Application Namespaces"
ING[Ingress / HTTPRoute / GRPCRoute]
APPSVC[Services + EndpointSlices]
PODS[Application Pods]
end
end
USERS[External Users] --> HAP_SVC
HAP_SVC --> HAP1
HAP_SVC --> HAP2
CTRL_SVC --> CTRL1 & CTRL2
API --> CTRL1 & CTRL2
HTPLCFG --> CTRL1 & CTRL2
CREDS --> CTRL1 & CTRL2
ING -.Watch.-> CTRL1 & CTRL2
APPSVC -.Watch.-> CTRL1 & CTRL2
CTRL1 --> AG1 & AG2
AG1 --> HAP1
AG2 --> HAP2
HAP1 --> PODS
HAP2 --> PODS
Deployment Components:
-
Controller Deployment — defaults to 2 replicas with leader election
- All replicas watch Kubernetes resources, run admission webhooks, and discover HAProxy pods and keep their render graphs warm
- Only the elected leader applies configuration through each pod's HAPTIC agent
- See High Availability for tuning failover and Leader Election for the full all-replica vs leader-only component split
-
Controller Service (ClusterIP) — operational endpoints only
:8080→ healthz probes;/debug/*accepts loopback connections through port-forwarding:9090→ Prometheus metrics:9443→ validating webhook
-
HAProxy Deployment (not StatefulSet) — scales horizontally
- Each pod runs HAProxy plus the HAPTIC agent, sharing the config volume
- Pods are auto-discovered via
controller.config.podSelector; a pod is admitted once it has an IP, itsagentcontainer is running, and itsGET /v1/stateanswers
-
HAProxy Service — NodePort by default; set
haproxy.service.type: LoadBalancerfor cloud providers- Service port 80 maps to HAProxy container port 80, service port 443 maps to 443 (the chart binds HAProxy on the literal 80/443; set
haproxy.ports.http/httpsto override)
- Service port 80 maps to HAProxy container port 80, service port 443 maps to 443 (the chart binds HAProxy on the literal 80/443; set
-
HAProxyTemplateConfig CRD — holds every piece of configuration the controller needs
- Template bodies (
haproxyConfig,templateSnippets,maps,files,sslCertificates) watchedResources(what to subscribe to and how to index it)- Apply tuning (
minDeploymentInterval,driftPreventionInterval, storage paths) - Validation tests shipped alongside the templates
- Template bodies (
-
Agent identity Secrets — separate client and server certificates authenticate the default mutual TLS connection. Both peers reload changed certificates. The bootstrap credentials Secret remains required; its username and password are used only by the explicit legacy HTTP transport.
Container Architecture¶
graph TB
subgraph "Controller Pod"
CTRL_MAIN[Controller Process<br/>:8080 healthz + /debug<br/>:9090 metrics<br/>:9443 webhook]
CTRL_TMP["/tmp emptyDir<br/>haproxy -c validation"]
end
subgraph "HAProxy Pod (Deployment member)"
HAP_PROC[HAProxy Process<br/>:80 HTTP<br/>:443 HTTPS<br/>:8404 Stats]
AGENT_PROC[HAPTIC agent<br/>:5555 API<br/>Unix master + worker sockets]
HAP_VOL[Shared config emptyDir<br/>/etc/haproxy<br/>maps/, ssl/, general/]
end
HTPLCFG[HAProxyTemplateConfig CRD] -. watch .-> CTRL_MAIN
CREDS_SECRET[Credentials Secret] -. watch .-> CTRL_MAIN
CTRL_TMP --> CTRL_MAIN
AGENT_PROC <-. master + worker sockets .-> HAP_PROC
HAP_VOL --> HAP_PROC
HAP_VOL --> AGENT_PROC
Resource requirements: use the resource sizing guide for chart defaults and planning estimates. The controller writes transient haproxy -c validation files to a /tmp emptyDir because its root filesystem is read-only. HAProxy and its agent share the config emptyDir mounted at /etc/haproxy.
Network topology¶
graph LR
subgraph "External Network"
INET[Internet]
end
subgraph "Kubernetes Cluster Network"
HAP_LB[HAProxy Service<br/>LoadBalancer<br/>External IP]
CTRL_SVC_NET[Controller Service<br/>ClusterIP<br/>10.96.0.10]
subgraph "Pod Network"
subgraph "Controller Pod<br/>10.0.0.10"
CTRL[Controller Process<br/>:8080, :9090]
end
subgraph "HAProxy Instances"
subgraph "haproxy pod A<br/>10.0.1.10"
HAP1[HAProxy Process<br/>:80, :443, :8404]
AG1[HAPTIC agent<br/>:5555]
end
subgraph "haproxy pod B<br/>10.0.1.11"
HAP2[HAProxy Process<br/>:80, :443, :8404]
AG2[HAPTIC agent<br/>:5555]
end
end
subgraph "Application Pods"
APP1[app-pod-1<br/>10.0.2.10]
APP2[app-pod-2<br/>10.0.2.11]
end
end
KUBE_API[Kubernetes API<br/>443]
PROM_NET[Prometheus]
end
INET --> HAP_LB
HAP_LB --> HAP1
HAP_LB --> HAP2
CTRL_SVC_NET --> CTRL
PROM_NET --> CTRL_SVC_NET
CTRL --> KUBE_API
CTRL --> AG1
CTRL --> AG2
HAP1 --> APP1
HAP1 --> APP2
HAP2 --> APP1
HAP2 --> APP2
AG1 -.sockets.-> HAP1
AG2 -.sockets.-> HAP2
Network Flow:
- Ingress Traffic: Internet → HAProxy Service → HAProxy Pods → Application Pods (the diagram shows the
haproxy.service.type: LoadBalancervariant; the chart default is NodePort) - Control Plane: Controller → Kubernetes API (resource watching)
- Configuration apply: Controller → each pod's agent (HTTPS with mutual TLS by default)
- Service Discovery: Controller watches HAProxy pods via Kubernetes API
- Monitoring: Prometheus → Controller Service (ClusterIP) → Controller Pod (metrics endpoint)
- Health Checks: Kubernetes → Controller Service → Controller Pod (healthz endpoint)
Scaling Considerations: HAProxy scales horizontally via haproxy.replicaCount (pods are auto-discovered through controller.config.podSelector); the controller scales for availability, not throughput — see Performance — Scaling Strategies and High Availability. NetworkPolicy must allow the controller to reach the agent on port 5555 on each HAProxy pod (Networking).
Build optimizations (contributors)¶
Controller images use Go's Profile-Guided Optimization (PGO) to optimize frequently called functions. The benefit depends on how closely the profile matches the workload. A baseline CPU profile (cmd/haptic/default.pgo) is committed to the repository; Go automatically uses it during builds to optimize hot paths.
Updating the profile from the development environment:
-
Start the dev environment:
-
Port-forward to the controller's debug port:
-
Generate workload (trigger reconciliation by modifying resources).
-
Collect a 30-second CPU profile:
-
Rebuild with the new profile:
For optimal results, collect profiles from production during representative workloads and merge multiple profiles for broader coverage: