Scaling Django Migrations on AWS Part 2


In our last post we described the steps necessary to build a Hubot-driven workflow for running Django’s migrate command in an ECS cluster.

Putting Hubot to work requires us to write some Javascript or CoffeeScript that processes commands we send it via Slack along with a response that Hubot sends back to the Slack channel.

To start, we need to configure some global variables:

  • NS_ENVIRONMENT is the namespace we’ll be searching for when we request our [Elastic Beanstalk][eb_lnk] environment’s environment variables
  • MIGRATE_CONTAINER_FAMILY is the container family we’ll provide to ECS the registerTask command
  • MIGRATE_CONTAINER_NAME is the name we’ll use to identify our container that runs the migrate command
  • MIGRATE_CONTAINER_IMAGE is our Docker image that we run the migrate command from (typically our main Django application image)
  • MIGRATE_CONTAINER_MEMORY is the amount of memory to give to the container (make sure the instance has sufficient memory to run this task)
  • MIGRATE_CONTAINER_COMMAND is the actual command we’ll run inside the container
  • ECS_CLUSTER is the name of the ECS cluster we’ll run our task in
  • AWS_REGION is our AWS region (i.e. us-east-1)
  • EB_APPLICATION_NAME is the name of our Elastic Beanstalk application
  • EB_ENVIRONMENT_NAME is the name of our Elastic Beanstalk environment

Next, we’ll define a function that we’ll use to fetch our Elastic Beanstalk environment variables:

This function calls describeConfigurationSettings from the AWS SDK, parses the environment variables using the NS_ENVIRONMENT variable we provided earlier, and creates an object containing the names and values of the environment’s current variables.

The final step is to listen for a particular message from the Slack channel and start our task:

After initializing the SDK, we call getEBEnvironmentVariables and create a container definition using the result of the function call (which includes a variable holding the environment variables we fetched). At the same time, we also define mount points for our logs and create the volume to hold the logs on the instance.

/path/to/logs should be replaced with the path that your Django application writes its logs to (i.e. /var/log for /var/log/django.log). This ensures that any logs written by ./manage.py migrate are accessible after the container is removed or killed. Accessing those logs on the instance is accomplished by visiting the sourcePath defined in the volumes variable.

Finally, we register the task definition via registerTaskDefinition and run the task in that function’s callback.

Once this CoffeeScript is added to Hubot and Hubot is restarted, sending the migrate message to your Hubot should kickoff the workflow.