Bruning Questions: Building Nodestack on a Smart Machine

February 08, 2013 - by Mr. Max Bruning

ask-mr-bruning-logo

For the past few weeks, I have been studying performance of an application that uses MongoDB, Node.js, and jmeter. The application was written by a third party, and they set up the application so that each component runs in its own Smart Machine. I wanted to blog about this, but then decided to make sure I can get MongoDB and Node.js to work together. The combination of MongoDB, Node.js, and Smart Machine is nodestack. For those unfamiliar with nodestack, click here. Basically, node does processing, MongoDB is a database engine, and SmartOS provides the operating system and development environment. A Smart Machine is an OS virtualized zone on SmartOS. Click here for a high level description of Smart Machines. And see http://smartos.org/ for a description of SmartOS.

In this blog, I'll describe the steps to take to get a nodestack system up and running. This does not assume any knowledge of Node.js or of MongoDB. You can learn about Node.js at http://nodejs.org, and about MongoDB at http://www.mongodb.org. Note that similar steps would be needed if you were using a different database engine.

Step 1: If you don't have one, set up an account on the Joyent Public Cloud by going to joyent.com and click on "Get Started." It will ask you for credit card information. You will also need to add an ssh key so that you can access any machines you provision. Then provision a "NodeJS," version 1.4.0 image with 2GB of memory (2GB is more than needed). This image contains both Node.js and MongoDB.

Once the machine is provisioned, you'll see that the machine has 2 IP addresses. One is an internal address (10.x.y.z), the other is external. You should ssh into the external IP address with the ssh key you set up.

Step 2: After logging in to your newly provisioned machine, we'll upgrade the packages to get the most recently supported versions. First, let's see what's available for Node.js.

# node -v
v0.8.9
# mongo --version
MongoDB shell version: 2.2.0
#
# pkgin search node
tex-pst-node-1.15    Draw connections using pstricks
p5-Tree-DAG_Node-1.06nb3  Class for representing nodes in a tree
nodejs-0.8.9 >       V8 JavaScript for clients and servers
nodejs-0.8.6 >       V8 JavaScript for clients and servers
nodejs-0.8.18 <      V8 JavaScript for clients and servers
nodejs-0.8.16 <      V8 JavaScript for clients and servers
nodejs-0.8.14 <      V8 JavaScript for clients and servers
nodejs-0.8.11 =      V8 JavaScript for clients and servers
nodejs-0.6.21 >      Evented I/O for V8 javascript
munin-node-2.0.0     System monitoring tool, client version
munin-common-1.4.5   Common components between a munin node and master

=: package is installed and up-to-date
<: package is installed but newer version is available
>: installed package has a greater version than available package

And let's see if MongoDB is running.

# pgrep mongod
12266

So, nodejs-0.8.18 is the most recent version of node that is available.

Now let's upgrade.

# pkgin upgrade
calculating dependencies... done.

4 packages to be upgraded: mongodb-2.2.0 nodejs-0.8.11 pkgin-0.6.0 smtools-20121010

4 packages to be installed: mongodb-2.2.2 nodejs-0.8.18 pkgin-0.6.0nb1 smtools-20130103 (166M to download, 0B to install)

proceed ? [Y/n] Y
downloading packages...
mongodb-2.2.2.tgz                   100%   53MB  11.5MB/s  11.5MB/s   00:01    
nodejs-0.8.18.tgz                   100% 5064KB   4.9MB/s   4.9MB/s   00:00    
pkgin-0.6.0nb1.tgz                  100%  329KB 329.1KB/s 329.1KB/s   00:00    
smtools-20130103.tgz                100%   25KB  25.4KB/s  25.4KB/s   00:00    
removing packages to be upgraded...
removing mongodb-2.2.0...
===========================================================================
The following users are no longer being used by mongodb-2.2.0,
and they can be removed if no other software is using them:

 mongodb

===========================================================================
===========================================================================
The following files are no longer being used by mongodb-2.2.0,
and they can be removed if no other packages are using them:

 /opt/local/etc/mongodb.conf

===========================================================================
===========================================================================
The following directories are no longer being used by mongodb-2.2.0,
and they can be removed if no other packages are using them:

       /var/mongodb/journal
    /var/mongodb
    /var/log/mongodb

===========================================================================
removing nodejs-0.8.11...
removing pkgin-0.6.0...
===========================================================================
The following files are no longer being used by pkgin-0.6.0,
and they can be removed if no other packages are using them:

  /opt/local/etc/pkgin/repositories.conf

