Add a Helm chart that migrates with a Job and replaces the raw manifest

This commit is contained in:
sajeyks 2026-10-09 21:44:34 +03:00
commit fdb9ce6033
9 changed files with 150 additions and 106 deletions

View file

@ -0,0 +1,6 @@
apiVersion: v2
name: dynamicweb
description: ungleich dynamicweb (Django CMS) with a Celery worker and Redis
type: application
version: 0.1.0
appVersion: "latest"

View file

@ -0,0 +1,4 @@
{{- define "dw.image" -}}{{ .Values.image.repository }}:{{ .Values.image.tag }}{{- end -}}
{{- define "dw.pod" -}}
enableServiceLinks: false # else a Service named "postgres" injects POSTGRES_PORT=tcp://...
{{- end -}}

View file

@ -0,0 +1,16 @@
apiVersion: apps/v1
kind: Deployment
metadata: {name: {{ .Release.Name }}-celery}
spec:
replicas: {{ .Values.celery.replicas }}
selector: {matchLabels: {app: {{ .Release.Name }}-celery}}
template:
metadata: {labels: {app: {{ .Release.Name }}-celery}}
spec:
{{- include "dw.pod" . | nindent 6 }}
containers:
- name: celery
image: {{ include "dw.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env: [{name: ROLE, value: celery}]
envFrom: [{secretRef: {name: {{ .Values.existingSecret }}}}]

View file

@ -0,0 +1,15 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: {name: {{ .Release.Name }}}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- with .Values.ingress.tlsSecret }}
tls: [{secretName: {{ . }}, hosts: {{ $.Values.ingress.hosts | toJson }}}]
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
http:
paths:
- {path: /, pathType: Prefix, backend: {service: {name: {{ $.Release.Name }}-web, port: {number: 80}}}}
{{- end }}

View file

