# ============================================================================= # AI Software Factory — Realtime (WebSocket) Deployment + HPA # # 2 replicas. The ingress routes /ws here with proxy buffering disabled. # # STICKY SESSIONS NOTE: # WebSocket clients can be connected to ANY replica; cross-replica room # fan-out is achieved through Redis pub/sub (every replica subscribes to the # room channels), so strictly no sticky sessions are REQUIRED. # # If your realtime implementation is single-replica-room-only (no pub/sub # fan-out), pin each client to one replica with an nginx consistent-hash # annotation on the ingress: # # nginx.ingress.kubernetes.io/upstream-vhost: realtime # nginx.ingress.kubernetes.io/configuration-snippet: | # location /ws { # set $realtime "realtime"; # proxy_pass http://$realtime; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "upgrade"; # } # plus an upstream with: # upstream realtime { # server 10.x.x.x:4001; # ip_hash; # } # (ip_hash via a ConfigMap-mounted nginx template; plain Ingress annotations # cannot set upstream-level ip_hash.) # ============================================================================= apiVersion: apps/v1 kind: Deployment metadata: name: realtime labels: app.kubernetes.io/name: realtime app.kubernetes.io/component: realtime spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: realtime strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 template: metadata: labels: app.kubernetes.io/name: realtime annotations: checksum/config: "factory-config" spec: containers: - name: realtime image: ghcr.io/ai-software-factory/realtime:TAG imagePullPolicy: IfNotPresent ports: - containerPort: 4001 name: ws envFrom: - configMapRef: name: factory-config - secretRef: name: factory-secrets env: - name: REALTIME_PORT value: "4001" resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gi # WebSocket connections keep sockets open — TCP probe only. livenessProbe: tcpSocket: port: 4001 initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: tcpSocket: port: 4001 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 seccompProfile: type: RuntimeDefault # Long-lived sockets: give them time to drain on shutdown. terminationGracePeriodSeconds: 45 --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: realtime spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: realtime minReplicas: 1 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # For a connection-count-based scale you can add (with the Prometheus # Adapter, exposing a ws_connections gauge from the server): # - type: Pods # pods: # metric: # name: ws_connections # target: # type: AverageValue # averageValue: "2500"