1# =============================================================================
2# AI Software Factory — Realtime (WebSocket) Deployment + HPA
3#
4# 2 replicas. The ingress routes /ws here with proxy buffering disabled.
5#
6# STICKY SESSIONS NOTE:
7# WebSocket clients can be connected to ANY replica; cross-replica room
8# fan-out is achieved through Redis pub/sub (every replica subscribes to the
9# room channels), so strictly no sticky sessions are REQUIRED.
10#
11# If your realtime implementation is single-replica-room-only (no pub/sub
12# fan-out), pin each client to one replica with an nginx consistent-hash
13# annotation on the ingress:
14#
15# nginx.ingress.kubernetes.io/upstream-vhost: realtime
16# nginx.ingress.kubernetes.io/configuration-snippet: |
17# location /ws {
18# set $realtime "realtime";
19# proxy_pass http:
20# proxy_set_header Upgrade $http_upgrade;
21# proxy_set_header Connection "upgrade";
22# }
23# plus an upstream with:
24# upstream realtime {
25# server 10.x.x.x:4001;
26# ip_hash;
27# }
28# (ip_hash via a ConfigMap-mounted nginx template; plain Ingress annotations
29# cannot set upstream-level ip_hash.)
30# =============================================================================
31apiVersion: apps/v1
32kind: Deployment
33metadata:
34 name: realtime
35 labels:
36 app.kubernetes.io/name: realtime
37 app.kubernetes.io/component: realtime
38spec:
39 replicas: 2
40 selector:
41 matchLabels:
42 app.kubernetes.io/name: realtime
43 strategy:
44 type: RollingUpdate
45 rollingUpdate:
46 maxUnavailable: 0
47 maxSurge: 1
48 template:
49 metadata:
50 labels:
51 app.kubernetes.io/name: realtime
52 annotations:
53 checksum/config: "factory-config"
54 spec:
55 containers:
56 - name: realtime
57 image: ghcr.io/ai-software-factory/realtime:TAG
58 imagePullPolicy: IfNotPresent
59 ports:
60 - containerPort: 4001
61 name: ws
62 envFrom:
63 - configMapRef:
64 name: factory-config
65 - secretRef:
66 name: factory-secrets
67 env:
68 - name: REALTIME_PORT
69 value: "4001"
70 resources:
71 requests:
72 cpu: 250m
73 memory: 512Mi
74 limits:
75 cpu: "1"
76 memory: 1Gi
77 # WebSocket connections keep sockets open — TCP probe only.
78 livenessProbe:
79 tcpSocket:
80 port: 4001
81 initialDelaySeconds: 30
82 periodSeconds: 30
83 timeoutSeconds: 5
84 failureThreshold: 3
85 readinessProbe:
86 tcpSocket:
87 port: 4001
88 initialDelaySeconds: 10
89 periodSeconds: 10
90 timeoutSeconds: 3
91 failureThreshold: 3
92 securityContext:
93 allowPrivilegeEscalation: false
94 runAsNonRoot: true
95 runAsUser: 1000
96 runAsGroup: 1000
97 seccompProfile:
98 type: RuntimeDefault
99 # Long-lived sockets: give them time to drain on shutdown.
100 terminationGracePeriodSeconds: 45
101---
102apiVersion: autoscaling/v2
103kind: HorizontalPodAutoscaler
104metadata:
105 name: realtime
106spec:
107 scaleTargetRef:
108 apiVersion: apps/v1
109 kind: Deployment
110 name: realtime
111 minReplicas: 1
112 maxReplicas: 6
113 metrics:
114 - type: Resource
115 resource:
116 name: cpu
117 target:
118 type: Utilization
119 averageUtilization: 70
120 # For a connection-count-based scale you can add (with the Prometheus
121 # Adapter, exposing a ws_connections gauge from the server):
122 # - type: Pods
123 # pods:
124 # metric:
125 # name: ws_connections
126 # target:
127 # type: AverageValue
128 # averageValue: "2500"
129