feat(deploy): add the kubernetes manifests so the deployment is in the repo

The cluster objects for the beer namespace were created by hand. This puts
them in the repo so they can be applied and reviewed like everything else.

- `beer-deployment.yaml` — the Deployment, with the security context, the
  resource limits, and both probes wired up (`/healthz` liveness,
  `/readyz` readiness).
- `beer-service.yaml` — the NodePort service.
- `beer_namespace.yaml` — the namespace.

The credentials are NOT in these files. `MONGO_PASS` and `SECRET_KEY` come
from the live `beer-db` and `beer-app` Secrets, so applying the manifests
cannot clobber a hand-provisioned credential.

Notes on the pod security context
---------------------------------
`runAsNonRoot: true` cannot be set on the container when the image's user is
a *named* user - kubelet can only verify a numeric UID. The pod-level
security context therefore sets `runAsUser: 999`, which matches the `beer`
user the image creates (verified with `id` in the built image).

The container also drops all capabilities and refuses privilege escalation.
This commit is contained in:
2026-11-09 11:45:41 +10:00
parent c0c6b4c1fc
commit 55c5de9d61
3 changed files with 114 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: beer-deployment
namespace: beer
labels:
app: beer-data
spec:
replicas: 3
selector:
matchLabels:
app: beer-data
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: beer-data
spec:
containers:
- name: beer-data
image: git.aridgwayweb.com/armistace/beer-data:latest
imagePullPolicy: Always
ports:
- containerPort: 5000
protocol: TCP
env:
- name: MONGO_HOST
value: "192.168.178.229"
- name: MONGO_USER
value: "root"
# MONGO_PASS and SECRET_KEY come from the live Secret - they are
# deliberately NOT in this file. See RUNBOOK.md.
- name: MONGO_PASS
valueFrom:
secretKeyRef:
name: beer-db
key: mongo_pass
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: beer-app
key: secret_key
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
livenessProbe:
httpGet:
path: /healthz
port: 5000
initialDelaySeconds: 10
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 5000
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
securityContext:
runAsNonRoot: true
# The image creates a named user "beer". kubelet cannot verify that a
# *named* user is non-root (it can only check a numeric UID), so the
# UID is set explicitly here. It must match the UID in the image.
runAsUser: 999
runAsGroup: 999
fsGroup: 999
nodeSelector:
kubernetes.io/hostname: archlinux-k3s-3
imagePullSecrets:
- name: beercred
terminationGracePeriodSeconds: 30
restartPolicy: Always
+17
View File
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: beer-service
namespace: beer
labels:
app: beer-data
spec:
type: NodePort
selector:
app: beer-data
ports:
- name: http
port: 80
targetPort: 5000
nodePort: 30015
protocol: TCP
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: beer
labels:
app: beer-data