A Job in Kubernetes is a one-off and immutable task to be carried out during deployment. But what if a job needs to run for each deployment? A new job with the same name can’t be deployed on top of the existing one, given it in completed or failed state. Since Kubernetes 1.23, A TTL(Time To Live) can be set for a job so it will be cleaned up after the seconds given. I personally like ArgoCD’s alternative solution: Resource Hooks for reasons:
- The migration job is always visible there until next deploy
- As in
PreSync, if the migration job failed or got stuck the main app won’t be deployed which prevents running stale database schemas
In the following example, a job will be cleaned up before a new deploy so a new version of the same job can be deployed – This is perfect for the scenario where a DB migration happens before each release.
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
# set to trigger before a sync starts
argocd.argoproj.io/hook: PreSync
# delete the existing one before sync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
template:
metadata:
name: db-migration
labels:
app: django
annotations:
# no we don't want istio for a job
sidecar.istio.io/inject: "false"
spec:
restartPolicy: Never
containers:
- name: django-job
image: my-django-app:v1.0
command:
- /bin/bash
- -c
- |
# run the migration
python3 manage.py migrate
# load fixtures
python3 manage.py loaddata my-project/fixtures/*.yaml
volumeMounts:
- name: django-config-volume
# django settings from a mounted secret
mountPath: /var/www/django/my-project/settings.py
subPath: settings.py
resources:
requests:
cpu: 100m
memory: 200Mi
volumes:
- name: django-config-volume
secret:
secretName: django-settingsThis works really well for my tiny Django app 🙂
Edit: applied this for the Mastodon DB migration job too. It worked perfectly.
