Fig - Docker - Setup & Tips - an example on SailsJs and Rails

Recently I have a chance to work with docker to setup a sandbox development environment. Like always, I end up reading a lot of technical blog posts, and getting overwhelmed by the information.

I always wish there was a post that doesn't start with a very technical introduction, but start by introducing the workflow, which is what inspired me this to write this post. So this post is really meant for people to get a quick taste of docker without knowing too much technical details.

If you are looking for more conceptual learner please jump directly to the related links section at the end for more information.

Concepts

If you completely new to the subject, please take 10 mins to watch this video: What is Docker? - CBT Nuggets. It's a really straight forward, and helpful to get to know why docker is useful. The other reference is: What is Docker? - Docker Official Website, which also summarized really well.

This tutorial is more about setting up and getting the fastest basics before mastering docker. For the purpose of this tutorial, just keep in mind that:

  • Image is a template of enviorment
  • Container is application with dependencies

I. Goal

  • (a) Setup the correct environment for docker development
  • (b) Start developing with the tool that simplifies docker command line, and work your way back to understand the basics
(a) Setting Up Your Environment (Mac)

Three things to install for this post:

Docker and Boot2docker: VM tool that mimic linux environment for docker daemon to run on

    brew cask install virtualbox
    brew install docker
    brew install boot2docker

Fig: Orchestration tool that allows you to spin out multiple docker containers at once

    sudo pip install -U fig

After you have everything, you have to initialize boot2docker with:

    boot2docker init  

And when you start a new terminal process, a typical work flow looks similar to this:

    boot2docker up # starting VM and Docker daemon
    $(boot2docker shellinit) # exporting correct docker enviorment variables for fig and docker to use
    fig up # look for fig.yml and start all containers together
    fig stop 
    fig rm # removing the current docker processes
    boot2docker down

Also, some other useful basic docker commands:

docker rm `docker ps -a -q`  
docker stop `docker ps -a -q`  
docker rmi `docker images | grep 'canvas' | cut -d " " -f 1`  
docker exec -it "CONTAINER ID" bash  
docker logs  
docker rmi  
fig up  
fig rm  
fig ps  
fig build  
(b) Example with NodeJs and Ruby on Rails Sharing one MySQL Database

This example is built on top of the official fig rails docker example, but in this post I'm taking the example to another level by:

  • making it work with different setup
  • adding a container for SailsJs app
  • adding a mysql database that's shared by both apps

The final repo is here.


Let's start by creating a fig.yml, a Rails app as well as a SailsJs app:

> rails new myrailsapp
> sails new mysailsapp

//your file structure should look similar to this: 
|── fig.yml
|── rails/
└── sails/

And let's try to work out how to connect sails with mysql server, the fig.yml would look something similar to this:

mysql:  
  image: mysql:5.6 //pulling an existing image from docker
  ports:
    - "3306:3306"
  environment:
    MYSQL_ROOT_PASSWORD: "password"

sails:  
  build: ./sails //building the sails container (it will docker for the dockerfile inside the sails folder)
  command: sh ./start //command to start the application (it could be just sails lift)
  volumes:
    - ./sails:/sails //allocating volumne inside the container by mapping the sails folder to a /sails directory inside the container
  working_dir: /sails //specifying the working directory inside the container. Since we just created a volumne under the directory /sails inside the container, we need to change to that directory in order to execute the current command specify on top.
  links:
    - mysql  //so that sails can use the mysql database
  ports:
    - "1337:1337" //open the port 1337 to public

However, inside the fig.yml script ablove, there are two files that fig.yml depends on: Dockerfile and the start script. So in fact, we need to create them first in order to run fig up. The file structure should now look like this:

|── fig.yml
└── sails/
  |── node_modules/
  |── api/
  |── assets/
  |── ...
  |── app.js
  |── Dockerfile
  └── start

with the Dockerfile:

FROM node:0.10.33
RUN npm install -g sails@0.10.5 grunt bower npm-check-

and the start script:

npm install
npm install sails-mysql
sails lift

