Building Nodestack on a Smart Machine

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 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 nodejs.org, and about MongoDB at 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 -vv0.8.9# mongo --versionMongoDB shell version: 2.2.0## pkgin search nodetex-pst-node-1.15    Draw connections using pstricksp5-Tree-DAG_Node-1.06nb3  Class for representing nodes in a treenodejs-0.8.9 >       V8 JavaScript for clients and serversnodejs-0.8.6 >       V8 JavaScript for clients and serversnodejs-0.8.18 <      V8 JavaScript for clients and serversnodejs-0.8.16 <      V8 JavaScript for clients and serversnodejs-0.8.14 <      V8 JavaScript for clients and serversnodejs-0.8.11 =      V8 JavaScript for clients and serversnodejs-0.6.21 >      Evented I/O for V8 javascriptmunin-node-2.0.0     System monitoring tool, client versionmunin-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 mongod12266

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

Now let's upgrade.

# pkgin upgradecalculating dependencies... done.4 packages to be upgraded: mongodb-2.2.0 nodejs-0.8.11 pkgin-0.6.0 smtools-201210104 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] Ydownloading packages...mongodb-2.2.2.tgz                   100%   53MB  11.5MB/s  11.5MB/s   00:01nodejs-0.8.18.tgz                   100% 5064KB   4.9MB/s   4.9MB/s   00:00pkgin-0.6.0nb1.tgz                  100%  329KB 329.1KB/s 329.1KB/s   00:00smtools-20130103.tgz                100%   25KB  25.4KB/s  25.4KB/s   00:00removing 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: 0installing 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:defaultThe 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: 0reading 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 mongod12266   <-- still the same as before upgrade

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

# node -vv0.8.18# mongo --versionMongoDB shell version: 2.2.2#

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

