diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f305358..81d3111 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,12 @@ image: - name: alpine/helm:3.2.4 + name: alpine/helm:3.5.4 entrypoint: ["/bin/sh", "-c"] +lint: + stage: test + script: + - helm lint + pages: stage: deploy script: diff --git a/Chart.lock b/Chart.lock index ce4e88d..fd607c9 100644 --- a/Chart.lock +++ b/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 10.3.17 + version: 10.4.8 - name: redis repository: https://charts.bitnami.com/bitnami - version: 13.0.1 -digest: sha256:ef9c8ed96cc70ed6d6c6fc0e83a166e46ce1a8c29fe03b6164b361e78782cb46 -generated: "2021-04-16T16:13:30.720115202-04:00" + version: 14.2.1 +digest: sha256:400dc8c7d3450f4f2cb2e7be70688092634a00c177771988bd03c0ec13d290d8 +generated: "2021-05-27T11:09:15.228491774-04:00" diff --git a/Chart.yaml b/Chart.yaml index ffc30e9..f4ef842 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -name: glitchtip -description: Open source error tracking that is compatible with Sentry +name: django +description: Generic Django + Celery Helm Chart # A chart can be either an 'application' or a 'library' chart. # @@ -22,10 +22,10 @@ appVersion: 1.0.0 dependencies: - name: postgresql - version: 10.3.17 + version: ~10.4.8 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled - name: redis - version: 13.0.1 + version: ~14.2.1 repository: https://charts.bitnami.com/bitnami condition: redis.enabled diff --git a/LICENSE b/LICENSE index 0d30bd7..d9899a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 GlitchTip +Copyright (c) 2021 Burke Software and Consulting LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7e5b512..1e6613d 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,58 @@ -# GlitchTip Helm Chart +# Django Helm Chart -We use this chart internally. However it's not fully documented yet nor tested in a wide range of scenarios. -If you are a helm and kubernetes expert - feel free to use this and help contribute to this repo. +A generic Django (plus Celery) Helm chart. + +# Preparing your Django app + +This chart supports a web plus optional celery and beat deployments. Be prepared to extend it as necessary. + +Django settings will be managed by environment variables. `os.getenv` is fine. `django-environ` is nice as well. This chart expects SECRET_KEY and DATABASE_URL variables. + +Kubernetes works best when it is able to determine application health. You Django app should have a `/_health/` view such as + +``` +def health(request): + return HttpResponse("ok", content_type="text/plain") + +urlpatterns = [ + path("_health/", health), +... +``` # Usage 1. Add our Helm chart repo `helm repo add glitchtip https://glitchtip.gitlab.io/glitchtip-helm-chart/` -2. Review our values.yaml. At a minimum you'll need to set databaseURL and secretKey. +2. Review our values.yaml. At a minimum you'll need to set SECRET_KEY. 3. Install the chart `helm install glitchtip/glitchtip --set databaseURL=your_db --set secretKey=random_string` + +# Tips + +- Do you really need kubernetes? It's very complex. +- Don't use helm without [helm diff](https://github.com/databus23/helm-diff). One typo will wipe your app without warning otherwise. +- While supported, I don't suggest running stateful services like PostgreSQL in kubernetes. Upgrades will likely involve downtime or extensive and arcane knowledge. +- It's fine to use this chart as a reference for your own chart instead of directly using it. + +## Managing environment variables and secrets + +I suggest either + +- Keep them in a values.yml file in a private repo +- Make use of --reuse-values and --set +- Keep them in a non helm chart managed service + +## Deploying in CI + +I use lwolf/helm-kubectl-docker with Gitlab CI. [Example](https://gitlab.com/glitchtip/glitchtip-frontend/-/blob/master/.gitlab-ci.yml). + +# Support development + +Maintaining this chart takes time. Considering supporting it by + +- [Donating on liberapay](https://liberapay.com/burke-software/) +- Check out [GlitchTip](https://glitchtip.com) error tracking, which is where this project started + +Commercial support is available - email info@burkesoftware.com + +# Contributing + +Contributions are welcome. Report bugs on gitlab issues. Please only open feature requests that you'd like to implement yourself or pay for. \ No newline at end of file diff --git a/charts/postgresql-10.3.17.tgz b/charts/postgresql-10.3.17.tgz deleted file mode 100644 index 2f71763..0000000 Binary files a/charts/postgresql-10.3.17.tgz and /dev/null differ diff --git a/charts/postgresql-10.4.8.tgz b/charts/postgresql-10.4.8.tgz new file mode 100644 index 0000000..3b4c421 Binary files /dev/null and b/charts/postgresql-10.4.8.tgz differ diff --git a/charts/redis-13.0.1.tgz b/charts/redis-13.0.1.tgz deleted file mode 100644 index 7b7267d..0000000 Binary files a/charts/redis-13.0.1.tgz and /dev/null differ diff --git a/charts/redis-14.2.1.tgz b/charts/redis-14.2.1.tgz new file mode 100644 index 0000000..3f287f5 Binary files /dev/null and b/charts/redis-14.2.1.tgz differ diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 2734712..9c466b7 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -78,19 +78,29 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this {{- end -}} {{- end -}} {{- end -}} + {{- define "django.redis.fullname" -}} {{- if .Values.redis.fullnameOverride -}} {{- .Values.redis.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- $name := default .Chart.Name .Values.redis.nameOverride -}} +{{- $name := default .Chart.Name -}} {{- if contains $name .Release.Name -}} {{- .Release.Name | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- printf "%s-%s" .Release.Name "django-redis" | trunc 63 | trimSuffix "-" -}} +{{- printf "%s-%s" .Release.Name "redis-master" | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- end -}} {{- end -}} +{{/* +Set postgresql url +*/}} +{{- define "django.postgresql.url" -}} +{{- if .Values.postgresql.enabled -}} +postgres://{{ .Values.postgresql.postgresqlUsername }}:{{ .Values.postgresql.postgresqlPassword }}@{{- template "django.postgresql.fullname" . -}} +{{- end -}} +{{- end -}} + {{/* Set redis host */}} @@ -107,7 +117,7 @@ Set redis url */}} {{- define "django.redis.url" -}} {{- if .Values.redis.enabled -}} -redis://{{- template "django.redis.password" -}}{{- template "django.redis.fullname" . -}}-master +redis://:{{ .Values.redis.auth.password }}@{{- template "django.redis.fullname" . -}}:{{- template "django.redis.port" . -}}/0 {{- end -}} {{- end -}} @@ -116,8 +126,8 @@ Set redis port */}} {{- define "django.redis.port" -}} {{- if .Values.redis.enabled -}} - "6379" + 6379 {{- else -}} -{{- default "6379" .Values.redis.port | quote -}} +{{- default "6379" .Values.redis.port -}} {{- end -}} {{- end -}} diff --git a/templates/beat/deployment.yaml b/templates/beat/deployment.yaml index 5a15e24..4112037 100644 --- a/templates/beat/deployment.yaml +++ b/templates/beat/deployment.yaml @@ -1,3 +1,4 @@ +{{- if .Values.worker.enabled -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -43,23 +44,9 @@ spec: env: - name: SERVER_ROLE value: "beat" - {{- if .Values.redisURL }} - - name: REDIS_URL - value: {{ .Values.redisURL }} - {{- end }} - {{- if .Values.redis.enabled }} - - name: REDIS_PASSWORD - valueFrom: - secretKeyRef: - key: redis-password - name: {{ include "django.fullname" . }}-redis - - name: REDIS_HOST - value: {{ template "django.redis.host" . }} - - name: REDIS_PORT - value: {{ template "django.redis.port" . }} - {{- end }} envFrom: - secretRef: name: {{ include "django.fullname" . }} - configMapRef: name: {{ include "django.fullname" . }} +{{- end }} \ No newline at end of file diff --git a/templates/configmap.yaml b/templates/configmap.yaml index 6b57ab6..70f8c56 100644 --- a/templates/configmap.yaml +++ b/templates/configmap.yaml @@ -7,6 +7,6 @@ metadata: annotations: "helm.sh/hook-weight": "-1" data: -{{- range $k, $v := .Values.environmentVariables }} +{{- range $k, $v := .Values.env.normal }} {{ $k }}: {{ $v | quote }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/pre-install-job.yaml b/templates/pre-install-job.yaml index e539a9e..d3c2d79 100644 --- a/templates/pre-install-job.yaml +++ b/templates/pre-install-job.yaml @@ -15,6 +15,10 @@ spec: activeDeadlineSeconds: 600 template: spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} restartPolicy: Never containers: - name: pre-install-job @@ -24,9 +28,12 @@ spec: env: - name: DEBUG value: "False" - - name: STATIC_URL - value: / +{{- if .Values.env.secret.DATABASE_URL }} - name: DATABASE_URL - value: {{ required "databaseURL is a required value." .Values.databaseURL }} + value: {{ .Values.env.secret.DATABASE_URL }} +{{- else if .Values.postgresql.enabled }} + - name: DATABASE_URL + value: {{ include "django.postgresql.url" . | b64enc | quote }} +{{- end }} - name: SECRET_KEY - value: {{ required "secretKey is a required value." .Values.secretKey }} + value: {{ required "env.secret.SECRET_KEY is a required value." .Values.env.secret.SECRET_KEY }} diff --git a/templates/secrets.yaml b/templates/secrets.yaml index 505738f..b8aff3f 100644 --- a/templates/secrets.yaml +++ b/templates/secrets.yaml @@ -6,12 +6,12 @@ metadata: {{- include "django.labels" . | nindent 4 }} type: Opaque data: - DATABASE_URL: {{ required "databaseURL is a required value." .Values.databaseURL | b64enc | quote }} - SECRET_KEY: {{ required "secretKey is a required value." .Values.secretKey | b64enc | quote }} - REDIS_URL: {{ printf "redis://:@glitchtip-staging-redis-master:6379/0" | b64enc | quote }} -{{- if .Values.stripeLiveSecretKey }} - STRIPE_LIVE_SECRET_KEY: {{ .Values.stripeLiveSecretKey | b64enc | quote }} +{{- range $k, $v := .Values.env.secret }} + {{ $k }}: {{ $v | b64enc | quote }} {{- end }} -{{- if .Values.djstripeWebhookSecret }} - DJSTRIPE_WEBHOOK_SECRET: {{ .Values.djstripeWebhookSecret | b64enc | quote }} +{{- if .Values.redis.enabled }} + REDIS_URL: {{ include "django.redis.url" . | b64enc | quote }} {{- end }} +{{- if .Values.postgresql.enabled }} + DATABASE_URL: {{ include "django.postgresql.url" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/templates/web/deployment.yaml b/templates/web/deployment.yaml index 8d109d3..706bac8 100644 --- a/templates/web/deployment.yaml +++ b/templates/web/deployment.yaml @@ -57,21 +57,6 @@ spec: resources: {{- toYaml .Values.web.resources | nindent 12 }} env: - {{- if .Values.redisURL }} - - name: REDIS_URL - value: {{ .Values.redisURL }} - {{- end }} - {{- if .Values.redis.enabled }} - - name: REDIS_PASSWORD - valueFrom: - secretKeyRef: - key: redis-password - name: {{ include "django.fullname" . }}-redis - - name: REDIS_HOST - value: {{ template "django.redis.host" . }} - - name: REDIS_PORT - value: {{ template "django.redis.port" . }} - {{- end }} - name: POD_IP valueFrom: fieldRef: diff --git a/templates/web/ingress.yaml b/templates/web/ingress.yaml index 3f9860d..c9f7e0f 100644 --- a/templates/web/ingress.yaml +++ b/templates/web/ingress.yaml @@ -1,7 +1,14 @@ {{- if .Values.web.ingress.enabled -}} {{- $fullName := include "django.fullname" . -}} {{- $svcPort := .Values.web.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if and .Values.web.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.web.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.web.ingress.annotations "kubernetes.io/ingress.class" .Values.web.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1beta1 {{- else -}} apiVersion: extensions/v1beta1 @@ -16,6 +23,9 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: + {{- if and .Values.web.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.web.ingress.className }} + {{- end }} {{- if .Values.web.ingress.tls }} tls: {{- range .Values.web.ingress.tls }} @@ -33,9 +43,19 @@ spec: paths: {{- range .paths }} - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} backend: - serviceName: {{ $fullName }}-web + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} servicePort: {{ $svcPort }} + {{- end }} {{- end }} {{- end }} - {{- end }} +{{- end }} diff --git a/templates/worker/deployment.yaml b/templates/worker/deployment.yaml index f114446..f37a890 100644 --- a/templates/worker/deployment.yaml +++ b/templates/worker/deployment.yaml @@ -1,3 +1,4 @@ +{{- if .Values.worker.enabled -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -38,21 +39,6 @@ spec: env: - name: SERVER_ROLE value: "worker" - {{- if .Values.redisURL }} - - name: REDIS_URL - value: {{ .Values.redisURL }} - {{- end }} - {{- if .Values.redis.enabled }} - - name: REDIS_PASSWORD - valueFrom: - secretKeyRef: - key: redis-password - name: {{ include "django.fullname" . }}-redis - - name: REDIS_HOST - value: {{ template "django.redis.host" . }} - - name: REDIS_PORT - value: {{ template "django.redis.port" . }} - {{- end }} envFrom: - secretRef: name: {{ include "django.fullname" . }} @@ -70,3 +56,4 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} +{{- end }} \ No newline at end of file diff --git a/values-sample.yaml b/values-sample.yaml deleted file mode 100644 index df6c311..0000000 --- a/values-sample.yaml +++ /dev/null @@ -1,18 +0,0 @@ -web: - hpa: - enabled: true - resources: - limits: - cpu: 1000m - memory: 512Mi - requests: - cpu: 100m - memory: 128Mi - -worker: - hpa: - enabled: true - -environmentVariables: - ENABLE_SOCIAL_AUTH: true - GLITCHTIP_DOMAIN: https://staging.glitchtip.com diff --git a/values.yaml b/values.yaml index 43733a2..6e90721 100644 --- a/values.yaml +++ b/values.yaml @@ -1,25 +1,22 @@ -# Default values for glitchtip. +# Default values for django. # This is a YAML-formatted file. # Declare variables to be passed into your templates. image: - repository: glitchtip/glitchtip + repository: glitchtip/glitchtip # Demo app, change this. tag: latest pullPolicy: Always imagePullSecrets: [] nameOverride: "" fullnameOverride: "" -databaseURL: -secretKey: -redisURL: -# Only needed when using stripe for saas environment -stripeLiveSecretKey: -djstripeWebhookSecret: -environmentVariables: - ENABLE_SOCIAL_AUTH: false - GLITCHTIP_DOMAIN: https://example.com +env: + normal: {} + secret: {} + # SECRET_KEY: + # DATABASE_URL: + # REDIS_URL: web: replicaCount: 2 @@ -41,18 +38,18 @@ web: memory: 128Mi nodeSelector: {} tolerations: [] - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: app.kubernetes.io/instance - operator: In - values: - - glitchtip-prod-web - topologyKey: kubernetes.io/hostname + affinity: {} + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - glitchtip-prod-web + # topologyKey: kubernetes.io/hostname livenessProbe: failureThreshold: 5 initialDelaySeconds: 5 @@ -67,19 +64,22 @@ web: ingress: enabled: false - annotations: - {} + className: "" + annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local - paths: [] + paths: + - path: / + pathType: ImplementationSpecific tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local worker: + enabled: true replicaCount: 1 autoscaling: enabled: false @@ -110,13 +110,12 @@ redis: master: persistence: enabled: false - slave: + replica: + replicaCount: 1 persistence: enabled: false - cluster: - enabled: false - slaveCount: 1 # Default to disabled, use a managed database service. But can be enabled here. postgresql: enabled: false + postgresqlPassword: # Must be set