Skip to content

Commit 0922147

Browse files
Atralupusclaude
andauthored
feat(ragnarok-breaker): add gs-gateway proxy for V8 connection (#3342)
Workers were each opening their own V8 GameServer SDK connection, which kicked both ends with duplicate_connection (4009) and crashed the SDK on the next emitter.emit. Introduce a singleton gs-gateway HTTP service that owns the V8 connection; workers now RPC over http://gs-gateway:3000. - New Deployment/Service `gs-gateway` (replicas=1, strategy=Recreate, liveness/readiness on /health:3000). HPA must never be attached — scaling beyond 1 reintroduces the duplicate-connection problem. - Worker Deployments gain GS_GATEWAY_URL env and an initContainer that waits for gs-gateway /health before the worker process starts. - gs-gateway reuses the existing ragnarok-breaker-env secret (AGENT8_* values) and the ragnarok-breaker-regcred image pull secret. AGENT8_AUTH in AWS Secrets Manager must be rotated from the short-lived game token to a long-lived V8 access token JWT; gs-gateway exchanges it for a game token at boot and refreshes every ~25 min. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4605f06 commit 0922147

3 files changed

Lines changed: 122 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{{- if .Values.gsGateway.enabled }}
2+
apiVersion: v1
3+
kind: Service
4+
metadata:
5+
name: gs-gateway
6+
namespace: {{ .Release.Namespace }}
7+
labels:
8+
app: gs-gateway
9+
app.kubernetes.io/instance: {{ .Release.Name }}
10+
app.kubernetes.io/component: gs-gateway
11+
spec:
12+
type: ClusterIP
13+
selector:
14+
app: gs-gateway
15+
ports:
16+
- name: http
17+
port: {{ .Values.gsGateway.port }}
18+
targetPort: {{ .Values.gsGateway.port }}
19+
protocol: TCP
20+
---
21+
apiVersion: apps/v1
22+
kind: Deployment
23+
metadata:
24+
name: gs-gateway
25+
namespace: {{ .Release.Namespace }}
26+
labels:
27+
app: gs-gateway
28+
app.kubernetes.io/instance: {{ .Release.Name }}
29+
app.kubernetes.io/component: gs-gateway
30+
spec:
31+
replicas: {{ .Values.gsGateway.replicas }}
32+
strategy:
33+
type: Recreate
34+
selector:
35+
matchLabels:
36+
app: gs-gateway
37+
template:
38+
metadata:
39+
labels:
40+
app: gs-gateway
41+
spec:
42+
{{- if .Values.imagePullSecret.secretKey }}
43+
imagePullSecrets:
44+
- name: {{ .Values.imagePullSecret.name }}
45+
{{- end }}
46+
containers:
47+
- name: gs-gateway
48+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
49+
imagePullPolicy: {{ .Values.image.pullPolicy }}
50+
command:
51+
{{- toYaml .Values.gsGateway.command | nindent 12 }}
52+
ports:
53+
- name: http
54+
containerPort: {{ .Values.gsGateway.port }}
55+
env:
56+
- name: GS_GATEWAY_HOST
57+
value: {{ .Values.gsGateway.host | quote }}
58+
- name: GS_GATEWAY_PORT
59+
value: {{ .Values.gsGateway.port | quote }}
60+
envFrom:
61+
- secretRef:
62+
name: ragnarok-breaker-env
63+
livenessProbe:
64+
{{- toYaml .Values.gsGateway.livenessProbe | nindent 12 }}
65+
readinessProbe:
66+
{{- toYaml .Values.gsGateway.readinessProbe | nindent 12 }}
67+
resources:
68+
{{- toYaml .Values.gsGateway.resources | nindent 12 }}
69+
{{- with .Values.nodeSelector }}
70+
nodeSelector:
71+
{{- toYaml . | nindent 8 }}
72+
{{- end }}
73+
restartPolicy: Always
74+
{{- end }}

charts/ragnarok-breaker/templates/worker-deployments.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,28 @@ spec:
2525
imagePullSecrets:
2626
- name: {{ $.Values.imagePullSecret.name }}
2727
{{- end }}
28+
{{- if and $.Values.gsGateway.enabled $.Values.gsGateway.waitForReady }}
29+
initContainers:
30+
- name: wait-for-gs-gateway
31+
image: busybox:1.36
32+
command:
33+
- sh
34+
- -c
35+
- |
36+
until wget -q -O /dev/null {{ $.Values.gsGateway.serviceUrl }}/health; do
37+
echo "waiting for gs-gateway..."
38+
sleep 2
39+
done
40+
{{- end }}
2841
containers:
2942
- name: worker
3043
image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag }}"
3144
imagePullPolicy: {{ $.Values.image.pullPolicy }}
3245
command:
3346
{{- toYaml $w.command | nindent 12 }}
47+
env:
48+
- name: GS_GATEWAY_URL
49+
value: {{ $.Values.gsGateway.serviceUrl | quote }}
3450
envFrom:
3551
- secretRef:
3652
name: ragnarok-breaker-env

charts/ragnarok-breaker/values.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,38 @@ imagePullSecret:
99
name: ragnarok-breaker-regcred
1010
secretKey: ""
1111

12+
# Single-instance HTTP proxy that owns the V8 GameServer connection.
13+
# NEVER scale beyond 1 — duplicate connections cause V8 to kick both with 4009.
14+
gsGateway:
15+
enabled: true
16+
replicas: 1
17+
command: ["bun", "run", "worker/src/entry/gs-gateway.entry.ts"]
18+
host: "0.0.0.0"
19+
port: 3000
20+
serviceUrl: "http://gs-gateway:3000"
21+
waitForReady: true
22+
resources:
23+
requests:
24+
cpu: 50m
25+
memory: 128Mi
26+
limits:
27+
cpu: 500m
28+
memory: 512Mi
29+
livenessProbe:
30+
httpGet:
31+
path: /health
32+
port: 3000
33+
initialDelaySeconds: 30
34+
periodSeconds: 15
35+
failureThreshold: 3
36+
readinessProbe:
37+
httpGet:
38+
path: /health
39+
port: 3000
40+
initialDelaySeconds: 10
41+
periodSeconds: 10
42+
failureThreshold: 3
43+
1244
workers:
1345
scheduler:
1446
enabled: true

0 commit comments

Comments
 (0)