Skip to main content

Database Migrations

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.

important

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.

Prerequisites​

  1. A migrations docker image
  2. A Helm chart defining your application.

Schema Migrations​

Using Helm lifecycle hooks​

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.

info

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
  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 }}
info

Above Job will be run each time when an App Environment is created and when a deployment is made with a new helm chart version.

Data Migrations​

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.

tip

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 App Environment. πŸ‘πŸ½