===========================================================================
===========================================================================
The following directories are no longer being used by pkgin-0.6.0,
and they can be removed if no other packages are using them:

      /opt/local/etc/pkgin

===========================================================================
removing smtools-20121010...
pkg_install warnings: 0, errors: 0
installing packages...
installing mongodb-2.2.2...
mongodb-2.2.2: /opt/local/etc/mongodb.conf already exists
===========================================================================
The following files should be created for mongodb-2.2.2:

      /etc/rc.d/mongodb (m=0755)
          [/opt/local/share/examples/rc.d/mongodb]

===========================================================================
passwd: password information changed for mongodb
//////////////////////////////////////////////////////////////////////////////

This package is SMF enabled, which means you can use SMF to 'enable',
'disable' or 'restart' the persistent daemon process, e.g.:

  svcadm enable mongodb:default

The SMF manifest was automatically imported now.

See our wiki on what's SMF and how to use it to your advantage:

  http://wiki.joyent.com/display/smart/About+the+Service+Management+Facility

//////////////////////////////////////////////////////////////////////////////
installing nodejs-0.8.18...
installing pkgin-0.6.0nb1...
pkgin-0.6.0nb1: /opt/local/etc/pkgin/repositories.conf already exists
===========================================================================
$NetBSD: MESSAGE,v 1.3 2010/06/10 08:05:00 is Exp $

First steps before using pkgin.

. Modify /opt/local/etc/pkgin/repositories.conf to suit your platform
. Initialize the database :

  # pkgin update

===========================================================================
installing smtools-20130103...
pkg_install warnings: 0, errors: 0
reading local summary...
processing local summary...
updating database: 100%

And let's make sure we have the latest version running running.

We'll use the SMF (Service Management Facility, see smf(5)).

# pgrep mongod
12266   <-- still the same as before upgrade

And we'll check the version of node and mongodb.

# node -v
v0.8.18
# mongo --version
MongoDB shell version: 2.2.2
#

We'll restart MongoDB to make sure were using the upgraded version.

# svcs -a | grep mongo
disabled       19:37:06 svc:/pkgsrc/quickbackup-mongodb:default
online         19:39:57 svc:/pkgsrc/mongodb:default
# svcadm restart svc:/pkgsrc/mongodb:default
#
# pgrep mongo
14164
# 

Step 3: Now we'll install the node-MongoDB-native driver. This driver contains code that can be used by a node application to talk with MongoDB.

# npm install mongodb
npm http GET https://registry.npmjs.org/mongodb
npm http 200 https://registry.npmjs.org/mongodb
npm http GET https://registry.npmjs.org/mongodb/-/mongodb-1.2.11.tgz
npm http 200 https://registry.npmjs.org/mongodb/-/mongodb-1.2.11.tgz
npm http GET https://registry.npmjs.org/bson/0.1.6
npm http 200 https://registry.npmjs.org/bson/0.1.6
npm http GET https://registry.npmjs.org/bson/-/bson-0.1.6.tgz
npm http 200 https://registry.npmjs.org/bson/-/bson-0.1.6.tgz

> bson@0.1.6 install /opt/local/node_modules/mongodb/node_modules/bson
> node install.js || (exit 0)

================================================================================
=                                                                              =
=  Attempting to build bson c++ extension                                      =
=   Windows: no build will be attempted as binaries are prepackaged            =
=   Unix: on failure the package will still install without the C++ extension  =
=                                                                              =
================================================================================
execvp(): No such file or directory
child process exited with code 127
mongodb@1.2.11 node_modules/mongodb
└── bson@0.1.6
#

Now we'll make sure we've got everything.

