Deploying your site

ICEkit is designed to be easily deployed and scaled using Docker. See More about Docker.

Your project’s Dockerfile is used to prepare a Docker image. It looks something like:

FROM interaction/icekit:local

WORKDIR /opt/project_template/

COPY package.json /opt/project_template/
RUN npm-install.sh && rm -rf /root/.npm
ENV PATH=/opt/project_template/node_modules/.bin:$PATH

COPY bower.json /opt/project_template/
RUN bower-install.sh && rm -rf /root/.cache/bower

COPY requirements.txt /opt/project_template/
RUN pip install --no-cache-dir -r requirements.txt
RUN md5sum requirements.txt > requirements.txt.md5

ENV ICEKIT_PROJECT_DIR=/opt/project_template

COPY . /opt/project_template/

RUN manage.py collectstatic --noinput --verbosity=0
RUN manage.py compress --verbosity=0

When you build an image, Docker steps through the Dockerfile, following the instructions therein.

Deploy a Demo to Docker Cloud

Use the Deploy to Docker Cloud button to create a new default ICEkit stack on Docker Cloud.

Deploy to Docker Cloud

(This is mostly for testing and demonstration purposes - you won’t be able to customise your project when deploying the official ICEkit Docker image this way.)

Deploying your project for the first time

This section is a summary of the Docker getting started instructions.

The process of deploying a site for the first time is:

  1. Create a Docker Hub account. You can use this for all the official Docker services, including Docker Cloud.

  2. Build and tag an image from your Dockerfile:

    docker build -t "$DOCKER_ID_USER/$PROJECT_NAME:master"
    

    You can use anything instead of “master” here - we recommend using the name of the git branch you use for production deployments.

  3. Push the image to Docker Hub:

    docker push "$DOCKER_ID_USER/$PROJECT_NAME:master"
    

    It should now appear in the Repositories section on Docker Cloud.

  4. Set up a new stack in Docker Cloud. This needs more documentation (see the Docker Cloud docs for more). For now, here’s a sample production stackfile:

    django:
      environment:
        - AWS_ACCESS_KEY_ID=...
        - AWS_SECRET_ACCESS_KEY=...
        - AWS_STORAGE_BUCKET_NAME=...
        - BASE_SETTINGS_MODULE=production
        - EMAIL_HOST=...
        - EMAIL_HOST_PASSWORD=...
        - EMAIL_HOST_USER=...
        - FORCE_SSL=yes
        - PGHOST=...
        - PGPASSWORD=...
        - PGUSER=...
        - PYTHONWARNINGS=ignore
        - 'SECRET_KEY=...'
        - SITE_DOMAIN=www.mydomain.com.au
        - 'VIRTUAL_HOST=www.domain.com.au,https://www.mydomain.com.au'
      expose:
        - '8080'
      image: '$DOCKER_ID_USER/$PROJECT_NAME:master'
      links:
        - elasticsearch
        - redis
      restart: on-failure
      sequential_deployment: true
      tags:
        - production
      volumes:
        - /opt/$PROJECT_NAME/var
    elasticsearch:
      image: 'interaction/elasticsearch-icu:1'
      restart: on-failure
      tags:
        - production
    redis:
      command: redis-server --appendonly yes
      image: 'redis:3-alpine'
      restart: on-failure
      tags:
        - production
      target_num_containers: 2
    

Subsequent deployments

When you want to release an update to your site, we suggest following these steps:

  1. Build your code into a Docker Image.
  2. Ensure that tests run on the Docker Image.
  3. Push the Image to Docker Hub.
  4. Deploy the Image using Docker Cloud.
  5. Check it worked (view some key pages).

Automating releases with Travis and Docker Cloud

In Travis

A Continuous Integration service like Travis can run tests and other commands automatically for every pushed revision.

The project template includes a .travis.yml file which instructs Travis to build the Docker image, run tests on the built image, and if the tests pass, to push the image to Docker Hub.

In Docker Cloud

You can set autoredeploy: true in your Docker Cloud stackfile to automatically redeploy services when their Docker images are updated.

Here’s an excerpt from a stackfile, which auto-redeploys when your {docker_hub_account}/{image_name}:{tag} image is updated (e.g. interaction/acmi:staging )

django:
  autoredeploy: true
  image: '{docker_hub_account}/{image_name}:{tag}'
  ...
elasticsearch:
  ...
redis:
  ...

Fixing a broken deployment

There are any number of reasons why a deployment can break. Assuming your app is unreachable, the best place to start is by looking at the logs in Docker Cloud. You can also restart and redeploy the services using the Docker Cloud tools.

Rolling back a deployment

(You almost never want to do this – the consistency of Docker and a good git workflow makes it easy to check whether a deployment will break well before it hits the production server.)

First, identify the revision you want to roll back to. Depending on your git workflow, this will either be the last merge to the production branch, or the last release tag, or you may need to guess. This is your target revision.

Rolling back the data

The main difficulty in rolling back is whether any Django migrations were applied between the new/breaking release and the target revision, and whether those migrations can be reversed.

If migrations have been applied, it’s probably still going to be quicker and less messyto fix the deployment or restore a database backup than to roll back migrations.

Further, if any migrations don’t have a reverse() operation specified, then they can’t be rolled back, and you’ll either have to restore from a database backup, improvise a reverse() operation, or do nothing and hope the target revision is compatible enough to work.

If you’re still determined to roll back the migrations, compare the breaking and target revisions to see if any migrations were added to the codebase. If so, then you must manually apply those migrations in reverse using:

manage.py migrate {app} {4-digit migration number to reverse minus 1, or 'zero'}

Rolling back code

After you’ve rolled back migrations, update the Docker Cloud stack file and change master tag to a full commit hash of the target release (you can get it from Travis build history) and click redeploy.

That was fun

When the dust has settled, consider how to modify your process to avoid the need to roll back deployments. We sugges:

  • Use Docker for consistent, repeatable server environments
  • Use a git-flow-based branching workflow for careful separation of features from main branches
  • Write tests, and never deploy unless unit tests pass.
  • Deploy release candidates to a staging environment first, which has recent copies of production data and media.
  • Deploy all of staging to production, not just the features that passed tests.