Two charts under helm/, both compile zddc-server from source via an
init container — no container image registry, no pre-built binary.
The init container clones the repo at a configured git ref, runs
`go build`, and writes the binary into a shared emptyDir; the main
container is alpine + the freshly built static binary.
helm/zddc-server-prod/ Production-shaped:
- gitRef pinned to a stable tag in
values.yaml.example (zddc-server-v0.0.7).
- imagePullPolicy IfNotPresent.
- Slower probe cadence (30s liveness, 10s
readiness).
- ZDDC_LOG_LEVEL=info.
- replicaCount: 1 (operators raise as needed
when backed by a shared filesystem).
helm/zddc-server-dev/ Dev/soak-shaped:
- gitRef defaults to "main" (rebuilt every pod
restart). build-time annotation forces
recreate on every helm upgrade.
- imagePullPolicy Always on the build image
so the latest golang:1.24-alpine is pulled.
- Faster probe cadence (10s liveness, 5s
readiness) — fail-fast in dev.
- ZDDC_LOG_LEVEL=debug. NOTE: debug logs every
request's full header map (includes auth
tokens / cookies) — this chart is for
private dev namespaces only.
- Strategy: Recreate (single replica racing
on different SHAs would be a mess).
Both charts:
- Wire the ZDDC_* env-var contract (ZDDC_ROOT, ZDDC_ADDR,
ZDDC_TLS_CERT=none, ZDDC_INSECURE_DIRECT=1, ZDDC_EMAIL_HEADER,
ZDDC_CORS_ORIGIN, ZDDC_LOG_LEVEL, ZDDC_INDEX_PATH).
- Mount a caller-supplied PVC at ZDDC_ROOT (chart does not create the
PVC; operators provision storage themselves).
- Optional Ingress (ingress.enabled: true). TLS is expected to be
terminated upstream of the pod; the pod listens on plain HTTP.
- No secrets in values.yaml.example. ACL email lists go in .zddc files
inside the data volume; image-pull and TLS secrets are referenced by
name only.
helm/README.md documents the design rationale (why build from source
instead of using a registry image), a quick-start example, and the
explicit list of what the charts do and don't do.
Note: `helm lint` cannot be run in this dev environment (helm isn't
installed). YAML syntax of Chart.yaml and values.yaml.example
verified via `python3 -c "yaml.safe_load(...)"`. Operators should
run `helm lint` and `helm template` before installing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
4.4 KiB
Markdown
108 lines
4.4 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`. |
|
|
| **`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). |
|
|
|
|
The chart values are nearly identical between the two; the differences
|
|
are encoded as defaults in each chart's `values.yaml.example`.
|
|
|
|
## 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`.
|
|
- 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
|
|
```
|