Files
jarvis-selfhost/kubernetes/05-web.yaml
T
2026-08-27 17:52:23 +02:00

153 lines
5.3 KiB
YAML

apiVersion: apps/v1
kind: Deployment
metadata:
name: jarvis-web
namespace: jarvis
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: jarvis
app.kubernetes.io/component: web
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app.kubernetes.io/name: jarvis
app.kubernetes.io/component: web
spec:
# Nothing here holds a long-lived connection of its own — the websockets go to the API — so
# this is only about letting in-flight responses finish.
terminationGracePeriodSeconds: 30
# Required by the `restricted` Pod Security Standard that 00-namespace.yaml enforces.
securityContext:
seccompProfile:
type: RuntimeDefault
# Same reasoning as 04-api.yaml: the scheduler will happily put both replicas on one node,
# and a pair that shares a node is a pair that shares a failure. Cheaper to satisfy here —
# nothing about serving static files pins a pod anywhere.
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: jarvis
app.kubernetes.io/component: web
containers:
- name: web
# By digest, like the API. It matters more here than it looks: the bundle's JavaScript
# chunks are content-hashed PER BUILD and answer 404 rather than falling through to
# index.html, so during a rollout a browser holding the previous shell can fail a lazy
# import. Two pods on two different builds widen that window; two pods on one digest do
# not have it at all.
image: git.luxit.be/luxit/jarvis-web@sha256:REPLACE_ME
ports:
- name: http
containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
envFrom:
- configMapRef:
name: jarvis-web-config
# Is this nginx answering? Nothing more. Restarting the web container because the API is
# down would take out the one thing still able to show an error page.
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
failureThreshold: 3
# Can THIS pod reach the API? It proxies /api/health/live, not /ready, on purpose: the
# question is whether this pod's route works — a broken DNS name, a NetworkPolicy — and
# not whether the API's dependencies are healthy. Pointed at /ready, one Postgres blip
# would fail every web pod at once and take the console offline for a fault that has
# nothing to do with serving a bundle.
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
securityContext:
# uid 101 is the `nginx` user the image already ships and the Dockerfile switches to.
runAsNonRoot: true
runAsUser: 101
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
volumeMounts:
# With a read-only root filesystem, the three paths this container writes at runtime
# have to be given to it. All three are ephemeral by nature — the substituted config is
# regenerated at every start from the template and the ConfigMap.
- name: nginx-conf
mountPath: /etc/nginx/conf.d
- name: nginx-cache
mountPath: /var/cache/nginx
- name: tmp
# The pid file lives here rather than in /var/run, which the nginx user does not own.
mountPath: /tmp
volumes:
- name: nginx-conf
emptyDir: {}
- name: nginx-cache
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: jarvis-web
namespace: jarvis
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: jarvis
app.kubernetes.io/component: web
ports:
- name: http
port: 8080
targetPort: http
---
# Web scales on CPU; the API deliberately does not.
#
# Every API scale-in kills a pod holding agent websockets, live assistant runs and open shells —
# the fleet reconnects, runs are interrupted and resumed, terminals die — and CPU is a poor proxy
# for a load made almost entirely of long-lived connections. Raise `jarvis-api` deliberately
# instead. If it ever does get an HPA, give it a scale-down stabilization window of ten minutes or
# more.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: jarvis-web
namespace: jarvis
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: jarvis-web
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70