ZDDC/helm/README.md
ZDDC 13ae1498e4 docs(helm): describe dev chart's OverlayFS isolation in README + Chart.yaml
The dev chart's overlay-isolation layer (added in 9765fa2) was not
called out in helm/README.md or zddc-server-dev/Chart.yaml. Readers
comparing the two charts saw "same shape but tracks main" without
learning that the dev chart wraps the data PVC in OverlayFS so its
writes never mutate the underlying store.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 08:33:04 -05:00

114 lines
5 KiB
Markdown

# Helm charts
Two example charts for deploying [zddc-server](../zddc/) on Kubernetes.
Both compile zddc-server from source via an init container — no
container image needs to be pulled from a registry, and no binary needs
to be built ahead of time. The init container clones the repo at a
configured git ref and runs `go build`; the main container is plain
alpine + the freshly built static binary.
## Charts
| Chart | When to use |
|---|---|
| **`zddc-server-prod/`** | Production. Pin `zddc.gitRef` to a stable tag (`zddc-server-vX.Y.Z`). Slower probe cadence; image-pull policy `IfNotPresent`. Mounts the data PVC directly RW at `ZDDC_ROOT`. |
| **`zddc-server-dev/`** | Development / soak. Tracks `main` by default; `helm upgrade` triggers a pod recreate so each rollout pulls the latest commit. Faster probes; debug-level logging (request headers logged — sensitive). Wraps the data PVC in **OverlayFS** (lower = PVC mounted RO, upper = ephemeral `emptyDir`) so dev-side writes never mutate the underlying store. Use this shape when the dev replica points at the same data as prod. |
The chart values are nearly identical between the two; the differences
are encoded as defaults in each chart's `values.yaml.example`. The
dev chart's overlay-isolation layer is a structural difference, not a
values-level toggle — see `zddc-server-dev/templates/deployment.yaml`
for the privileged init container and the `data-readonly` /
`overlay-scratch` / `data` volume sandwich.
## Quick start
```sh
# Pre-requisite: a PersistentVolumeClaim for ZDDC_ROOT data
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: zddc-root
spec:
accessModes: [ReadWriteMany] # or RWO if single replica is fine
resources: { requests: { storage: 100Gi } }
storageClassName: your-shared-fs # NFS, CephFS, SMB, etc.
EOF
# Production install
cp helm/zddc-server-prod/values.yaml.example my-prod-values.yaml
$EDITOR my-prod-values.yaml # set zddc.gitRef, hostnames, etc.
helm install zddc-server-prod helm/zddc-server-prod/ -f my-prod-values.yaml
# Dev install (tracks main HEAD)
cp helm/zddc-server-dev/values.yaml.example my-dev-values.yaml
$EDITOR my-dev-values.yaml
helm install zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml
# Trigger a rebuild from latest main HEAD (dev chart)
helm upgrade zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml
```
## What the chart does and doesn't do
**Does:**
- Clones the configured `zddc.gitRepo` at `zddc.gitRef` in an init
container, builds the Go binary, copies it to a shared `emptyDir`,
and starts the main container against that binary.
- Wires the `ZDDC_*` environment-variable contract (root path, addr,
email header, CORS allowlist, log level, index path).
- Mounts a caller-supplied PersistentVolumeClaim at `ZDDC_ROOT` (prod
chart) or as the OverlayFS lowerdir behind a merged `ZDDC_ROOT`
(dev chart).
- Optionally creates an Ingress (`ingress.enabled: true`).
**Does not:**
- Create the PVC. Operators provision storage themselves; the chart
only references it by name.
- Manage TLS for the pod. zddc-server runs in plain HTTP mode behind
whatever ingress / authenticating reverse proxy the cluster already
has. `ZDDC_TLS_CERT=none` and `ZDDC_INSECURE_DIRECT=1` are hardcoded
in the templates because the chart is opinionated about the
TLS-terminated-upstream deployment shape.
- Authenticate users. zddc-server reads the user's email from a header
set by the upstream proxy (`X-Auth-Request-Email` by default). The
chart does not deploy oauth2-proxy / nginx-auth-request / Pomerium /
etc. — bring your own.
- Manage secrets. `values.yaml.example` contains no secrets and never
should. ACL email lists belong in `.zddc` files inside the data
volume; image-pull credentials and TLS certs (if you enable ingress
TLS) reference Kubernetes secrets you've created separately.
## Why build from source instead of using a registry image
Three reasons:
1. **Reproducibility.** The init container's logs show exactly which
git ref was built. There's no opaque "what did I deploy" question
that a registry tag can introduce.
2. **One distribution channel.** Codeberg release-asset binaries
already exist for direct downloads; the chart compiles its own
binary from the same source git ref so there's nothing extra to
maintain (no separate image registry, no image-promotion pipeline).
3. **Smaller blast radius.** A compromised build image affects only
pods that pull during the compromise window. A compromised registry
image stays compromised across rollbacks until the digest is rotated.
The cost: every pod start takes 30-60s to clone + `go build` instead
of pulling a pre-baked image. Acceptable for both chart audiences
(production rollouts are infrequent; dev rollouts trade build time
for tracking-main convenience).
## Linting
```sh
helm lint helm/zddc-server-prod/
helm lint helm/zddc-server-dev/
# Render to inspect (uses default values from values.yaml.example):
helm template test-prod helm/zddc-server-prod/ \
--values helm/zddc-server-prod/values.yaml.example
```