Tag Archives: production

Deploying Node.js to production

Node.js community has not yet defined any standard or a good practice for deploying applications. And dealing with production caos can be a total pain in the butt. Fortunately after searching a lot over the web, I found some consensus and tips for deploying a Node.js app to production.

  • Express
  • If you typically use Express like me, you can set up the configs for your deployment modes (development, production, etc) in the main file. The below config is simple but easily extensible for supporting your needs. Here I just set the exception verbosity and an initializer for the MongoDB connection which can differ between the deployment modes:

    app.configure('development', function () {
      app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
      mongo.connect('development',logger); // mongodb initialization
    });
    
    app.configure('production', function () {
      app.use(express.errorHandler());
      mongo.connect('production',logger); // mongodb initialization
    });
    
  • Uncaughts Exceptions
  • A Node.js app (without forking or cluster facilities) runs on a single process. An uncaught exception can totally tear your instance down, stopping your service from responding to client’s requests. One way for surpassing this issue is to listen to ‘uncaughtException’, log the error and gracefully shutdown the process.

    process.on('uncaughtException', function (err) {
      console.error('uncaughtException:', err.message)
      console.error(err.stack)
      process.exit(1)})
    
  • Process Manager
  • However, even with this safety measure, your service is offline and doesn’t respond to any requests. First of all, you can daemonize your app by using Linux Upstart so the node process could run in background (forget about screen multiplexing or leaving the terminal open all day!).

    On Debian systems (Ubuntu, etc) run this for installing Upstart:

    $ sudo apt-get install upstart
    

    Ok good, now create a new file (/etc/init/nodeapp.conf) for configuring your new daemon. I borrowed this config and made some little changes:

    #/etc/init/myapp.conf
    description "Node App"
    author "me"
     
    start on startup # it's possible to use "net-device-up IFACE=eth0" for starting the process when the ethernet adapter is up
    stop on shutdown
    
    script
            cd /home/me/nodeapp
            exec sudo -u ubuntu NODE_ENV=production /usr/bin/node /home/me/nodeapp/app.js >> /home/me/nodeapp/log/app.log 2>&1
    end script
    

    In this simple config I teel Upstart to auto-start the process on boot and log output to a specific log file. Upstart also gives you some basics commands for managing the daemon:

    $ start nodeapp
    $ stop nodeapp
    
  • Monit
  • Having your app daemonized doesn’t save yourself from waking up at 3am because of some crashing activity (uncaught exceptions, network timeouts, etc). The rescue tool is Monit. Monit is a awesome utility which monitor and manage processes, files, directories, networking connections on Unix systems. Under the hood it just runs tests in certain intervals and proactively takes some action based on configured rules.

    On Debian systems (Ubuntu, etc) run this for installing Monit:

    $ sudo apt-get install monit
    

    Open “/etc/monit/monitrc” and uncomment the “set httpd” to be able to monitor on localhost:

    set httpd port 2812
        use address localhost  # only accept connection from localhost
    

    This test rule will check if the node and mongodb processes responds to HTTP requests:

     check host nodeapp with address 127.0.0.1
               start "/sbin/start nodeapp"
               stop "/sbin/stop nodeapp"
               if failed port 3004 protocol HTTP
                        request /
                        with timeout 5 seconds
                        then restart
                        if 5 restarts within 5 cycles then timeout
               if failed port 28017 protocol HTTP
                        request /
                        with timeout 5 seconds
                        then exec "/sbin/start mongodb"
    

    The DSL is pretty self-explanatory. For the node process running on 3004, the test will check if HTTP requests are failing, wait 5 seconds, restart the process, and if after 5 restarts the instance still doesn’t respond, it will call timeout. On the other side, for the mongo instance running on 28107, the test will checks if HTTP requests are failing, wait 5 seconds, and start a fresh mongodb process.

    Wait! don’t forget to restart monit:

    $ sudo service monit restart
    

    Monit has lots of configurations for dealing with every possible situation. I recommend searching over the web or just checking their website.

    For managing a Apache web server:

    check process apache with pidfile /var/run/apache2.pid
        start program = "/etc/init.d/apache2 start" with timeout 20 seconds
        stop program  = "/etc/init.d/apache2 stop"
        if totalcpu > 20% for 2 cycles then alert
        if totalcpu > 20% for 5 cycles then restart
    

    Alert the sysadmin by mail is something goes wrong:

    set mailserver localhost
    set alert sysadmin@domain.com
    
       set mail-format {
           from:xxxx@gmail.com
         subject: monit alert --  $EVENT $SERVICE
         message: $EVENT Service $SERVICE
                       Date:        $DATE
                       Action:      $ACTION
                       Host:        $HOST
                       Description: $DESCRIPTION
      }
    

    Finally use this command (or the web GUI) for checking the actual monitoring status:

    $ sudo monit status
    The Monit daemon 5.3.2 uptime: 2h 7m 
    
    Remote Host 'nodeapp'
      status                            Online with all services
      monitoring status                 Monitored
      port response time                0.000s to 127.0.0.1:28017/ [HTTP via TCP]
      port response time                0.001s to 127.0.0.1:3004/ [HTTP via TCP]
      data collected                    Fri, 26 Sep 2013 19:31:00
    
    System 'system_Server'
      status                            Running
      monitoring status                 Monitored
      load average                      [0.01] [0.02] [0.05]
      cpu                               0.1%us 0.2%sy 0.1%wa
      memory usage                      791252 kB [19.5%]
      swap usage                        0 kB [0.0%]
      data collected                    Fri, 26 Sep 2013 19:31:00
    
  • Git
  • Finally the deployment process per-se.

    Set your development environment by editing “~/.ssh/config”:

    Host nodeapp
    Hostname nodeapp-server.com
    
    IdentityFile ~/.ssh/mykey.pem
    User me
    

    Next create a bare (empty) git repo on the server:

    $ mkdir nodeapp.git
    $ cd nodeapp.git
    $ git init --bare
    

    And a git hook in “~/nodeapp.git/hooks/post-receive” for automatically restarting your node process with the deployed modifications:

    #!/bin/sh
     
    GIT_WORK_TREE=/home/me/nodeapp git checkout -f
     
    echo "Restarting node process..."
    sudo restart nodeapp
    
    

    Back in your development machine, add the remote repo to your local setup:

    $ git remote add origin ssh://nodeapp/home/me/nodeapp.git
    

    And finally push the code!

    $ git push origin nodeapp
    

    In this post I only presented some tools and tricks for deploying to production without fear. Eventually you will need to setup a reverse proxy as Nginx in front of your processes for load balancing between them or just for serving static assets and error pages. I will talk about Nginx in a future post. Good hacking!

    Advertisements
    Tagged , , , ,