Meteor Continuous Integration & Deployment - Bitbucket to Digital Ocean.

Problem Statement

Source code for the meteor application is in a private repository in bitbucket. It is deployed as a dockerised node application in Digital Ocean. Master branch is used for staging and the release branch is used for production. Whenever there is a change in the master branch, velocity tests are to run if it all works, it is to be deployed.

Solution

The solution described below uses a free bitbucket account, free wercker account (allows two concurrent builds) and an account in tutum(currently it is free Beta, not sure how it will be in the future when it becomes GA.).

Build

Continuous Integration(CI) is an easy problem to solve with various given examples if the repository is github. I tried shippable and semaphore first, with no success towards the end goal. Shippable was very close, however, it was very slow and found it difficult to customise.

Finally the one which worked like a breeze is Wercker.

Wercker allows you to define your own custom steps in their build and deploy steps. (It was great if they supported the facility to define your own pipelines too.)

For example, define a wercker.yml file in your root repository for meteor’s velocity testing. (refer to this example project) Note that you can define your own custom base docker image for your build step, this is a great value add.

build:
  box: lucidprogrammer/meteor-velocity-base
  steps:
    - script:
        name: Run the test suites
        code: velocity test-packages ./ --ci

Deploy

As wercker allows you to define a different base image for deploy too, it becomes very helpful. Plus it allows you to push the resulting image to your own defined private registry.

I am using tutum for orchestration.

An example, wercker deploy pipeline,

deploy:
  box: lucidprogrammer/meteor-production-base
  steps:
    - script:
        name: bundle meteor source
        code: meteor build --directory /meteor
    - script:
        name: npm install
        code: cd /meteor/bundle/programs/server/ && npm install && touch /meteor/bundle/.foreverignore
    - script:
        # sysctl to fix Waiting...Fatal error: watch ENOSPC Ref: http://stackoverflow.com/questions/16748737/grunt-watch-error-waiting-fatal-error-watch-enospc
        name: sysctl configuration
        code: echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf
    - script:
        name: sysctl
        code: sysctl -p
    - internal/docker-push:
        #provide tutum username and password as env settings in your deploy target in wercker.
        username: $TUTUMUSER
        password: $TUTUMPASSWORD
        repository: tutum.co/yourname/yourrepositoryname
        registry: tutum.co

Things to do at Tutum

First thing to do is to setup a node. (you can use bring your own node or create a Digital Ocean node directly from tutum.) Tag your node, example “staging”. Similar to docker-compose, you can create a tutum.yml file which defines your services.

An example as follows,

mongo:
  image: tutum/mongodb:3.0
  restart: on-failure
  target_num_containers: 1
  tags:
    - staging
  volumes:
    - /root/production/mongo:/data/db
  environment:
    - MONGODB_PASS=password
  deployment_strategy: high_availability
  sequential_deployment: true

web:
  image: tutum.co/yourname/yourrepositoryname:latest
  # Refer to https://github.com/docker/docker/issues/4611
  # Using sysctl in the locum production CMD
  privileged: true
  # when the image is updated, redeploy the container
  autoredeploy: true
  restart: on-failure
  deployment_strategy: high_availability
  sequential_deployment: true
  target_num_containers: 1
  # which of the node/s are we to deploy
  tags:
    - staging
  ports:
    - "80:80"
  links:
    - mongo
  environment:
    - ROOT_URL=http://yourapp.com
    - MONGO_URL=mongodb://admin:password@mongo:27017
    - PORT=80
  command: /usr/local/bin/forever ./main.js
  working_dir: /meteor/bundle
  ports:
    - "80:80"