deploy: track the k3s manifest and relax the probes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Liveness and readiness timeouts go from 1s to 5s, and a startup probe
covers OIDC discovery. Liveness only hits /healthz, so a database or
network blip no longer restarts the pod.
This commit is contained in:
2026-10-03 16:30:21 +02:00
parent 4821ddca1a
commit c37fb77be3
3 changed files with 112 additions and 1 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,4 @@
*.env *.env
deploy/k8s/ # Cluster manifests stay out of the repo, except the LMR deployment itself.
deploy/k8s/*
!deploy/k8s/lmr.yaml

View File

@@ -150,6 +150,25 @@ Docker/systemd, pass it with `env_file` / `EnvironmentFile` instead.
| `GET /healthz` | Liveness — process is up | | `GET /healthz` | Liveness — process is up |
| `GET /readyz` | Readiness — database reachable (pings the pool) | | `GET /readyz` | Readiness — database reachable (pings the pool) |
## Deploy
A push to `master` runs the Woodpecker pipeline in `.woodpecker.yml`. It builds
the root `Dockerfile` and pushes `registry.govekar.net/lovrog05/localmodelroutex`
with the tags `latest` and the commit SHA.
The gateway runs on k3s from [`deploy/k8s/lmr.yaml`](deploy/k8s/lmr.yaml). The
manifest expects `configmap/lmr-config`, `secret/lmr-secret` and the pull secret
`regcred2` to exist in the cluster. To roll out a new image:
```bash
kubectl apply -f deploy/k8s/lmr.yaml # only when the manifest changed
kubectl rollout restart deployment/localmodelroutex
kubectl rollout status deployment/localmodelroutex
```
`compose.yml` and `compose.bundled.yml` build from source for single-host use.
The bundled variant runs Postgres inside the same container.
## Database & migrations ## Database & migrations
SQL migrations live in [`backend/migrations/`](backend/migrations/) using SQL migrations live in [`backend/migrations/`](backend/migrations/) using

90
deploy/k8s/lmr.yaml Normal file
View File

@@ -0,0 +1,90 @@
# LMR gateway on k3s. Config and secrets live in the cluster:
# configmap/lmr-config GATEWAY_* settings
# secret/lmr-secret GATEWAY_DB_URL, GATEWAY_OIDC_CLIENT_SECRET, GATEWAY_SECURITY_ENCRYPTION_KEY
# secret/regcred2 registry pull credentials
# Apply with: kubectl apply -f deploy/k8s/lmr.yaml
# Roll a new image with: kubectl rollout restart deployment/localmodelroutex
apiVersion: apps/v1
kind: Deployment
metadata:
name: localmodelroutex
namespace: default
labels:
app: localmodelroutex
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: localmodelroutex
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
labels:
app: localmodelroutex
spec:
imagePullSecrets:
- name: regcred2
terminationGracePeriodSeconds: 30
containers:
- name: localmodelroutex
image: registry.govekar.net/lovrog05/localmodelroutex:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
envFrom:
- configMapRef:
name: lmr-config
- secretRef:
name: lmr-secret
# Startup covers OIDC discovery and the first database connection.
# Up to 2 minutes before the liveness probe takes over.
startupProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 24
# /healthz only checks that the process answers. A database or
# network blip must not restart the pod, so this never touches the DB.
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
# /readyz pings the database with a 3 second internal timeout. The
# pod leaves the Service while the database is unreachable and
# returns when it recovers, without a restart.
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
---
apiVersion: v1
kind: Service
metadata:
name: lmr
namespace: default
labels:
app: localmodelroutex
spec:
type: LoadBalancer
selector:
app: localmodelroutex
ports:
- name: http
port: 80
targetPort: http
protocol: TCP