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.
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β
- A migrations docker image
- 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.
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.
- General
- Django
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 }}
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: migrate
# Image to run migration
# Incase of Python Django, it is the same image as application
image: {{ .Values.django.image.repository }}:{{ .Values.django.image.tag }}
# Command to run DB migration
command: ["sh", "-c", "python manage.py migrate"]
# 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 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.
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. ππ½