The workout, the protein, the calories, the water, and three lines of gratitude. One page per day, a card of seventy-five tiles that fill by how many of the five landed, and a missed day left as a gap rather than a reset. Single Go binary with no dependencies. The log is one JSON file on a mounted volume, written atomically and never silently replaced when it fails to parse, since it is the one thing here that cannot be recreated. Auth is Traefik basic auth at the ingress; the app has no login of its own, so every POST checks Sec-Fetch-Site to stop another origin posting with those credentials. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
157 lines
3.8 KiB
YAML
157 lines
3.8 KiB
YAML
---
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: tracker
|
|
|
|
---
|
|
# The whole log is one JSON file, so the "database" is this volume. It is
|
|
# ReadWriteOnce, which is why the deployment below replaces rather than rolls.
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: tracker-data-pvc
|
|
namespace: tracker
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: tracker-svc
|
|
namespace: tracker
|
|
spec:
|
|
selector:
|
|
app: tracker
|
|
ports:
|
|
- port: 80
|
|
targetPort: 8080
|
|
protocol: TCP
|
|
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: tracker-dep
|
|
namespace: tracker
|
|
spec:
|
|
replicas: 1
|
|
# Two pods would both hold the file in memory and overwrite each other's
|
|
# writes, and the volume only attaches to one node anyway. Recreate makes the
|
|
# old pod let go before the new one starts.
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels:
|
|
app: tracker
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: tracker
|
|
spec:
|
|
imagePullSecrets:
|
|
- name: harborcred
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 65534
|
|
runAsGroup: 65534
|
|
# Without fsGroup the volume comes up owned by root and nobody cannot
|
|
# write the file it exists to hold.
|
|
fsGroup: 65534
|
|
containers:
|
|
- name: tracker
|
|
image: harbor.scottyah.com/scottyah/tracker:latest
|
|
imagePullPolicy: Always
|
|
ports:
|
|
- containerPort: 8080
|
|
env:
|
|
- name: ADDR
|
|
value: ":8080"
|
|
- name: BASE_URL
|
|
value: "https://fit.scottyah.com"
|
|
- name: DATA_PATH
|
|
value: "/data/tracker.json"
|
|
# Every date in the app is a local date, so this decides when the
|
|
# day rolls over.
|
|
- name: TZ_NAME
|
|
value: "America/Los_Angeles"
|
|
- name: GOMEMLIMIT
|
|
value: "40MiB"
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /data
|
|
resources:
|
|
requests:
|
|
memory: "16Mi"
|
|
cpu: "10m"
|
|
limits:
|
|
memory: "64Mi"
|
|
cpu: "200m"
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8080
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 20
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8080
|
|
initialDelaySeconds: 2
|
|
periodSeconds: 10
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
# /data is the only thing the process writes.
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop: ["ALL"]
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: tracker-data-pvc
|
|
|
|
---
|
|
# The app has no login of its own; this is the only thing between the log and
|
|
# the internet, so the Service must not be exposed any other way.
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: tracker-basic-auth
|
|
namespace: tracker
|
|
spec:
|
|
basicAuth:
|
|
secret: tracker-basic-auth
|
|
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: tracker-ingress
|
|
namespace: tracker
|
|
annotations:
|
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
|
traefik.ingress.kubernetes.io/router.middlewares: tracker-tracker-basic-auth@kubernetescrd
|
|
spec:
|
|
ingressClassName: traefik
|
|
tls:
|
|
- hosts:
|
|
- fit.scottyah.com
|
|
secretName: scottyah-tls
|
|
rules:
|
|
- host: fit.scottyah.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: tracker-svc
|
|
port:
|
|
number: 80
|