# cd /opt/local/node_modules/mongodb
# ls
CONTRIBUTING.md  Readme.md  install.js  node_modules  upload.py
Makefile         index.js   lib         package.json
#
# npm install
npm http GET https://registry.npmjs.org/dox/0.2.0
npm http GET https://registry.npmjs.org/uglify-js/1.2.5
npm http GET https://registry.npmjs.org/ejs/0.6.1
npm http GET https://registry.npmjs.org/nodeunit/0.7.4
npm http GET https://registry.npmjs.org/github3
npm http GET https://registry.npmjs.org/markdown/0.3.1
npm http GET https://registry.npmjs.org/gleak/0.2.3
npm http GET https://registry.npmjs.org/step/0.0.5
npm http GET https://registry.npmjs.org/async/0.1.22
npm http 200 https://registry.npmjs.org/ejs/0.6.1
npm http GET https://registry.npmjs.org/ejs/-/ejs-0.6.1.tgz
npm http 200 https://registry.npmjs.org/uglify-js/1.2.5
npm http GET https://registry.npmjs.org/uglify-js/-/uglify-js-1.2.5.tgz
npm http 200 https://registry.npmjs.org/markdown/0.3.1
npm http GET https://registry.npmjs.org/markdown/-/markdown-0.3.1.tgz
npm http 200 https://registry.npmjs.org/dox/0.2.0
npm http 200 https://registry.npmjs.org/nodeunit/0.7.4
npm http GET https://registry.npmjs.org/dox/-/dox-0.2.0.tgz
npm http GET https://registry.npmjs.org/nodeunit/-/nodeunit-0.7.4.tgz
npm http 200 https://registry.npmjs.org/github3
npm http 200 https://registry.npmjs.org/step/0.0.5
npm http 200 https://registry.npmjs.org/gleak/0.2.3
npm http GET https://registry.npmjs.org/github3/-/github3-1.0.0.tgz
npm http GET https://registry.npmjs.org/step/-/step-0.0.5.tgz
npm http GET https://registry.npmjs.org/gleak/-/gleak-0.2.3.tgz
npm http 200 https://registry.npmjs.org/ejs/-/ejs-0.6.1.tgz
npm http 200 https://registry.npmjs.org/uglify-js/-/uglify-js-1.2.5.tgz
npm http 200 https://registry.npmjs.org/markdown/-/markdown-0.3.1.tgz
npm http 200 https://registry.npmjs.org/dox/-/dox-0.2.0.tgz
npm http 200 https://registry.npmjs.org/nodeunit/-/nodeunit-0.7.4.tgz
npm http 200 https://registry.npmjs.org/github3/-/github3-1.0.0.tgz
npm http 200 https://registry.npmjs.org/async/0.1.22
npm http GET https://registry.npmjs.org/async/-/async-0.1.22.tgz
npm http 200 https://registry.npmjs.org/gleak/-/gleak-0.2.3.tgz
npm http 200 https://registry.npmjs.org/step/-/step-0.0.5.tgz
npm http 200 https://registry.npmjs.org/async/-/async-0.1.22.tgz
npm http GET https://registry.npmjs.org/github-flavored-markdown
npm http GET https://registry.npmjs.org/commander/0.5.2
npm http GET https://registry.npmjs.org/request/2.12.0
npm http GET https://registry.npmjs.org/tap
npm http 200 https://registry.npmjs.org/github-flavored-markdown
npm WARN deprecated github-flavored-markdown@1.0.1: This project is long out of date. Use 'marked' instead.
npm http GET https://registry.npmjs.org/github-flavored-markdown/-/github-flavored-markdown-1.0.1.tgz
npm http 200 https://registry.npmjs.org/commander/0.5.2
npm http GET https://registry.npmjs.org/commander/-/commander-0.5.2.tgz
npm http 200 https://registry.npmjs.org/request/2.12.0
npm http GET https://registry.npmjs.org/request/-/request-2.12.0.tgz
npm http 200 https://registry.npmjs.org/commander/-/commander-0.5.2.tgz
npm http 200 https://registry.npmjs.org/request/-/request-2.12.0.tgz
npm http 200 https://registry.npmjs.org/github-flavored-markdown/-/github-flavored-markdown-1.0.1.tgz
npm http 200 https://registry.npmjs.org/tap
npm http GET https://registry.npmjs.org/tap/-/tap-0.4.0.tgz
npm http 200 https://registry.npmjs.org/tap/-/tap-0.4.0.tgz
npm http GET https://registry.npmjs.org/slide
npm http GET https://registry.npmjs.org/runforcover
npm http GET https://registry.npmjs.org/mkdirp
npm http GET https://registry.npmjs.org/difflet
npm http GET https://registry.npmjs.org/deep-equal
npm http GET https://registry.npmjs.org/buffer-equal
npm http GET https://registry.npmjs.org/glob
npm http GET https://registry.npmjs.org/nopt
npm http 200 https://registry.npmjs.org/slide
npm http GET https://registry.npmjs.org/slide/-/slide-1.1.3.tgz
npm http 200 https://registry.npmjs.org/difflet
npm http 200 https://registry.npmjs.org/mkdirp
npm http GET https://registry.npmjs.org/difflet/-/difflet-0.2.3.tgz
npm http GET https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.4.tgz
npm http 200 https://registry.npmjs.org/buffer-equal
npm http 200 https://registry.npmjs.org/runforcover
npm http GET https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.0.tgz
npm http GET https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgz
npm http 200 https://registry.npmjs.org/deep-equal
npm http GET https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgz
npm http 200 https://registry.npmjs.org/difflet/-/difflet-0.2.3.tgz
npm http 200 https://registry.npmjs.org/slide/-/slide-1.1.3.tgz
npm http 200 https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.4.tgz
npm http 200 https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.0.tgz
npm http 200 https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgz
npm http 200 https://registry.npmjs.org/nopt
npm http GET https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz
npm http 200 https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgz
npm http 200 https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz
npm http 200 https://registry.npmjs.org/glob
npm http GET https://registry.npmjs.org/glob/-/glob-3.1.17.tgz
npm http 200 https://registry.npmjs.org/glob/-/glob-3.1.17.tgz
npm http GET https://registry.npmjs.org/bunker
npm http GET https://registry.npmjs.org/traverse
npm http GET https://registry.npmjs.org/charm
npm http GET https://registry.npmjs.org/minimatch
npm http GET https://registry.npmjs.org/graceful-fs
npm http GET https://registry.npmjs.org/inherits
npm http 200 https://registry.npmjs.org/bunker
npm http 200 https://registry.npmjs.org/graceful-fs
npm http GET https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgz
npm http GET https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgz
npm http GET https://registry.npmjs.org/abbrev
npm http 200 https://registry.npmjs.org/charm
npm http GET https://registry.npmjs.org/charm/-/charm-0.0.8.tgz
npm http 200 https://registry.npmjs.org/traverse
npm http 200 https://registry.npmjs.org/inherits
npm http GET https://registry.npmjs.org/traverse/-/traverse-0.6.3.tgz
npm http GET https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz
npm http 200 https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgz
npm http 200 https://registry.npmjs.org/charm/-/charm-0.0.8.tgz
npm http 200 https://registry.npmjs.org/abbrev
npm http GET https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz
npm http 200 https://registry.npmjs.org/traverse/-/traverse-0.6.3.tgz
npm http 200 https://registry.npmjs.org/minimatch
npm http GET https://registry.npmjs.org/minimatch/-/minimatch-0.2.9.tgz
npm http 200 https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz
npm http 200 https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgz
npm http 200 https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz
npm http 200 https://registry.npmjs.org/minimatch/-/minimatch-0.2.9.tgz
npm http GET https://registry.npmjs.org/burrito
npm http GET https://registry.npmjs.org/sigmund
npm http GET https://registry.npmjs.org/lru-cache
npm http 200 https://registry.npmjs.org/burrito
npm http GET https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgz
npm http 200 https://registry.npmjs.org/lru-cache
npm http GET https://registry.npmjs.org/lru-cache/-/lru-cache-2.0.4.tgz
npm http 200 https://registry.npmjs.org/sigmund
npm http GET https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz
npm http 200 https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgz
npm http 200 https://registry.npmjs.org/lru-cache/-/lru-cache-2.0.4.tgz
npm http 200 https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz
npm http GET https://registry.npmjs.org/uglify-js
npm http GET https://registry.npmjs.org/traverse
npm http 304 https://registry.npmjs.org/traverse
npm http GET https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgz
npm http 200 https://registry.npmjs.org/uglify-js
npm http GET https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgz
npm http 200 https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgz
npm http 200 https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgz
gleak@0.2.3 node_modules/gleak

