Skip to content

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:

  1. 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
  2. Controller Service (ClusterIP) — operational endpoints only

    • :8080 → healthz probes; /debug/* accepts loopback connections through port-forwarding
    • :9090 → Prometheus metrics
    • :9443 → validating webhook
  3. 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, its agent container is running, and its GET /v1/state answers
  4. HAProxy Service — NodePort by default; set haproxy.service.type: LoadBalancer for 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/https to override)
  5. 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
  6. 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:

  1. Ingress Traffic: Internet → HAProxy Service → HAProxy Pods → Application Pods (the diagram shows the haproxy.service.type: LoadBalancer variant; the chart default is NodePort)
  2. Control Plane: Controller → Kubernetes API (resource watching)
  3. Configuration apply: Controller → each pod's agent (HTTPS with mutual TLS by default)
  4. Service Discovery: Controller watches HAProxy pods via Kubernetes API
  5. Monitoring: Prometheus → Controller Service (ClusterIP) → Controller Pod (metrics endpoint)
  6. 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:

  1. Start the dev environment:

    ./scripts/start-dev-env.sh
    
  2. Port-forward to the controller's debug port:

    kubectl -n haptic port-forward deploy/haptic-controller 8080:8080
    
  3. Generate workload (trigger reconciliation by modifying resources).

  4. Collect a 30-second CPU profile:

    make pgo-profile
    # Or manually:
    curl -o cmd/haptic/default.pgo http://localhost:8080/debug/pprof/profile?seconds=30
    
  5. Rebuild with the new profile:

    make build
    

For optimal results, collect profiles from production during representative workloads and merge multiple profiles for broader coverage:

make pgo-merge PROFILES='profile1.pgo profile2.pgo'
Found a problem on this page? Report it