diff --git a/charts/dynamicweb/Chart.yaml b/charts/dynamicweb/Chart.yaml new file mode 100644 index 00000000..5d42e90a --- /dev/null +++ b/charts/dynamicweb/Chart.yaml @@ -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" diff --git a/charts/dynamicweb/templates/_helpers.tpl b/charts/dynamicweb/templates/_helpers.tpl new file mode 100644 index 00000000..ae558f2d --- /dev/null +++ b/charts/dynamicweb/templates/_helpers.tpl @@ -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 -}} diff --git a/charts/dynamicweb/templates/celery.yaml b/charts/dynamicweb/templates/celery.yaml new file mode 100644 index 00000000..0db5d5ca --- /dev/null +++ b/charts/dynamicweb/templates/celery.yaml @@ -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 }}}}] diff --git a/charts/dynamicweb/templates/ingress.yaml b/charts/dynamicweb/templates/ingress.yaml new file mode 100644 index 00000000..443257a9 --- /dev/null +++ b/charts/dynamicweb/templates/ingress.yaml @@ -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 }} diff --git a/charts/dynamicweb/templates/migrate-job.yaml b/charts/dynamicweb/templates/migrate-job.yaml new file mode 100644 index 00000000..ff70ad7d --- /dev/null +++ b/charts/dynamicweb/templates/migrate-job.yaml @@ -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 }}}}] diff --git a/charts/dynamicweb/templates/redis.yaml b/charts/dynamicweb/templates/redis.yaml new file mode 100644 index 00000000..19ea49e7 --- /dev/null +++ b/charts/dynamicweb/templates/redis.yaml @@ -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}] diff --git a/charts/dynamicweb/templates/web.yaml b/charts/dynamicweb/templates/web.yaml new file mode 100644 index 00000000..b3669e72 --- /dev/null +++ b/charts/dynamicweb/templates/web.yaml @@ -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 }}}} diff --git a/charts/dynamicweb/values.yaml b/charts/dynamicweb/values.yaml new file mode 100644 index 00000000..4452c9dd --- /dev/null +++ b/charts/dynamicweb/values.yaml @@ -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: "" diff --git a/k8s/dynamicweb.yaml b/k8s/dynamicweb.yaml deleted file mode 100644 index 1bf44ced..00000000 --- a/k8s/dynamicweb.yaml +++ /dev/null @@ -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}}}}