Files
jarvis-selfhost/kubernetes/06-ingress.yaml
T
2026-10-21 12:12:12 +02:00

95 lines
4.6 KiB
YAML

# One hostname, two backends.
#
# The API is routed STRAIGHT AT ITS SERVICE rather than through the web container's nginx, which is
# what that nginx does on Compose. Two reasons: it removes a hop from every API request and every
# websocket frame, and it makes the web pod a pure static server again — no dependency on resolving
# the API, nothing to keep in sync between an ingress and a config file.
#
# The consequence has to be applied, not just noted: there is now exactly ONE proxy rewriting
# X-Forwarded-For, so `TRUST_PROXY_HOPS` is 1. It is 2 in the compose files, and leaving it there
# would make `req.ip` a value the caller chooses. See 02-config.yaml.
#
# Set `externalTrafficPolicy: Local` on the ingress controller's own Service as well, or the node's
# SNAT will make every request in the world appear to come from a handful of addresses — which
# defeats every per-address limit in the application at once.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jarvis
namespace: jarvis
# ONE annotations map. This file used to carry two `annotations:` keys under metadata — the
# sync-wave above the name, the proxy settings below it — and tools disagree about a duplicated
# key: kustomize merged the two, while `kubectl apply -f` kept only the second and dropped the
# sync-wave without a warning. What a cluster received depended on which tool rendered the file.
annotations:
# Last, so that the first traffic admitted from outside meets Services with endpoints behind
# them. An Ingress applied early is a route to a 503.
argocd.argoproj.io/sync-wave: "2"
# An agent is idle between operations and the server pings every 30s, so this only has to
# outlast a quiet period comfortably. Too short and a healthy fleet reconnects all day.
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
# Matches the API's own ceiling on a JSON body, `BodyLimit.MaxBytes` in
# src/Jarvis.Api/Common/BodyLimit.cs: 4 MiB, and nginx's `m` is MiB. A document may hold 400,000
# characters, which is over a megabyte once accented text is UTF-8 encoded, and a diagram export
# posts back the SVG the browser rendered. Below this they are rejected as a bare 413 with no
# message.
#
# File uploads need more and are NOT raised here: they post under /api/uploads/, which has its
# own Ingress in 06b-ingress-uploads.yaml, because ingress-nginx applies these annotations to
# every path of the object that carries them — 13376k here would be 13376k on every API route.
nginx.ingress.kubernetes.io/proxy-body-size: "4m"
#
# NO affinity annotation, and none should be added. The reflex is understandable — there are
# websockets here — but stickiness cannot solve the problem it would be hired for: the hard case
# is co-locating an operator's browser with an AGENT's websocket, which arrives from a different
# network at a different time and whose id is not even known at the browser's handshake. No
# ingress annotation can express that; it is handled in the application, over Redis.
#
# And there is no polling to make sticky: both the client and the server pin
# transports: ["websocket"], so one logical socket is one TCP connection.
spec:
ingressClassName: nginx
tls:
- hosts: ["jarvis.example.com"]
secretName: jarvis-tls
rules:
- host: jarvis.example.com
http:
paths:
# Longest prefix first for readers; the controller sorts by specificity itself.
#
# The agent websocket has its own rule even though it sits under /api/, mirroring the
# split in src/Jarvis.Web/nginx.conf: it is the one path where an idle connection
# legitimately stays open for an hour between frames.
- path: /api/agents/ws
pathType: Prefix
backend:
service:
name: jarvis-api
port:
name: http
- path: /socket.io/
pathType: Prefix
backend:
service:
name: jarvis-api
port:
name: http
- path: /api/
pathType: Prefix
backend:
service:
name: jarvis-api
port:
name: http
# Everything else is the SPA, whose nginx answers index.html for any path it does not
# have a file for.
- path: /
pathType: Prefix
backend:
service:
name: jarvis-web
port:
name: http