@ -0,0 +1,25 @@
# Runs after the first install and before each upgrade. Web pods don't migrate.
# (After install it needs Redis, which the app uses while loading, so it waits for it.)
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-migrate
annotations:
"helm.sh/hook": post-install,pre-upgrade
"helm.sh/hook-delete-policy": before-hook-creation
spec:
backoffLimit: 2
template:
spec:
{{- include "dw.pod" . | nindent 6 }}
restartPolicy: Never
initContainers:
- name: wait-for-redis
image: redis:6-alpine
command: [sh, -c, "until redis-cli -h redis ping; do sleep 2; done"]
containers:
- name: migrate
image: {{ include "dw.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env: [{name: ROLE, value: migrate}]
envFrom: [{secretRef: {name: {{ .Values.existingSecret }}}}]

View file

@ -0,0 +1,21 @@
# The app's env points at a Service called "redis" (CELERY_BROKER_URL=redis://redis:6379/0)
apiVersion: apps/v1
kind: Deployment
metadata: {name: redis}
spec:
replicas: 1
selector: {matchLabels: {app: redis}}
template:
metadata: {labels: {app: redis}}
spec:
containers:
- name: redis
image: redis:6-alpine
ports: [{containerPort: 6379}]
---
apiVersion: v1
kind: Service
metadata: {name: redis}
spec:
selector: {app: redis}
ports: [{port: 6379}]

View file

@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata: {name: {{ .Release.Name }}-web}
spec:
replicas: {{ .Values.web.replicas }}
selector: {matchLabels: {app: {{ .Release.Name }}-web}}
template:
metadata: {labels: {app: {{ .Release.Name }}-web}}
spec:
{{- include "dw.pod" . | nindent 6 }}
containers:
- name: web
image: {{ include "dw.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env: [{name: RUN_MIGRATIONS, value: "False"}] # the migrate Job does it
envFrom: [{secretRef: {name: {{ .Values.existingSecret }}}}]
ports: [{containerPort: 8000}]
readinessProbe: {tcpSocket: {port: 8000}, periodSeconds: 10}
volumeMounts: [{name: media, mountPath: /usr/src/app/media}]
volumes:
- name: media
persistentVolumeClaim: {claimName: {{ .Release.Name }}-media}
---
apiVersion: v1
kind: Service
metadata: {name: {{ .Release.Name }}-web}
spec:
selector: {app: {{ .Release.Name }}-web}
ports: [{port: 80, targetPort: 8000}]
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: {{ .Release.Name }}-media}
spec:
accessModes: [{{ .Values.media.accessMode }}]
{{- with .Values.media.storageClass }}
storageClassName: {{ . }}
{{- end }}
resources: {requests: {storage: {{ .Values.media.size }}}}

View file

@ -0,0 +1,24 @@
image:
repository: REGISTRY/dynamicweb
tag: VERSION
pullPolicy: IfNotPresent
# Secret holding the app's environment (kubectl create secret generic ... --from-env-file=...)
# It must exist before the install. See K8S.md.
existingSecret: dynamicweb
# One host per domain in UNGLEICH_SITE_CONFIGS (add the www. variants)
ingress:
className: nginx
hosts: [ungleich.ch, www.ungleich.ch]
tlsSecret: "" # set to enable TLS for the hosts above
web:
replicas: 1
celery:
replicas: 1
media:
size: 5Gi
accessMode: ReadWriteOnce # ReadWriteMany if web.replicas > 1
storageClass: ""

View file

@ -1,106 +0,0 @@
# Apply with: kubectl apply -f k8s/dynamicweb.yaml (see K8S.md)
# Edit: the image (3 places) and the ingress hosts. Needs the Secret "dynamicweb".
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: dynamicweb-media}
spec:
accessModes: [ReadWriteOnce] # ReadWriteMany if you run more than one web replica
resources: {requests: {storage: 5Gi}}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: redis}
spec:
replicas: 1
selector: {matchLabels: {app: redis}}
template:
metadata: {labels: {app: redis}}
spec:
containers:
- name: redis
image: redis:6-alpine
ports: [{containerPort: 6379}]
---
apiVersion: v1
kind: Service
metadata: {name: redis}
spec:
selector: {app: redis}
ports: [{port: 6379}]
---
# Run once per release, after the database is restored and before the web pods start.
apiVersion: batch/v1
kind: Job
metadata: {name: dynamicweb-migrate}
spec:
backoffLimit: 2
template:
spec:
enableServiceLinks: false # else a Service named "postgres" injects POSTGRES_PORT=tcp://...
restartPolicy: Never
containers:
- name: migrate
image: REGISTRY/dynamicweb:VERSION
env: [{name: ROLE, value: migrate}]
envFrom: [{secretRef: {name: dynamicweb}}]
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: dynamicweb-web}
spec:
replicas: 1
selector: {matchLabels: {app: dynamicweb-web}}
template:
metadata: {labels: {app: dynamicweb-web}}
spec:
enableServiceLinks: false
containers:
- name: web
image: REGISTRY/dynamicweb:VERSION
env: [{name: RUN_MIGRATIONS, value: "False"}] # the migrate Job does it
envFrom: [{secretRef: {name: dynamicweb}}]
ports: [{containerPort: 8000}]
readinessProbe: {tcpSocket: {port: 8000}, periodSeconds: 10}
volumeMounts: [{name: media, mountPath: /usr/src/app/media}]
volumes:
- name: media
persistentVolumeClaim: {claimName: dynamicweb-media}
---
apiVersion: v1
kind: Service
metadata: {name: dynamicweb-web}
spec:
selector: {app: dynamicweb-web}
ports: [{port: 80, targetPort: 8000}]
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: dynamicweb-celery}
spec:
replicas: 1
selector: {matchLabels: {app: dynamicweb-celery}}
template:
metadata: {labels: {app: dynamicweb-celery}}
spec:
enableServiceLinks: false
containers:
- name: celery
image: REGISTRY/dynamicweb:VERSION
env: [{name: ROLE, value: celery}]
envFrom: [{secretRef: {name: dynamicweb}}]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: {name: dynamicweb}
spec:
ingressClassName: nginx # your ingress controller
# tls: [{secretName: dynamicweb-tls, hosts: [ungleich.ch]}]
rules:
- host: ungleich.ch # one rule per domain in UNGLEICH_SITE_CONFIGS
http:
paths:
- {path: /, pathType: Prefix, backend: {service: {name: dynamicweb-web, port: {number: 80}}}}
- host: www.ungleich.ch
http:
paths:
- {path: /, pathType: Prefix, backend: {service: {name: dynamicweb-web, port: {number: 80}}}}