step@0.0.5 node_modules/step

async@0.1.22 node_modules/async

ejs@0.6.1 node_modules/ejs

uglify-js@1.2.5 node_modules/uglify-js

dox@0.2.0 node_modules/dox
├── commander@0.5.2
└── github-flavored-markdown@1.0.1

markdown@0.3.1 node_modules/markdown

github3@1.0.0 node_modules/github3
└── request@2.12.0

nodeunit@0.7.4 node_modules/nodeunit
└── tap@0.4.0 (deep-equal@0.0.0, buffer-equal@0.0.0, mkdirp@0.3.4,
slide@1.1.3, difflet@0.2.3, nopt@2.1.1, glob@3.1.17, runforcover@0.0.2)
#

And check now to see what npm has installed.

# npm list
mongodb@1.2.11 /opt/local/node_modules/mongodb
├── async@0.1.22
├── bson@0.1.6
├─┬ dox@0.2.0
│ ├── commander@0.5.2
│ └── github-flavored-markdown@1.0.1
├── ejs@0.6.1
├─┬ github3@1.0.0
│ └─┬ request@2.12.0
│   ├─┬ form-data@0.0.3
│   │ ├── async@0.1.9
│   │ └─┬ combined-stream@0.0.3
│   │   └── delayed-stream@0.0.5
│   └── mime@1.2.7
├── gleak@0.2.3
├── markdown@0.3.1
├─┬ nodeunit@0.7.4
│ └─┬ tap@0.4.0
│   ├── buffer-equal@0.0.0
│   ├── deep-equal@0.0.0
│   ├─┬ difflet@0.2.3
│   │ ├── charm@0.0.8
│   │ └── traverse@0.6.3
│   ├─┬ glob@3.1.17
│   │ ├── graceful-fs@1.1.14
│   │ ├── inherits@1.0.0
│   │ └─┬ minimatch@0.2.9
│   │   ├── lru-cache@2.0.4
│   │   └── sigmund@1.0.0
│   ├── inherits@1.0.0
│   ├── mkdirp@0.3.4
│   ├─┬ nopt@2.1.1
│   │ └── abbrev@1.0.4
│   ├─┬ runforcover@0.0.2
│   │ └─┬ bunker@0.1.2
│   │   └─┬ burrito@0.2.12
│   │     ├── traverse@0.5.2
│   │     └── uglify-js@1.1.1
│   ├── slide@1.1.3
│   └── yamlish@0.0.5
├── step@0.0.5
└── uglify-js@1.2.5

