





















































When you start learning how to use Docker, you play around running a single container in a single project. Soon after, you want to start multiple Docker containers in multiple projects.
A nifty little tool called Fig helps you do just that.
At the end of this blog post, you will have the following small sample app running on Docker using a MongoDB database and a Node.js web server in two separate containers:
But first, some background.
Before this article, I had written my own shell script for the setup of named-data.education. But as I was exploring the realm of Docker orchestration, I came to the conclusion that throwing my script out and going with an existing solution would be the better practice.
I ended up choosing Fig because it supports (and simplifies) the workflow I had implemented with my custom shell script. Also, it was recently acquired by Docker, and will soon be integrated with Docker.
This state diagram illustrates the life cycle of a typical Docker container:
These actually correspond to Docker commands, the most prominent ones being build and run:
This diagram illustrates how Fig orchestrates multiple Docker containers:
These transitions also correspond to Fig commands, with fig up being the champion here. (There is also a fig run command, but it has a marginal role in comparison.)
Okay, let's get practical.
As an example, let's say your app is really simple and consists of just a database and a web frontend:
In Docker, this architecture is implemented by running each part in a separate container. The external connection, between NodeJS and the Internet, is realized by exposing a port, whereas the internal connection, between NodeJS and MongoDB, is realized using a link.
For this sample application, create a directory with the following layout with the listings shown below, or get the source code from GitHub:
fig-nodejs-mongodb-example/
fig.yml
web/
Dockerfile
liststorage.coffee
package.json
server.coffee
fig.yml:
web:
build: ./web
ports:
- "8080:8080"
links:
- db
db:
image: mongo:2.6
Dockerfile:
FROM node:0.10
ADD package.json /code/
WORKDIR /code
RUN npm install
ADD . /code
CMD ["./node_modules/.bin/coffee", "./server.coffee"]
liststorage.coffee:
{MongoClient, ObjectID} = require 'mongodb'
class module.exports.ListStorage
constructor: ->
@ready = false
@collection = null
MongoClient.connect 'mongodb://db_1:27017/list', (err, db) =>
throw err if err
db.createCollection 'list', (err, collection) =>
throw err if err
@ready = true
@collection = collection
toArray: (callback) ->
return callback new Error 'not ready' unless @ready
@collection.find().toArray (err, list) ->
return callback err if err
callback null, list
push: (item, callback) ->
doc = item: item
@collection.insert doc, {w: 1}, (err, result) ->
return callback err if err
callback null
remove: (_id, callback) ->
@collection.remove {_id: ObjectID(_id)}, {w: 1}, (err, result) ->
return callback err if err
callback null
package.json: (The bare minimum is to make npm install happy, but normally npm init should be used to create this file.)
{
"dependencies": {
"body-parser": "^1.6.6",
"coffee-script": "^1.8.0",
"express": "^4.8.5",
"handlebars": "^2.0.0-beta.1",
"mongodb": "^1.4.9"
}
}
server.coffee:
#!/usr/bin/env coffee
require 'coffee-script/register'
ListStorage = require('./liststorage').ListStorage
listStorage = new ListStorage
handlebars = require 'handlebars'
indexTemplate = handlebars.compile '''
<title>List</title>
<h1>List</h1>
<ul>
{{#each items}}
<li><a href="/delete/{{_id}}">[×]</a> {{item}}</li>
{{/each}}
</ul>
<form method="POST">
<label>Add something:</label>
<input name="item" autofocus="autofocus" />
<input type="submit" value="Submit" />
</form>
'''
express = require 'express'
bodyParser = require 'body-parser'
app = express()
app.use bodyParser.urlencoded extended: true
app.get '/', (req, res) ->
listStorage.toArray (err, items) ->
throw err if err
res.send indexTemplate items: items
app.post '/', (req, res) ->
listStorage.push req.body.item, (err) ->
throw err if err
res.redirect '/'
app.get '/delete/:_id', (req, res) ->
listStorage.remove req.params._id, (err) ->
throw err if err
res.redirect '/'
app.listen 8080
First, make sure you have Docker and Fig installed. (This example has been tested with Fig 0.5.2 and Docker 1.2.0. On OS X, brew install fig works fairly well, together with docker-osx or boot2docker.)
Then, open the terminal and type the following:
cd fig-nodejs-mongodb-example
fig up -d
Then (assuming you run docker-osx), open http://localdocker:8080/ and play around - knowing that you did not have to manually set up two virtual machines!
The sections in fig.yml are called “services.”
As Fig allows scaling by running multiple instances of services, link aliases get an additional suffix inside containers compared to plain Docker; db_1 for Fig versus just db for Docker.
Also, as Docker (and Fig) manage the container's /etc/hosts file, you get a db_1 host for free.
Now, go have fun and keep containin’!
Felix Rabe has been programming and working with different technologies and companies at different levels since 1993. Currently, he is researching and promoting Named Data Networking (http://named-data.net/), an evolution of the Internet architecture that currently relies on the host-bound Internet Protocol.