apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "zddc-server.fullname" . }} labels: {{- include "zddc-server.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} # Dev: always re-pull the build image and re-clone source, so a kubectl # rollout restart picks up new commits on the tracked ref. strategy: type: Recreate selector: matchLabels: {{- include "zddc-server.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "zddc-server.selectorLabels" . | nindent 8 }} annotations: # Forces pod recreation on every helm upgrade, ensuring the init # container re-clones the tracked ref. Useful in dev where you # want `helm upgrade` to pick up new main HEAD without changing # values. zddc.varasys.io/build-time: {{ now | quote }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} volumes: - name: zddc-bin emptyDir: {} # Production data volume — mounted READ-ONLY so the dev pod # cannot corrupt prod even with a bug. Becomes the lowerdir of # the OverlayFS mount below. - name: data-readonly persistentVolumeClaim: claimName: {{ .Values.data.pvcName }} readOnly: true # Writable scratch for OverlayFS upperdir + workdir. emptyDir # is ephemeral by default — dev tweaks evaporate on pod restart, # which is usually right for a dev replica. Replace with a # small PVC if persistence across restarts matters. - name: overlay-scratch emptyDir: {} # The composed read-write view zddc-server reads from. Populated # by the setup-overlay init container; passed through to the main # container as ZDDC_ROOT. - name: data emptyDir: {} initContainers: # OverlayFS sandwich: # lowerdir = /mnt/data-readonly (prod data, RO) # upperdir = /mnt/overlay-scratch/upper # workdir = /mnt/overlay-scratch/work # merged = /mnt/data (what main container sees) # # Why this exists: dev runs against the same on-disk dataset as # prod, but its writes (anything zddc-server writes — index # state, form submissions during testing, .zddc edits via the # admin page, etc.) MUST NOT mutate prod data. OverlayFS solves # this at the filesystem layer: prod data is RO, dev's writes # land in upperdir, the dev container sees the merged view. No # zddc-server code change required. # # Requires CAP_SYS_ADMIN (the overlay mount syscall is # privileged). Stays scoped to this one init container; the main # container runs without elevated privs. - name: setup-overlay image: {{ printf "%s:%s" .Values.runtimeImage.repository .Values.runtimeImage.tag | quote }} securityContext: privileged: true command: ["/bin/sh", "-c"] args: - | set -eu mkdir -p /mnt/overlay-scratch/upper /mnt/overlay-scratch/work mount -t overlay overlay \ -o lowerdir=/mnt/data-readonly,upperdir=/mnt/overlay-scratch/upper,workdir=/mnt/overlay-scratch/work \ /mnt/data echo "OverlayFS mounted: /mnt/data-readonly (RO) + /mnt/overlay-scratch (RW) -> /mnt/data" ls -la /mnt/data | head -10 volumeMounts: - name: data-readonly mountPath: /mnt/data-readonly readOnly: true - name: overlay-scratch mountPath: /mnt/overlay-scratch - name: data mountPath: /mnt/data mountPropagation: Bidirectional - name: build-zddc-server image: {{ printf "%s:%s" .Values.buildImage.repository .Values.buildImage.tag | quote }} imagePullPolicy: Always command: ["/bin/sh", "-c"] args: - | set -eu apk add --no-cache git git clone --depth 1 --branch "$GIT_REF" "$GIT_REPO" /workspace cd /workspace/zddc CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -trimpath \ -ldflags="-s -w -X main.version=$GIT_REF" \ -o /out/zddc-server \ ./cmd/zddc-server echo "built /out/zddc-server from $GIT_REF ($(git -C /workspace rev-parse --short HEAD))" env: - name: GIT_REPO value: {{ .Values.zddc.gitRepo | quote }} - name: GIT_REF value: {{ .Values.zddc.gitRef | quote }} volumeMounts: - name: zddc-bin mountPath: /out resources: requests: cpu: 100m memory: 128Mi limits: cpu: 1000m memory: 512Mi containers: - name: zddc-server image: {{ printf "%s:%s" .Values.runtimeImage.repository .Values.runtimeImage.tag | quote }} imagePullPolicy: IfNotPresent command: ["/zddc/zddc-server"] ports: - name: http containerPort: 8080 protocol: TCP env: - name: ZDDC_ROOT value: {{ .Values.zddc.env.rootPath | quote }} - name: ZDDC_ADDR value: {{ .Values.zddc.env.addr | quote }} - name: ZDDC_TLS_CERT value: "none" - name: ZDDC_INSECURE_DIRECT value: "1" - name: ZDDC_EMAIL_HEADER value: {{ .Values.zddc.env.emailHeader | quote }} - name: ZDDC_CORS_ORIGIN value: {{ .Values.zddc.env.corsOrigin | quote }} - name: ZDDC_LOG_LEVEL value: {{ .Values.zddc.env.logLevel | quote }} - name: ZDDC_INDEX_PATH value: {{ .Values.zddc.env.indexPath | quote }} volumeMounts: - name: zddc-bin mountPath: /zddc - name: data mountPath: {{ .Values.zddc.env.rootPath }} {{- with .Values.data.subPath }} subPath: {{ . | quote }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} # Tighter probe cadence than prod — fail fast in dev so issues # surface immediately during testing. livenessProbe: httpGet: path: / port: http initialDelaySeconds: 3 periodSeconds: 10 timeoutSeconds: 3 readinessProbe: httpGet: path: / port: http initialDelaySeconds: 1 periodSeconds: 5 timeoutSeconds: 2