# svcs -a | grep mongodisabled       19:37:06 svc:/pkgsrc/quickbackup-mongodb:defaultonline         19:39:57 svc:/pkgsrc/mongodb:default# svcadm restart svc:/pkgsrc/mongodb:default## pgrep mongo14164#

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 mongodbnpm http GET https://registry.npmjs.org/mongodbnpm http 200 https://registry.npmjs.org/mongodbnpm http GET https://registry.npmjs.org/mongodb/-/mongodb-1.2.11.tgznpm http 200 https://registry.npmjs.org/mongodb/-/mongodb-1.2.11.tgznpm http GET https://registry.npmjs.org/bson/0.1.6npm http 200 https://registry.npmjs.org/bson/0.1.6npm http GET https://registry.npmjs.org/bson/-/bson-0.1.6.tgznpm 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 directorychild process exited with code 127mongodb@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# lsCONTRIBUTING.md  Readme.md  install.js  node_modules  upload.pyMakefile         index.js   lib         package.json## npm installnpm http GET https://registry.npmjs.org/dox/0.2.0npm http GET https://registry.npmjs.org/uglify-js/1.2.5npm http GET https://registry.npmjs.org/ejs/0.6.1npm http GET https://registry.npmjs.org/nodeunit/0.7.4npm http GET https://registry.npmjs.org/github3npm http GET https://registry.npmjs.org/markdown/0.3.1npm http GET https://registry.npmjs.org/gleak/0.2.3npm http GET https://registry.npmjs.org/step/0.0.5npm http GET https://registry.npmjs.org/async/0.1.22npm http 200 https://registry.npmjs.org/ejs/0.6.1npm http GET https://registry.npmjs.org/ejs/-/ejs-0.6.1.tgznpm http 200 https://registry.npmjs.org/uglify-js/1.2.5npm http GET https://registry.npmjs.org/uglify-js/-/uglify-js-1.2.5.tgznpm http 200 https://registry.npmjs.org/markdown/0.3.1npm http GET https://registry.npmjs.org/markdown/-/markdown-0.3.1.tgznpm http 200 https://registry.npmjs.org/dox/0.2.0npm http 200 https://registry.npmjs.org/nodeunit/0.7.4npm http GET https://registry.npmjs.org/dox/-/dox-0.2.0.tgznpm http GET https://registry.npmjs.org/nodeunit/-/nodeunit-0.7.4.tgznpm http 200 https://registry.npmjs.org/github3npm http 200 https://registry.npmjs.org/step/0.0.5npm http 200 https://registry.npmjs.org/gleak/0.2.3npm http GET https://registry.npmjs.org/github3/-/github3-1.0.0.tgznpm http GET https://registry.npmjs.org/step/-/step-0.0.5.tgznpm http GET https://registry.npmjs.org/gleak/-/gleak-0.2.3.tgznpm http 200 https://registry.npmjs.org/ejs/-/ejs-0.6.1.tgznpm http 200 https://registry.npmjs.org/uglify-js/-/uglify-js-1.2.5.tgznpm http 200 https://registry.npmjs.org/markdown/-/markdown-0.3.1.tgznpm http 200 https://registry.npmjs.org/dox/-/dox-0.2.0.tgznpm http 200 https://registry.npmjs.org/nodeunit/-/nodeunit-0.7.4.tgznpm http 200 https://registry.npmjs.org/github3/-/github3-1.0.0.tgznpm http 200 https://registry.npmjs.org/async/0.1.22npm http GET https://registry.npmjs.org/async/-/async-0.1.22.tgznpm http 200 https://registry.npmjs.org/gleak/-/gleak-0.2.3.tgznpm http 200 https://registry.npmjs.org/step/-/step-0.0.5.tgznpm http 200 https://registry.npmjs.org/async/-/async-0.1.22.tgznpm http GET https://registry.npmjs.org/github-flavored-markdownnpm http GET https://registry.npmjs.org/commander/0.5.2npm http GET https://registry.npmjs.org/request/2.12.0npm http GET https://registry.npmjs.org/tapnpm http 200 https://registry.npmjs.org/github-flavored-markdownnpm 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.tgznpm http 200 https://registry.npmjs.org/commander/0.5.2npm http GET https://registry.npmjs.org/commander/-/commander-0.5.2.tgznpm http 200 https://registry.npmjs.org/request/2.12.0npm http GET https://registry.npmjs.org/request/-/request-2.12.0.tgznpm http 200 https://registry.npmjs.org/commander/-/commander-0.5.2.tgznpm http 200 https://registry.npmjs.org/request/-/request-2.12.0.tgznpm http 200 https://registry.npmjs.org/github-flavored-markdown/-/github-flavored-markdown-1.0.1.tgznpm http 200 https://registry.npmjs.org/tapnpm http GET https://registry.npmjs.org/tap/-/tap-0.4.0.tgznpm http 200 https://registry.npmjs.org/tap/-/tap-0.4.0.tgznpm http GET https://registry.npmjs.org/slidenpm http GET https://registry.npmjs.org/runforcovernpm http GET https://registry.npmjs.org/mkdirpnpm http GET https://registry.npmjs.org/diffletnpm http GET https://registry.npmjs.org/deep-equalnpm http GET https://registry.npmjs.org/buffer-equalnpm http GET https://registry.npmjs.org/globnpm http GET https://registry.npmjs.org/noptnpm http 200 https://registry.npmjs.org/slidenpm http GET https://registry.npmjs.org/slide/-/slide-1.1.3.tgznpm http 200 https://registry.npmjs.org/diffletnpm http 200 https://registry.npmjs.org/mkdirpnpm http GET https://registry.npmjs.org/difflet/-/difflet-0.2.3.tgznpm http GET https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.4.tgznpm http 200 https://registry.npmjs.org/buffer-equalnpm http 200 https://registry.npmjs.org/runforcovernpm http GET https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.0.tgznpm http GET https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgznpm http 200 https://registry.npmjs.org/deep-equalnpm http GET https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgznpm http 200 https://registry.npmjs.org/difflet/-/difflet-0.2.3.tgznpm http 200 https://registry.npmjs.org/slide/-/slide-1.1.3.tgznpm http 200 https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.4.tgznpm http 200 https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.0.tgznpm http 200 https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgznpm http 200 https://registry.npmjs.org/noptnpm http GET https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgznpm http 200 https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgznpm http 200 https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgznpm http 200 https://registry.npmjs.org/globnpm http GET https://registry.npmjs.org/glob/-/glob-3.1.17.tgznpm http 200 https://registry.npmjs.org/glob/-/glob-3.1.17.tgznpm http GET https://registry.npmjs.org/bunkernpm http GET https://registry.npmjs.org/traversenpm http GET https://registry.npmjs.org/charmnpm http GET https://registry.npmjs.org/minimatchnpm http GET https://registry.npmjs.org/graceful-fsnpm http GET https://registry.npmjs.org/inheritsnpm http 200 https://registry.npmjs.org/bunkernpm http 200 https://registry.npmjs.org/graceful-fsnpm http GET https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgznpm http GET https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgznpm http GET https://registry.npmjs.org/abbrevnpm http 200 https://registry.npmjs.org/charmnpm http GET https://registry.npmjs.org/charm/-/charm-0.0.8.tgznpm http 200 https://registry.npmjs.org/traversenpm http 200 https://registry.npmjs.org/inheritsnpm http GET https://registry.npmjs.org/traverse/-/traverse-0.6.3.tgznpm http GET https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgznpm http 200 https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgznpm http 200 https://registry.npmjs.org/charm/-/charm-0.0.8.tgznpm http 200 https://registry.npmjs.org/abbrevnpm http GET https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgznpm http 200 https://registry.npmjs.org/traverse/-/traverse-0.6.3.tgznpm http 200 https://registry.npmjs.org/minimatchnpm http GET https://registry.npmjs.org/minimatch/-/minimatch-0.2.9.tgznpm http 200 https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgznpm http 200 https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgznpm http 200 https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgznpm http 200 https://registry.npmjs.org/minimatch/-/minimatch-0.2.9.tgznpm http GET https://registry.npmjs.org/burritonpm http GET https://registry.npmjs.org/sigmundnpm http GET https://registry.npmjs.org/lru-cachenpm http 200 https://registry.npmjs.org/burritonpm http GET https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgznpm http 200 https://registry.npmjs.org/lru-cachenpm http GET https://registry.npmjs.org/lru-cache/-/lru-cache-2.0.4.tgznpm http 200 https://registry.npmjs.org/sigmundnpm http GET https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgznpm http 200 https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgznpm http 200 https://registry.npmjs.org/lru-cache/-/lru-cache-2.0.4.tgznpm http 200 https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgznpm http GET https://registry.npmjs.org/uglify-jsnpm http GET https://registry.npmjs.org/traversenpm http 304 https://registry.npmjs.org/traversenpm http GET https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgznpm http 200 https://registry.npmjs.org/uglify-jsnpm http GET https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgznpm http 200 https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgznpm http 200 https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgzgleak@0.2.3 node_modules/gleakstep@0.0.5 node_modules/stepasync@0.1.22 node_modules/asyncejs@0.6.1 node_modules/ejsuglify-js@1.2.5 node_modules/uglify-jsdox@0.2.0 node_modules/dox├── commander@0.5.2└── github-flavored-markdown@1.0.1markdown@0.3.1 node_modules/markdowngithub3@1.0.0 node_modules/github3└── request@2.12.0nodeunit@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 listmongodb@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# lsCONTRIBUTING.md  Readme.md  install.js  node_modules  upload.pyMakefile         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.gitCloning 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# lsHISTORY   Readme.md  dev   examples      index.js    lib           testMakefile  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# lsadmin.js   cursor.js  info.js     replSetServersQueries.js  types.jsblog.js    gridfs.js  oplog.js    replSetServersSimple.jscapped.js  index.js   queries.js  simple.js#

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

# node simple.jsConnecting to localhost:27017There 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.

We offer comprehensive training for Triton Developers, Operators and End Users.



Post written by Mr. Max Bruning