dockerode – Not another Docker.io Node.js module

While developing nodechecker.com I looked for Node.js modules that implemented the Docker.io Remote API.

I found a few, the majority was  unfinished lacking a lot of functionality and tests.

Things I was looking for:

  • Complete feature set
  • Tests
  • Leverage streams
  • Good abstraction

I didn’t found a module with these characteristics or with a code base ready to accept contributions.

At the time I ended up forking https://npmjs.org/package/docker.io fixed some bugs it had (this module was revamped recently and it’s better now) and implemented some functionality I needed. But all this was really quick patch, I knew that someday I had to do something about this.

That day came yesterday, and another Docker.io node.js module was born… it’s main objectives are those four points I enumerated in this post.

I called it dockerode (docker+node yes naming stuff isn’t a skill I’m proud of…)

How it works and what it can do

Initialization:

var Docker = require('dockerode');
var docker = new Docker({socketPath: '/var/run/docker.sock'});
var docker2 = new Docker({host: 'http://192.168.1.10', port: 3000});

By leveraging streams it allows to do cool stuff like:

container.attach({stream: true, stdout: true, stderr: true}, function(err, stream) {
  stream.pipe(process.stdout);
});

Containers and images are entities:

docker.createContainer({Image: 'ubuntu', Cmd: ['/bin/bash']}, function(err, container) {
  container.start(function(err, data) {
    //...
  });
});

var container = docker.getContainer('71501a8ab0f8');
container.inspect(function(err, data) {
  console.log(data);
});

//...

“docker run” implementation:

//docker.run(image, cmd, stream, temporary, callback)
docker.run('ubuntu', 'uname -a', process.stdout, true, function(err, data) {
  console.log(data.StatusCode);
});

Installation and tests

npm install dockerode

Tests use mocha and chai.js

Status

  • Feature set is almost complete, tests are about 85%.
  • New WebSockets endpoints added in Docker v0.6 will be supported soon.

Scaling Nodechecker.com

Nodechecker is an app I built a few weeks ago that automatically tests all node.js modules available in the NPM registry. Check these slides from require(‘lx’) for more details, they explain the idea and motivation behind Nodechecker but the technical stuff is now outdated as you will see in this post.

It all started with a PoC around node.js and docker.io, it worked well for the NPM use case but it didn’t scale for other use cases like on demand testing.

Nodechecker new architecture

Nodechecker new architecture

This is the new architecture, it relies on multiple Docker instances completely abstracted each one running nodechecker-engine, I call this entity worker nodes.

The beautiful thing about this is, that I can add more worker nodes without even rebooting/restarting anything. Just boot another VM with Docker already installed and run nodechecker-engine specifying the nodechecker-balancer‘s ip address in the argument.

When the engine starts it will make a dnode call to nodechecker-balancer basically saying “i’m here and ready to rock please add me to the available worker nodes list

Nodechecker-balancer always dispatches work to the node that have the smallest work queue, if you add new nodes they will probably be the ones getting new work orders.

This architecture is so agile that if you just want to fiddle a little with it you don’t need to run a balancer, just run a single worker node without specifying an ip address in it’s nodechecker-engine. Then in the nodechecker-crawler and api just use the worker ip address.

You may even run crawler, api and balancer and even a worker inside a single machine :)

Everything is still in a rough state, feel free to contribute.

Binary encoding deprecation in Node.js

Recently I had to implement an endpoint for an archaic API in Node.js, which required some binary manipulation.

Giving a quick peak at documentation, you will see that binary encoding should be avoided at all costs and is already deprecated, instead you should rely on buffers.

Here goes a really basic and quick example doing the same thing using encoding and then using buffers.

What you should NOT be doing:

stream.write(utils.pack('N', payload.data.length + 4) + payload.data,
 'binary', payload.callback);

Instead you should be doing:

var myb = new Buffer(payload.data.length + 4);
myb.writeUInt32BE((payload.data.length + 4), 0);
myb.write(payload.data, 4);
stream.write(myb, payload.callback);

Realize that when you use a buffer in the write method, encoding does not fallback to utf8 by default.

SSH connection’s channel limit and Node.js

Recently while rebuilding Outkept, I learned something interesting. I knew there was a channel limit for each SSH connection, what I didn’t know was that the default limit was so low (usually 8 channels per connection by default in the majority of ssh daemons).

In Outkept each sensor uses a channel, if I exclusively used one channel per sensor that meant that only 8 sensors per server was allowed.

This meant that I needed to implement a queue based pool of some sort, in order to “multiplex” the available channels. Something like the gist below, just pass it a SSH2 module connection object and use the send method.

This code does not feature stream error treatment, so complement it or only use it when missing something isn’t important.

In this use case, each sensor interval is user defined, if someone defined a few sensors with intervals in the millisecond range the queue is going to quickly fill forever. I monitor this alerting the user in the dashboard for a “queued up” scenario, in this situation there is only two solutions: increase the intervals or increase the channels limit.

Rebuilding Outkept

Outkept DashboardLast November I did a talk at Codebits about a private project that ended to be Outkept, it was basically a project that was build for a  specific use case and then I generalized it.

At the time I developed it in Java, but after the talk I decided to rebuild it for more generalist use cases and start everything entirely from scratch in node.js.

Outkept allows you to control and monitor a large server cluster, without needing to manually specify which and what servers to monitor/control. Instead you just define which subnets you want to control and then, using SSH, Outkept‘s crawlers will look for servers and what do they support.

Rebuilding it in node.js was awesome, allowing me to tackle my node.js skills and dig more into node’s scene while using a lot ‘earlier adopter’ tech.

Right now Outkept v2 supports everything the old version supported and even more, things are quicker and more fluid. New dashboard connects using shoe (sockjs) and the new system relies entirely in multiple node.js processes.

I will talk more about this project later, but right now I would love some feedback. The codebase is big and mostly uncommented, in the next posts will fix this and talk a bit about it.

If you want to give it a try just follow these instructions. If you need help you can reach me at petermdias@gmail.com

Follow

Get every new post delivered to your Inbox.

Join 183 other followers