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>
114 lines
5 KiB
Markdown
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
|
|
```
|