Step 4: Let's test to see if everything is in place and working. The node-mongodb-native source code includes tests and examples. We'll git the source, copy the examples to the appropriate place, and run some of the examples. I have not had luck yet with the tests (possible version mismatch?), but the examples run fine.

# cd /opt/local/node_modules/mongodb
# ls
CONTRIBUTING.md  Readme.md  install.js  node_modules  upload.py
Makefile         index.js   lib         package.json
#

Note that the examples and test directories are not present. Let's get them.

# cd /var/tmp
# git clone https://github.com/christkv/node-mongodb-native.git
Cloning into 'node-mongodb-native'...
remote: Counting objects: 11961, done.        
remote: Compressing objects: 100% (2820/2820), done.        
remote: Total 11961 (delta 8130), reused 11671 (delta 7929)        
Receiving objects: 100% (11961/11961), 3.73 MiB | 4.02 MiB/s, done.
Resolving deltas: 100% (8130/8130), done.
#
# cd node-mongodb-native
# ls
HISTORY   Readme.md  dev   examples      index.js    lib           test
Makefile  TODO       docs  external-libs  install.js  package.json
#

So the directories are slightly different, even though the package.json file is getting them from the same place as the git command. (You can check by looking at the package.json file in either directory. Figuring this out is left as an exercise for the reader.

First, we'll copy the examples directory from the location into which we just downloaded into the directory where the node-mongodb-native driver was installed.

# cp -r /var/tmp/node-mongodb-native/examples /opt/local/node_modules/mongodb

And now cd to the directory where the driver was installed and try some examples.

# cd /opt/local/node_modules/mongodb/examples
# ls
admin.js   cursor.js  info.js     replSetServersQueries.js  types.js
blog.js    gridfs.js  oplog.js    replSetServersSimple.js
capped.js  index.js   queries.js  simple.js
#

And run one of the examples. You may want to run additional examples.

# node simple.js
Connecting to localhost:27017
There are 3 records in the test collection. Here they are:
{ a: 0, _id: 51109daae0af6d8b13000001 }
created at Fri Jan 16 1970 17:47:23 GMT+0000 (UTC)

{ a: 2, _id: 51109dabe0af6d8b13000003 }
created at Fri Jan 16 1970 17:47:23 GMT+0000 (UTC)

{ a: 1, _id: 51109daae0af6d8b13000002 }
created at Fri Jan 16 1970 17:47:23 GMT+0000 (UTC)

#

This post describes the first steps necessary to get Nodestack up and running, but those interested in a more in depth investigation should sign up to join Shaun Verch of MongoDB and me for a day-long workshop on building and deploying your app in Nodestack at Scale 11x on Feb 22.

You can also submit your DTrace, MDB, or SmartOS questions to be answered in a future column by emailing MrBruning@joyent.com, ask at #BruningQuestions, or attend one of my upcoming courses.

:

Sign up Now for Instant Cloud Access

Get Started

View PricingSee Benchmarks