Chartreuse: Automated Alembic SQL schema migrations within kubernetes
"How to automate management of Alembic database schema migration at scale using CI/CD and Kubernetes"
Chartreuse is a wrapper around Alembic to ease, detect and automate migrations on deployed Python applications.
Chartreuse leverages Helm Hooks, the Hooks are defined in Chartreuse Helm Chart.
Usage
Requirements
- Python >= 3.7
- Using Helm to deploy you application
- This Python package requires the
expecteddeploymentscales.wiremind.io
KubernetesCustom Resource Definition
fromwiremind-kubernetes
repository:
kubectl apply -f https://raw.githubusercontent.com/wiremind/wiremind-kubernetes/main/CustomResourceDefinition-expecteddeploymentscales.yaml
- Please make sure Chartreuse Python Package version and Chartreuse Helm Chart version, you use, share
major.minor
otherwise Chartreuse won't start.
Configuration
Using Helm
Chartreuse comes with a Helm Chart ready to be used as a Helm Subchart in your own Helm Chart.
All you have to do is build your own container image containing:
- Chartreuse Python package
- Your Alembic migrations in an
alembic
directory - All required dependencies to run your alembic migrations.
Usually, it will be the same container image for your project with your code as usual, with Chartreuse added as dependency in your setup.py.
and state in the Chartreuse Helm Chart values.yaml:
- the image repository and tag
- URL to connect to your PostgreSQL
During install and/or upgrade of your Helm Release, Chartreuse will run as Kubernetes Job and automatically migrate PostgreSQL shchema to HEAD
if needed.
If required, it will also scale down Deployments that should NOT run during a Deployment using ExpectedDeploymentScale CRD.
Please refer to the example directory for example.
Diagram
The state diagram of your application while upgrading using Helm and using Chartreuse for your migrations is as follows:
Notes
- PG clusters managed by postgres-operator (Patroni PG):
- When Chartreuse starts running against a PG cluster managed by
postgres-operator
(Patroni PG), it may run the migrations before that the cluster is configured, and by configured we mean:- the Roles, especially
wiremind_owner_user
andwiremind_owner
used by Chartreuse and Alembic, are created. - The default privileges are set, so the other Roles, like
wiremind_writer_user
used by the application, can interact with the created objects. To ensure that, Chartreuse will not start untilpostgres-operator
has performed the above two actions. To make Chartreuse wait, the environment variableCHARTREUSE_PATRONI_POSTGRESQL
should be set:
# in the appropriate Helm values file chartreuse-for-a-patroni-pg: additionalEnvironmentVariables: CHARTREUSE_PATRONI_POSTGRESQL: "anything" CHARTREUSE_ALEMBIC_POSTGRES_WAIT_CONFIGURED_TIMEOUT: 100 # It's set to 60s by default
- the Roles, especially
- The default privileges above-mentioned are set for the NOLOGIN owner
wiremind_owner
, e.g. tables should be created bywiremind_owner
sowiremind_writer[_user]
can insert to them. This is why we need toSET ROLE wiremind_owner
in the beginning of the transaction before running the migrations, Chartreuse does set-x patroni_postgresql=yes
toalembic upgrade head
when the environment variablePATRONI_POSTGRESQL
is set, you can then retrieve the argument and set the role in yourenv.py
:... patroni_postgresql: bool = "patroni_postgresql" in context.get_x_argument(as_dictionary=True) ... with connectable.connect() as connection: ... with context.begin_transaction(): if patroni_postgresql: context.execute("SET ROLE wiremind_owner") context.run_migrations() ...
- When Chartreuse starts running against a PG cluster managed by
- Chartreuse in pre-upgrade mode:
- When running Chartreuse in pre-upgrade mode (
upgradeBeforeDeployment: true
), it will not start running (The Chartreuse Pod will hang inInit
state) until one PG Pod (and ES Pod if ES is used) is running, so make sure these Pods are available to Chartreuse. To fix that:- You will need to delete the Chartreuse Job so the upgrade can resume and fix you PG and ES pods (or create them if they don't exist), then you can redeploy so your migrations can run.
- You can also try the
upgradeBeforeDeployment: false
mode (maybe temporarily).
- When running Chartreuse in pre-upgrade mode (
Development
Test
There are three kind of tests:
- Unit tests
- Integration tests: allows to run in a real environment, but still control chartreuse from the inside
- blackbox test: deploy a real Helm Release and test if databases are migrated.
Documentation
- The diagram has been drawn using the free online software https://draw.io, the source code is located at
doc/chartreuse_sd.xml
, feel free to correct it or make it more understandable.