Objectives

This lab should help you to deploy your node app from the EWD module on AWS for scaling and load balancing.

Deploying a Node.js or Hapi app to AWS

These initial steps are identical to the start of the TLS lab we did a few weeks ago.

Install node, npm and hapi on an AWS EC2 instance

  1. Go to AWS and select the EC2 service

  2. Launch a micro instance using the default Amazon Linux 2 AMI

  3. At Step 6 (Configure Security Group), create a new security group that allows access to TCP port 3000 as well as 22/SSH

  4. Connect to your instance using SSH

  5. On your instance, install the current version of nvm (node version manager):

    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
    . ~/.nvm/nvm.sh
  6. Next install node 10.15.3 LTS and npm and the latest version of hapi (on your instance):

    nvm install 10.15.3
    npm install hapi


Test your intallation by deploying a basic Hapi application

You may wish to test everything is ok by downloading the very basic Hapi "Hello World" application provided here. Start the application with:

node hapi_example.js

and point your browser to http://ip-address:3000, replacing ip-address with your instance's IP address

Deploy an app with a local mongo database

Here we will deploy the final version of the donation-web sample app (from Eamonn de Leastar). This application uses a mongo database, which we configure first.

Install mongod on EC2 instance:

Firstly you will need to configure yum to find mongodb:

# SSH into your instance first
sudo nano /etc/yum.repos.d/mongodb-org-4.0.repo

Then enter the following into this file:

[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc

Then install and run mongod:

sudo yum install -y mongodb-org
mkdir db
sudo mongod -dbpath db --bind_ip_all

From another terminal, make a new SSH connection to your instance and download and configure a recent version of the donation web app

curl https://codeload.github.com/wit-hdip-comp-sci-2018/donation-web/zip/donation.11.end -o donation.zip
unzip donation.zip
cd donation-web-donation.11.end/
npm install

Create a .env file

nano .env

and add the following to it

cookie_name=donation-web
cookie_password=secretpasswordnotrevealedtoanyone
db=mongodb://localhost/donation

Now start the server

node index.js

Deploy an app with a separate mongo database

If your application runs on a single instance, you can deploy a local database on that instance. However this is not a good design from the perspective of fault tolerance, high availability and scalability. Configuring auto scaling and load balancing on such an application could result in data inconsistency as we would have multiple non-replicated copies of the database.

Launch a new EC2 instance and use a security group that has the default mongo port (27017) open. N.B. This should normally always be done in a private subnet as exposing databases to the public Internet is a major security risk. For this practice exercise though we will keep it public to make it easy to log in using SSH and carry out some configuration. Tag this instance 'database'

Install and run mongod on this new 'database' instance (as in previous step of lab)

Go back to your other instance where donation is installed and edit the .env file

nano .env

to point it to the database instance. Change 'localhost' to your database instance's private IP address.

cookie_name=donation-web
cookie_password=secretpasswordnotrevealedtoanyone
db=mongodb://172.1.2.3/donation     (replace with correct IP address)

Scaling a Node App - Starting your App or Database automatically

When creating the Launch Configuration for scaling you can specify a script to run at startup at Step 3 (Create Launch Configuration) - Expand Advanced Details and enter your script under User Data. The following would start the sample app.

#!/bin/bash
su - ec2-user -c 'cd donation-web; node index.js'

Note that this can also be done when launching instances manually.

Load Balancing a Node App

Sticky Sessions

If your app uses sessions for authentication of requests after users log in and sets a session cookie, then you'll need to enable stickinesss. This means that the load balancer binds a client's session to a particular target instance for a specified period of time. This makes sure that a sequence of requests from a particular user to the app all go to the same target.

You can only configure sticky sessions after completing the wizard to create the target group.

  • First, create the target group as normal.
  • Select your target group and click on the Description tab where you can see a button "Edit Attributes" at the bottom. Check the "Enable" button for "Stickiness" and enter the desired duration.

As an alternative, classic load balancers can be configured for session stickiness that is tied to a target instance's session cookie.

Load balancer http port and path (route) configuration

When creating an application load balancer, you configure ports in two places:

  • Load balancer port (default 80)
  • Target group port (default 80)

The first is the port the load balancer listens on. The second is the port your target application listens on. These do not need to be the same port number.

For a node app, you might wish to have the load balancer listen on the default port and forward the traffic on to port 3000 on the targets. Then you would browse to http://lbname rather than http://lbname:3000. Note that your security group configuration needs to be consistent with the choices you make here.

You also configure the path for HTTP health checks in Step 4. The default route is '/'. This should be changed if your app does not listen on this path.

Demonstrating load balancing with a node application

To show the local hostname in a browser and log test requests to the console, you could use the value of os.hostname() by editing routes.js to have:

const os = require("os");

and add a route like this one (in routes.js)

{
  method: 'GET',
  path: '/testlb',
  handler: function (request, h) {
     return('Server: ' + os.hostname());
  }
},