# ============================================================================= # AI Software Factory — Worker (BullMQ) Deployment + HPA # # Consumes job queues from Redis (search indexing, file pipeline, automation # execution, email, webhooks). 2 replicas by default. # # HPA NOTE — custom queue-depth metric: # The autoscaler below scales on the custom metric `queue_depth` (the # EXPOSED name of the OTel gauge the worker itself emits — TASK-0135: # packages/observability `queue_depth{queue}`, scraped by Prometheus at # /metrics:4003 per queue every 15 s). # For the metric to reach the autoscaler you need: # 1. The Prometheus scrape target (infrastructure/observability/prometheus/ # scrape.yaml, job `factory-worker`) — shipped with this task. # 2. The Prometheus Adapter (k8s) with an `APIService` mapping the custom # metric, e.g. configmap adapter rules: # --- # rules: # - seriesQuery: queue_depth{queue=~".+"} # resources: {service: worker} # name: {as: queueDepth} # metricsQuery: | # sum by (queue) (queue_depth{namespace="{{.Namespace}}",service_name="factory-worker"}) # (adjust the metricsQuery to your exporter's labels) # Without the adapter, replace the metric with a CPU target (commented at # the bottom) so the HPA still functions. # ============================================================================= apiVersion: apps/v1 kind: Deployment metadata: name: worker labels: app.kubernetes.io/name: worker app.kubernetes.io/component: jobs spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: worker strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 template: metadata: labels: app.kubernetes.io/name: worker annotations: checksum/config: "factory-config" spec: containers: - name: worker image: ghcr.io/ai-software-factory/worker:TAG imagePullPolicy: IfNotPresent # Workers have no HTTP port; liveness is a process heartbeat file # maintained by the worker (written each loop tick). Adjust the # path if your worker uses a different heartbeat mechanism. envFrom: - configMapRef: name: factory-config - secretRef: name: factory-secrets resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gi livenessProbe: exec: command: - sh - -c - "test -f /tmp/worker.heartbeat && [ $(( $(date +%s) - $(cat /tmp/worker.heartbeat) )) -lt 90 ]" initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 3 securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 seccompProfile: type: RuntimeDefault terminationGracePeriodSeconds: 60 # let in-flight jobs finish --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: worker spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: worker minReplicas: 2 maxReplicas: 8 metrics: # Preferred: scale on backlog. Requires the Prometheus Adapter setup # documented in the header of this file. - type: Pods pods: metric: name: queue_depth target: type: AverageValue averageValue: "50" # 50 pending jobs per worker pod → scale up # Fallback (uncomment if the custom metric is not wired yet): # - type: Resource # resource: # name: cpu # target: # type: Utilization # averageUtilization: 70