Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions k8s/app/dashboard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# The Relay dashboard on https://cdn.myrelay.tech: static files built by Vite and
# served by nginx. Deployment, Service and Ingress, like the API's own manifests in
# k8s/app/, with four differences: port 8080 instead of 80 (the unprivileged nginx
# image cannot bind a low port), probes so a rollout never sends traffic to a pod
# that is not serving yet, small resource requests because the VPS is shared with
# Postgres, the API and Grafana, and cdn.myrelay.tech instead of api.myrelay.tech.

apiVersion: apps/v1
kind: Deployment
metadata:
name: relay-dashboard
labels:
app.kubernetes.io/name: relay-dashboard
spec:
replicas: 2 # two pods, so a rollout always has one serving
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # never take a pod down before its replacement is ready
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: relay-dashboard # manage the pods that carry this label...
template:
metadata:
labels:
app.kubernetes.io/name: relay-dashboard # ...and give every new pod this label
spec:
containers:
- name: nginx
# Tag is the git SHA, set by the pipeline. Never "latest": a rollback has
# to name an exact build, and "latest" means something different tomorrow.
image: ghcr.io/devinder-dev/relay-dashboard:__GIT_SHA__
ports:
- containerPort: 8080
readinessProbe:
# Kubernetes sends no traffic here until this answers. Without it, a new
# pod receives requests while nginx is still starting.
httpGet:
path: /index.html
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
livenessProbe:
# If this stops answering, the pod is restarted.
httpGet:
path: /index.html
port: 8080
initialDelaySeconds: 10
periodSeconds: 20
# Before SIGTERM, wait 5 s, so Traefik stops sending this pod traffic first.
# Removing a pod from the Service and telling it to stop happen at the same moment,
# but Traefik needs a moment to hear about the removal. Without this pause nginx is
# already shutting down while Traefik still routes here, and those requests fail.
# terminationGracePeriodSeconds stays at its default 30 s, which comfortably holds
# these 5 s: nginx serves static files, so it has nothing to drain afterwards.
lifecycle:
preStop:
sleep:
seconds: 5
resources:
# Serving static files needs almost nothing. The requests exist so the
# scheduler knows this pod is cheap and leaves room for the database.
requests:
cpu: 10m
memory: 24Mi
limits:
memory: 64Mi
securityContext:
# Namespace relay enforces Pod Security "restricted" (k8s/platform/deployer.yaml),
# which requires all four of these. runAsNonRoot and seccompProfile are not
# optional hardening here: without them the API server refuses the pod, even
# though the image already runs as uid 101. Kubernetes does not look inside the
# image to check, so being non-root has to be declared.
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
allowPrivilegeEscalation: false
# Not required by "restricted". Left false because nginx writes its cache and
# pid file under /var; making it read-only would mean emptyDir mounts for both.
readOnlyRootFilesystem: false
capabilities:
drop: ["ALL"]
---
apiVersion: v1
kind: Service
metadata:
name: relay-dashboard
spec:
selector:
app.kubernetes.io/name: relay-dashboard # the label the Deployment gives its pods
ports:
- port: 80 # the Service listens on 80 inside the cluster...
targetPort: 8080 # ...and forwards to 8080 in the pod
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relay-dashboard
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- cdn.myrelay.tech
secretName: cdn-myrelay-tech-tls
rules:
- host: cdn.myrelay.tech
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relay-dashboard
port:
number: 80
Loading