Configure Node Apps with Nconf and ETCD

Recently, I have been working with, and learning a lot about the new distributed operating system, CoreOS. It is really interesting and makes managing micro-service architectures, quite a bit easier than manually SSHing into each machine and dealing every node individually. At the heart of CoreOS sits ETCD, a distributed key / value store. Internally CoreOS uses it for node discovery, communication and orchestration. Unlike other key / value stores, ETCD feels a bit more like a file system with directories and files. A directory can contain multiple directories and files where a file can contain a single value. For example, you might store the name of the environment as /company/metadata/environment = staging. It is a data hierarchy, which means it maps very well to JavaScript Objects.

Because there is such a simple translation between JavaSript objects and ETCD data structures, it is pretty good fit for storing configuration for various thing. Which is why I set out to write a ETCD storage backend for NConf. If you are already using nconf for your node apps, it is really easy to plug in.

npm install nconf-etcd2 --save  

Once installed all you need to do is require it, and drop it into nconf.

var nconf = require('nconf') // require nconf first  
var Etcd  = require('nconf-etcd2') // will attatch itself to nconf ( if nconf was required first)

nconf.use('etcd', { /* options */});  
nconf.get('your:data')  

Done! The act of telling nconf to use etcd does the initial load from your ETCD cluster synchronously and transforms it into a plain object for you. By default, it will try to connect to localhost running on port 4001, but you can change that by passing a hosts array of <IP>:<PORT>. You can also set the primary namespace directory for your data, which defaults to nconf

nconf.use('etcd', {  
   namespace:'foobar',
   hosts:['192.168.0.1:4001', '10.50.0.1:4001'] 
});

There are a couple of things to note

Namespaces

The namespace managed internally for you and is not exposed in the working data set. For example, with a namespace as foobar, if you set the nconf value of baz:biff, this would create the key /foobar/baz/biff

// /foobar/baz/biff 1

{
    baz:{
        biff: 1
    }
}

Persistence

Upon the initial load from ETCD data is stored in memory and all access is local. This keeps Things snappy, and cuts down on the network chatter. If and when you would like to write data back to ETC, you must call nconf.save()

Data Transofmation

The etcd backend will translate change in data structures back to ETC. With the above example, if we set baz to 1, and called save, this would delete the baz directory, and any data in it, then create a new file with a value of 1

// /foobar/baz/biff 1
nconf.set('baz', 1)  
nconf.save(); // baz directory deleted

// /foobar/baz 1 

Syncrounous Calls

Under the hood, nconf-etcd2 supports the synchronous forms of load and save from nconf, as well as the async methods. In short, if you pass a callback to either load or save, they will be async, otherwise they will be synchronous.

nconf.save() // sync call  
nconf.save( console.log ) // async call  

CoreOS and Etc and very powerful and flexible tools, and nconf-etcd2 makes it very easy to plug into your node applications. More over, you don't have to re-work any of your code to deal with asynchronous hijinks if you don't want to.

configuration nconf coreos etcd node.js