simpers

simpers

Preferred way to run Ecto Migrations in Kubernetes cluster

Some environment info

We are hosting our Phoenix application on a Kubernetes cluster using a Dockerfile based on the one in distillery’s documentation.

We are using peerage to discover other nodes of this application, which solves node discovery when dynamically scaling the cluster up or down. This means that the nodes will know about each other so that Phoenix Channels can send events to all nodes.

Other perhaps useful info?

There’s a CICD pipeline for each dev, beta and prod, where in dev we’d want migrations to run automatically while in beta and prod we might want other settings/steps for this.

Hooks don’t work

The deal is though that the previous solution to running ecto migrations was to have a pre-start hook in the rel/config.exs such that it would run these migrations before starting the application. This will however run them each time a node starts, or if they are restarted. Which is not what I want.

We do not want running migrations to be part of any boot-up sequence to minimize boot-up times and to also make sure that we can control it manually, should we need to.

Goal

Find a flexible and easy way to run ecto migrations without adding hooks in the release that would run on each pod start, slowing a pod’s start up time down.

Approach one - Escript

One approach I have considered would be to write an escript that could be compiled and installed locally to manage these tasks, which could then be used in our CD by running the script(s) before deploying the updated docker image.

Approach two - Admin feature

We could implement an internal API that’s restricted to devs where one can start migrations, rollback or whatever. However, it will require that I deploy before I actually can migrate the newest changes, which would mean that until it is deployed and somebody then starts the migrations, there has to be some kind of precautions in all the endpoints checking if migrations has been done, as there might be missing fields in the database before this. Which is why I would prefer the first approach.

Combined

One could also combine these approaches for different cases

Current solutions?

What are the current solutions people use? Are they adaptable to a cluster environment where you don’t always know the host machine (or at least shouldn’t have to know it)?

Most Liked

blatyo

blatyo

Conduit Core Team

My preferred way to run migrations is to divide them into two buckets.

  1. Schema changes
  2. Data changes

Schema changes are the default migrations in the phoenix app and I setup data changes to run from a separate directory. In K8s, I setup schema changes to run as a pre-init container. In ecto 3, I added the feature that will lock the schema migrations table when migrations are running, so other pre-init containers in other pods will block until migrations have completed. I start a transient process as part of the supervision tree that runs data migrations. If you use the ecto 3 migrator for this, it’ll also ensure that only one of these runs across all pods.

For changes in the first group, they are required to be backwards compatible. That means only additions. Removals are allowed as long as the currently deployed application has already stopped using the thing being removed. Renames are not allowed, but can effectively be done by performing multiple simpler steps. This stuff is done to ensure that during a rolling deploy, the current app and the new app being deployed can run simultaneously.

Data migrations are written in such a way as to not add much load to an application. Also, they’re typically run outside of a transaction. So, they have to be written in such a way that if only part of the migration ran, that is ok and if the migration were to start running again, it would pick up where it left off.

I do these things because I write applications where zero downtime is important. If that’s not a requirement, you could probably get away with something simpler.

coryodaniel

coryodaniel

We only do backwards compat migrations and run them as a kubernetes Job with the “next” version of our docker image.

We do deployments as updates to a CRD “MyApp” using the operator pattern. It runs the Job^1 on CRD update, waits for it to succeeded and then updates the k8s Deployment along with some other stuff like running ElasticSearch reindexes in a similar manner.

I recently released the tool we use internally, Bonny. It allows you to have Operators (CRD lifecycles) written in Elixir.

  1. Technically our migrations are CRDs around jobs. We have DBMigration and DBRollback CRDs so we can easily issue them from outside of an application deployment if need be.
sanswork

sanswork

I’ve always used hooks. If you do rolling updates start up time doesn’t matter and if the migrations are up to date the time to run them will be negligible on your start up time. You’ll also waste a lot of time trying to implement a perfect solution.

Where Next?

Popular in Questions Top

New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 43487 311
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41989 114
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement