Deploying to a server environment

Virtual Private Servers (VPS), Dedicated Servers, or Infrastructure as a Service (IaaS for example, the likes of Amazon EC2 or Rackspace) and owning our own server machines all have one thing in common: total control over the server environment.

However, with great power comes great responsibility, and there are a few challenges we need to be aware of. This recipe will demonstrate how to overcome these challenges as we safely initialize a Node web app on port 80.

Getting ready

We will, of course, need a remote server environment (or our own setup). It's important to research the best package for our needs.

Dedicated Servers can be expensive. The hardware to software ratio is one to one, we're literally renting a machine.

VPS can be cheaper since they share the resources of a single machine (or cluster), so we're only renting out the resources it takes to host an instance of an operating system. However, if we begin to use resources beyond those assigned, we could hit penalties (downtime, excessive charges) since over usage can affect other VPS users.

IaaS can be relatively cheap, particularly when up-scaling is involved (when we need more resources), though IaaS tends to contain a pay-as-you-go element to its pricing which means the costs aren't fixed and could require extra monitoring.

Our recipe assumes the usage of a Unix/Linux server with the sshd (SSH Service) running. Furthermore, we should have a domain pointed at our server. In this recipe, we'll assume the domain name as nodecookbook.com. Finally, we must have Node installed on our remote server. If difficulties arise, we can use the instructions available at https://www.github.com/joyent/node/wiki/Installation, or for installing via a package manager we can use the instructions at https://www.github.com/joyent/node/wiki/Installing-Node.js-via-package-manager.

We'll be deploying the login app from the second-to-last recipe ofChapter 6, Accelerating Development with Express, so we need this at hand.

How to do it...

To ready our app for transfer to the remote server, we'll remove the node_modules folder (we can rebuild it on the server):

rm -fr login/node_modules

Then we compress the login directory by executing the following:

npm pack login

This will generate a compressed archive named after the app's name and version as given in the package.json file, which will generate the filename application-name-0.0.1.tgz for an untouched Express generated package.json file.

Whatever npm pack called it, let's rename it to login.tgz:

mv application-name-0.0.1.tgz login.tgz #Linux/Mac OS X
rename application-name-0.0.1.tgz login.tgz ::Windows.

Next, we upload login.tgz to our server. For example, we could use SFTP:

Once logged in to the via SFTP, we can issue the following commands:

cd /var/www
put login.tgz

It's not necessary to upload to the /var/www directory, it's just a natural place to put a website.

This assumes that we have SFTPed into our server from the directory holding login.tgz.

Next, we SSH into the server:

ssh -l root nodecookbook.com

Tip

If we're using a Windows desktop, we could SFTP and SSH into our server using putty: http://www.chiark.greenend.org.uk/~sgtatham/putty/.

Once logged in to the remote server, we navigate to /var/www and decompress login.tar.gz:

tar -xvf login.tar.gz

As login.tar.gz decompresses, it recreates our login folder on the server.

To rebuild the node_modules folder, we enter the login folder and use npm to regenerate the dependencies.

cd login
npm -d install

Most servers have a shell-based editor, such as nano, vim, or emacs. We can use one of these editors to change one line in app.js (or otherwise SFTP over a modified app.js):

app.listen(80, function () { process.setuid('www-data'), });

We're now listening on the standard HTTP port, meaning we can access our app without suffixing a port number to its web address. However, since we'll be starting the app as root (necessary in order to bind to port 80), we also pass a callback to the listen method which changes access privileges of the app from root to www-data.

In some cases, dependent upon file permissions, reading or writing to files from our app may no longer work. We can fix this by changing ownership:

chown -R www-data login

Finally, we can start our app with:

cd login
nohup node app.js &

We can ensure that our app is running as www-data with:

ps -ef | grep node

How it works...

We modified app.listen to bind to port 80 and added a callback function that resets the user ID from root to www-data.

Adding a callback to listen isn't limited to Express, it works the same way with a plain httpServer instance.

Running a web server as root is bad practice. If our app was compromised by an attacker, they would have root access to our system via our app's privileged status.

To demote our app, we call process.setuid and pass in www-data. process.setuid. This takes either the name of a user, or the user's UID. By passing in a name, we cause process.setuid to block the event loop (essentially freezing operations) while it cross-references the user string to its UID. This eliminates the potential sliver of time where the app is bound to port 80 and also running as root. In essence, passing a string to process.setuid instead of the underlying UID means nothing can happen until the app is no longer root.

We call our process with nohup and follow up with the ampersand (&). This means we freely end our SSH session without causing our app to terminate along with the session.

The ampersand turns our process into a background task, so we can do other things (like exit) while it runs. nohup means ignore the hangup signal (HUP). HUP is sent to any running processes initiated via SSH whenever the SSH session is terminated. Essentially, using nohup allows our web app to outlive the SSH session.

There's more...

There are other ways to start our app independent from our session, and to bind to port 80 without running the app as root. Plus, we can also run multiple apps and proxy them to port 80 with http-proxy.

Using screen instead of nohup

An alternative to using nohup to achieve independence from our SSH session is screen. We would use it as follows:

screen -S myAppName

This would give us a virtual terminal, from which we could say:

cd login
node app.js

Then we could leave the virtual terminal by pressing Ctrl + A followed by D. We would return to our initial terminal. The virtual terminal would continue to run after we had logged out of SSH. We could also log back in to SSH at any time and say:

screen -r myAppName

Where we would be able to see any console output and stop (Ctrl + C) and start the app.

Using authbind for privileged ports

For this example, we should SSH into our server as a non-root user:

ssh -l dave nodecookbook.com

An alternative way to bind to port 80 is with authbind, which can be installed via our server's package manager. For instance, if our package manager is apt-get we could say:

sudo apt-get install authbind

authbind works by preempting the operating system policies on port binding and exploiting an environment variable called LD_PRELOAD upon execution. Therefore, it never needs to be run with root privileges.

To get it working for us we have to perform some simple configuration work as follows:

sudo touch /etc/authbind/byport 80
sudo chown dave /etc/authbind/byport 80
sudo chmod 500 /etc/authbind/byport 80

This tells authbind to allow the user dave to bind processes to port 80.

We no longer need to change the process UID, so we edit the penultimate line of app.js to:

app.listen(80);

We should also change ownership of the login folder as follows:

chown -R dave login

Now we can start our server without touching the root access at all:

nohup authbind node app.js &

authbind can cause our app to work out of the box, no modifications necessary. However, it currently lacks IPv6 support so it's not yet future-proof.

Hosting multiple processes from port 80

What about serving multiple processes with the default HTTP port?

We can achieve this with the third-party http-proxy module.

npm install http-proxy

Let's say we have two apps one (our login app) to be hosted at login.nodecookbook.com and the other (the server.js file from the very first recipe of this book) to be simply at nodecookbook.com. Both domains point to the same IP.

server.js will be listening on port 8080, and we'll modify login/app.js to listen again to port 3000 as shown in the following code:

app.listen(3000, '127.0.0.1'),

We also added a second argument defining what address to bind to (rather than any address). This prevents our server from being accessed by port.

Let's create a file in a new folder, call it proxy.js, and write the following:

require('http-proxy')
  .createServer({router : {
    'login.nodecookbook.com': 'localhost:3000',
    'nodecookbook.com': 'localhost:8080'
  }}).listen(80, function () {
    process.setuid('www-data'),
  });

The object passed to createServer contains a router property, which in turn is an object instructing http-proxy to route incoming traffic on a particular domain to the correct locally-hosted process according to its port.

We finish off by binding to port 80 and degrading from root to www-data.

To initialize, we must do:

nohup node login/app.js &
nohup node server.js &
nohup node proxy.js &

Since we're binding our proxy server to port 80, these commands must be run as root. If we're operating SSH with a non-root account, we simply prefix these three commands with sudo.

See also

  • Automatic crash recovery discussed in this chapter
  • Continuous deployment discussed in this chapter
  • Hosting with a Platform as a Service provider discussed in this chapter
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.219.65