And if you go ahead and do a > fig up right now, it's really likely to fail lifting the app, due to some unclear module dependencies issue. The reason being that we are creating a volumne inside the sails container with the exact same contect from our local repo with the fig volumnes setup:

volumes:
  - ./sails:/sails

And the enviorment on your local computer might not be consistent with the image for this container. Therefore, we need to remove /node_modules from local sails directory, and do npm install right before sails lift inside the container.

Now, the sails app will start, but we need to specify the database connection. Make sure connection is specify in ./sails/config/connection.js:

module.exports.connections = {
  someMysqlServer: {
    adapter: 'sails-mysql',
    host: 'mysql',
    user: 'root',
    password: 'password',
    database: 'sails_development'
  }
}

and your ./sails/config/models.js is using this mysql setting:

module.exports.models = {
  connection: 'someMysqlServer',
  migrate: 'alter'
}

If you take a look at the host: mysql. You might wonder how mysql find the correct host ip to connect to mysql server from another container. The anaswer is right in the fig.yml:

links:
  - mysql 

If you open another terminal tab and do the following commands:

>$(boot2docker shellinit)
Writing /Users/neil/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/neil/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/neil/.boot2docker/certs/boot2docker-vm/key.pem
> docker ps
CONTAINER ID        IMAGE                 COMMAND                CREATED              STATUS              PORTS                                            NAMES
49b6af32095a        docker_sails:latest   "sh ./start"           About a minute ago   Up About a minute   0.0.0.0:1337->1337/tcp, 0.0.0.0:3000->3000/tcp   docker_sails_1      
e0ad3926d281        mysql:5.6             "/entrypoint.sh mysq   About a minute ago   Up About a minute   0.0.0.0:3306->3306/tcp                           docker_mysql_1 

$docker exec -it 49b6af32095a bash
root@49b6af32095a:/sails# cat /etc/hosts 
172.17.0.8    49b6af32095a
127.0.0.1   localhost
...
172.17.0.6  docker_mysql_1
172.17.0.6  mysql
172.17.0.6  mysql_1

You will see mysql host is declared and can be found within the sails container network, and that's exactly how links allows you to connect to the mysql server from another container.

One more thing to setup: we need to create a sails_development database inside mysql container. First, let's ctrl + c to stop the docker processes first and do a

> fig up mysql

to start only the mysql container, and in another terminal tab:

> docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS              PORTS                    NAMES
3faa511ccf54        mysql:5.6           "/entrypoint.sh mysq   About a minute ago   Up About a minute   0.0.0.0:3306->3306/tcp   docker_mysql_1 
> docker exec -it 3faa511ccf54 bash
root@3faa511ccf54:/# mysql -uroot -ppassword 
mysql> create database sails_development;

After existing all, do a fig restart, and point your browser do http://dockerhost:1337/. You should now be able to do any CRUD on the api you have inside sails.

The rails part is really similar to sails, so I won't repeat again with too much details, but the file structure will end up like this:

|── fig.yml
|── rails/
  |── ...
  |── Gemfile
  |── Dockerfile
  └── start
└── sails/
  |── ...
  |── app.js
  |── Dockerfile
  └── start

with the Dockerfile, start script, database.yml and final fig.yml:

mysql:
image: mysql:5.6
ports:
  - "3306:3306"
environment:
  MYSQL_ROOT_PASSWORD: "password"

sails:
  build: ./sails
  command: sh ./start
  volumes:
    - ./sails:/sails
  working_dir: /sails
  links:
    - mysql 
  ports:
    - "1337:1337"
    - "3000:3000"

rails:
  build: ./rails
  command: sh ./start
  volumes:
    - ./rails:/rails
  working_dir: /rails
  ports:
    - "3000:3000"
  volumes_from:
    - "sails"
  net: "container:docker_sails_1"

Creating a MySQL Docker Container (10 Nov 2013)

Docker Cheat Sheet

Docker Monitoring

Docker + Fig + Rails on OS X (Sep 22, 2014)

Use Fig and Docker to run a Rails app... without installing Rails (17 January 2014)

rails/Dockerfile

artificialio/docker-sails

comments powered by Disqus