Tutorials

Subdots on Docker

Capturing multiple stateful components in a single commit

In Dotmesh, operations like commits and branches operate on dots, but each dot can contain multiple volumes you can mount into your containers - and we call those subdots.

This tutorial will explore how to use subdots to help manage the stateful components of an app. We’re basing the tutorial on a fictitious app that is left to your imagination, but you can follow along with the examples in a real Dotmesh installation.

Getting Started.

Imagine you have an amazing application that uses MySQL to stores its data, backed by a dot. Your docker-compose.yaml file contains something like this:

version: '3'
services:
  catalog-db:
    image: mysql:5.6.39
    environment:
     - MYSQL_ROOT_PASSWORD=secret
    hostname: catalog-db
    volumes:
     - myapp:/var/lib/mysql

volumes:
  myapp:
    driver: dm

…in addition to some containers that hold your actual app, but let’s leave those to the imagination for this tutorial!

Triumphantly, we deploy our app into production:

$ docker-compose up -d

Gaining a second stateful container.

Life is good, but after six months, the investors start asking if you’re going to show any revenue this year and you decided you need to actually start handling orders. The best way to add order handling to your app is going to be using Mongo, so you add a Mongo container and a new volume to your YAML.

Your order processing feature gets developed, goes into production, and starts earning you revenue. Hooray!

$ docker-compose down
version: '3'
services:
  orders-db:
    image: mongo:3.4.10
    hostname: orders-db
    volumes:
     - myapp-orders:/data/db
  catalog-db:
    image: mysql:5.6.39
    environment:
     - MYSQL_ROOT_PASSWORD=secret
    hostname: catalog-db
    volumes:
     - myapp:/var/lib/mysql

volumes:
  myapp-orders:
    driver: dm
  myapp:
    driver: dm
$ docker-compose up -d

But your devops team are starting to grumble that they now have to remember to commit and push both the default_myapp and default_myapp-orders dots for the hourly snapshot. Indeed, Bob, who set up the production backup system didn’t know the developers had added a new stateful container at all for the first few weeks, and so only the MySQL database was getting hourly snapshots at all!

And outside of production, all the scripts that drive the CI processes, spinning up new environments for new developers, and all that day to day routine, now have to have an extra line in them to handle the default_myapp-orders dot as well as default_myapp. And, perhaps worst of all, you have a niggling aching feeling that calling your MySQL volume myapp in the first place was a mistake; it should have been myapp-catalog - but at the time, it was just The Database and that was that…

Enter Subdots.

Belatedly, you remember reading about subdots in the Dotmesh manual ages ago, and thinking “Hmmm, I’ll be sure to remember that when my app gets a bit more complex”. Clearly, the two databases should be subdots of a single default_myapp dot that, as the name suggests, stores the state of your app.

The orders database only stores orders currently being processed, so it’s not very large. With a simple change to the YAML, you configure the Mongo container to stores its data in an orders-db subdot of the exist default_myapp dot:

$ docker-compose down
version: '3'
services:
  orders-db:
    image: mongo:3.4.10
    hostname: orders-db
    volumes:
     - myapp.orders-db:/data/db
  catalog-db:
    image: mysql:5.6.39
    environment:
     - MYSQL_ROOT_PASSWORD=secret
    hostname: catalog-db
    volumes:
     - myapp:/var/lib/mysql

volumes:
  myapp.orders-db:
    driver: dm
  myapp:
    driver: dm

You transfer your existing Mongo database from the default_myapp-orders dot into default_myapp.orders-db:

$ docker run -v default_myapp-orders:/from -v default_myapp.orders-db:/to --volume-driver dm busybox sh -c 'cp -r /from/* /to'

And then you bring Mongo back up again, and your app’s state is now all in a single dot. You can delete the old default_myapp-orders dot once you’re confident everything is working fine:

$ dm dot delete default_myapp-orders
Please confirm that you really want to delete the dot default_myapp-orders, including all branches and commits? (enter Y to continue): Y
$ docker-compose up -d

Now, all your backup, deployment, CI, testing, and developer onboarding infrastructure can just deal with a single dot, myapp, and all the subdots will tag along for the ride automatically. You don’t need to change your infrastructure or inform various teams if your add needs a new database container.

But your setup is still a little… ugly. The Mongo orders database is in a subdot, but the MySQL catalog database isn’t. Or so it seems - if you don’t specify a subdot name, Dotmesh will just use the default subdot, __default__. So it’s easy to fix this!

$ docker-compose down
version: '3'
services:
  orders-db:
    image: mongo:3.4.10
    hostname: orders-db
    volumes:
     - myapp.orders-db:/data/db
  catalog-db:
    image: mysql:5.6.39
    environment:
     - MYSQL_ROOT_PASSWORD=secret
    hostname: catalog-db
    volumes:
     - myapp.catalog-db:/var/lib/mysql

volumes:
  myapp.orders-db:
    driver: dm
  myapp.catalog-db:
    driver: dm

You don’t need to transfer the (massive) MySQL database across, though; its old and new homes are still in the same dot, just different subdots. You can use docker to mount the root of the dot and rename the subdot:

$ docker run -v default_myapp.__root__:/data --volume-driver dm busybox mv /data/__default__ /data/catalog-db

With that done, you can bring the system back up again:

$ docker-compose up -d

And everything looks nice and neat:

$ dm list
Current remote: local (use 'dm remote -v' to list and 'dm remote switch' to switch)

  DOT            BRANCH  SERVER            CONTAINERS                                  SIZE        COMMITS  DIRTY
  default_myapp  master  f67d6806aac60301  /default_orders-db_1,/default_catalog-db_1  315.77 MiB  0        315.77 MiB  
$ docker run -v default_myapp.__root__:/data --volume-driver dm busybox sh -c 'du -sk /data/*'
118139  /data/catalog-db
205228  /data/orders-db