82 lines
3.6 KiB
YAML
82 lines
3.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
|
|
annotations:
|
|
# 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 BODY_LIMIT in apps/api/src/main.ts. 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.
|
|
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 apps/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
|