In this guide, we demonstrate how DB migrations can be integrated into Helm charts in such a way that satisfies the
principles for deploying schema migrations before starting the application. We will see how to handle both DB schema and
data migrations.
We recommend using a robust migration framework for your applications that uses versioned migration files. Most migration frameworks support linear migrations, which only allow progressive changes.Therefore, if you need to remove a database column or field introduced in an earlier migration, you must create a new
migration file specifically for this purpose. This approach ensures that all changes are tracked and applied in a
controlled and sequential manner.
To satisfy the principle of having migrations run before the new application version starts, as well as ensure that
only one migration job runs concurrently, we use Helm’s pre-install and pre-upgrade hooks feature.
Helm pre-upgrade hooks are chart hooks that:Executes on an upgrade request after templates are rendered, but before any resources are updated
To use pre-install and pre-upgrade hooks to run migrations as part of our chart definition, we create a template for
a Kubernetes Job and annotate it with the relevant Helm hook annotations.
job.yaml
Copy
apiVersion: batch/v1 kind: Job metadata: # Job name should include a unix timestamp to make sure it's unique name: "{{ .Release.Name }}-migrate-{{ now | unixEpoch }}" labels: helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: template: metadata: name: "{{ .Release.Name }}-db-migrate" labels: app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: restartPolicy: Never imagePullSecrets: - name: {{ .Values.imagePullSecret }} containers: - name: db-migrate # Image to run DB migration image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" # Command to run DB migration args: - migrate - apply - -u - {{ .Values.dburl }} - --dir - file:///src/ # Environment variables required for migrations to run env: - name: DATABASE_HOST value: {{ .Values.django.env.DATABASE_HOST }} - name: DATABASE_PORT value: {{ .Values.django.env.DATABASE_PORT }} - name: DATABASE_NAME value: {{ .Values.django.env.DATABASE_NAME }} - name: DATABASE_USER value: {{ .Values.django.env.DATABASE_USER }} - name: DATABASE_PASSWORD value: {{ .Values.django.env.DATABASE_PASSWORD }}
Above Job will be run each time when an Environment is created and when a deployment is made with a new helm chart
version.
Similarly, DB data migrations can be performed within the same migration files and executed sequentially in a consistent
order. This approach allows you to manage both schema and data changes together within the same migration framework,
ensuring consistency and ease of development and maintenance.
LocalOps intends to adopt Blue/Green deployment strategy soon. Therefore, it is advisable to avoid introducing
breaking database or API changes in consecutive deployments. This practice ensures smoother transitions and minimizes
disruptions during version upgrades.
In this article you have learnt how to run DB migrations from your Helm Chart inside Environment. 👍🏽