3

It's 2013 and a lot has changed since 2011 (new & obsolete libraries), when most popular node.js deployment questions were answered.

I'd like to know what do you consider the best practices when deploying node.js.

How to auto restart node.js app

Monit?

How to create a cluster and load balance requests (websocket support)

node-http-proxy, HAProxy ?

hook.io has been removed, thus I am not very fond of using node-http-proxy either.

Node.js logging utility

and so on...

Node.js is much more mature now, what have you learned, what do you recommend?

Ben
  • 2,435
  • 6
  • 43
  • 57
  • Why downvote? A lot changed, for example 'cluster (https://github.com/learnboost/cluster)' does not support new node.js, hook.io does not exist anymore,... – Ben Apr 12 '13 at 12:20
  • The downvotes are for the _question_, which isn't appropriate for Stack Overflow (see [the FAQ](http://stackoverflow.com/faq) for more information). I doubt anyone has downvoted to disagree that node.js has changed in the last 2 years – Clive Apr 12 '13 at 12:22
  • Where should I ask this then? – Ben Apr 12 '13 at 12:22
  • I don't know of any site in the SE network that would accept a question this open-ended, but that's not to say there isn't one. You could always open a discussion on [meta](http://meta.stackoverflow.com/) to find out if there is one – Clive Apr 12 '13 at 12:24
  • This question is as open ended as http://stackoverflow.com/questions/7259232/how-to-deploy-node-js-in-cloud-for-high-availability-using-multi-core-reverse-p?rq=1 – Ben Apr 12 '13 at 12:26
  • Yeah perhaps that one shouldn't have been left open. Perhaps it should, who knows. If you read the FAQ, though, you'll understand why this specific question falls under the 'not constructive' banner in my opinion. – Clive Apr 12 '13 at 12:34
  • In this particular case I am with Ben, I am facing the same issues with NodeJs and non other site is so popular to ask this question. I know that @Clive want to sustain the level of the site but please reconsider to open this question. All the question are not yes/no question. – McSas Dec 13 '13 at 15:47
  • @McSas Not allowing discursive questions is something that underpins the Q+A philosophy here - there's no argument that it's a good, interesting, potentially useful question about node.js - it's just not appropriate for _this_ site – Clive Dec 13 '13 at 15:51

1 Answers1

3

Deployment

My choice for deployment is using fleet by substack

I deploy on SmartOS and have the fleet hub and drones running as services which automatically get restarted.

I am working on dispatcher which is a front-end to fleet. Dispatcher will allow to you keep all your repos in a central place such as github or bitbucket and then deploy the latest code from your central git server

Load Balancing

See my answer here for setting up an http and an https server. In my applications the http server is actually another node-http-proxy server. In this setup my application can consist of many small services that are registered with seaport

Routing http server

var http = require('http')
var https = require('https')
var httpProxy = require('http-proxy');
var seaport = require('seaport');
var fs = require('fs')
var inspect = require('eyespect').inspector();
var express = require('express')
function router(data, cb) {
  var app = express()
  var config = data.config
  var logger = data.logger
  var appPort = config.get('application:port');
  var routerConfig = config.get('router')
  var seaHost = config.get('seaport:host')
  var seaPort = config.get('seaport:port')
  var ports = seaport.connect(seaPort, seaHost)
  var proxy = new httpProxy.RoutingProxy();
  app.use(express.methodOverride());
  app.use(app.router)
  var server = http.createServer(app)
  app.all('/api/:service/*', function (req, res) {
    var service = req.params.service
    var ps = ports.query(service);
    if (!ps || ps.length === 0) {
      ps = null
      unavailable(req, res, service, logger);
      service = null
      return
    }
    var item = ps[0]
    // remove /api/service/ from start of the url
    var newURL = req.url.replace(/^\/api\/.*?\//, '/')
    logger.debug('proxying to api service', {
      role: router,
      service: service,
      url: req.url,
      newURL: newURL
    })
    req.url = newURL
    proxy.proxyRequest(req, res, {
      host: item.host,
      port: item.port
    });
    item = null
  })

  var pong = 'PONG'
  app.get('/ping', function (req, res) {
    res.send(pong)
  })
  app.get('/services', function (req, res) {
    return showServices(req, res, ports)
  })
  app.all('/*', function (req, res) {
    var service = 'web'
    var ps = ports.query(service);
    if (!ps || ps.length === 0) {
      unavailable(req, res, service, logger);
      service = null
      ps = null
      return
    }

    proxy.proxyRequest(req, res, {
      host: ps[0].host,
      port: ps[0].port
    });
    ps = null
  })

  var serverPort = routerConfig.port
  server.listen(serverPort, function (err, reply) {
    if (err) { return cb(err); }
    logger.debug('router application online', {
      type: 'router',
      port: serverPort
    });
    var output = {
      port: serverPort,
      server: server
    }
    cb(null, output)
  });
}
function showServices(req, res, ports) {
  var ps = ports.query();
  var data = {
    message: 'Current services registered',
    services: ps
  }
  res.writeHead(200)
  return res.end(JSON.stringify(data))
}

function isServicesURL(url) {
  var pattern = /^\/services/i;
  return pattern.test(url)
}

function unavailable(req, res, service, logger ) {
  var resData = {
    error: 'service unavailable',
    message: 'no servers are available to serve your request',
    url: req.url,
    role: 'router'
    service: service
  };
  logger.debug('router service unavailable', {
    role: 'router',
    responseData: resData
  })
  res.writeHead(500);
  return res.end(JSON.stringify(resData));
}

module.exports = router;

spinUpRouter.js this is the node process that is actually spawned by fleet

var inspect = require('eyespect').inspector()
var assert = require('assert')
var fs = require('fs')
var routerLib = require('./index.js');
var optimist = require('optimist');
var nconf = require('nconf')
var argv = optimist.demand(['config']).argv;
var configFilePath = argv.config
assert.ok(fs.existsSync(configFilePath), 'config file not found at path: ' + configFilePath);
var config = nconf.argv().env().file({file: configFilePath});
var logger = require('loggly-console-logger')
var routerData = {
  config: config,
  logger: logger
}
logger.debug('spinning up router', {
  type: 'router',
  configFilePath: configFilePath
})
routerLib(routerData, function (err, server) {
  inspect('router online')
})

Logging

I use winston to manage my logs. More specifically I use the console and Loggly transports. I do this so much I have wrapped it up into a module loggly-console-logger

Community
  • 1
  • 1
Noah
  • 33,851
  • 5
  • 37
  • 32