Session 3 | Node Elastic Beanstalk Workshop

NodeJS logo

Node Elastic Beanstalk Workshop

In this workshop, you'll deploy a pre-built, database-backed Node/Express application using Elastic Beanstalk and CodePipeline.

Before you start, make sure that you are in the same region where you created your EC2 instances since this is where you have created previous key pairs.

You'll want to download this Express app. Unzip it and put it in a directory you want to work with on the command line. We're specifically not using git to share the code so that you can set it up with your own git repository later on, and this exercise will mimic the same steps you'll take for a brand new Express project.

A few quick notes about this Express app:

  • This app uses express 4.17.1.
  • We're using Sequelize as the ORM for a Postgres database. This tutorial assumes that you have Postgres installed locally.
  • This app has one resource (tasks). The app does not have tests.
  • The routes available are:
    1. GET /
    2. GET /tasks
    3. GET /tasks/:id
    4. POST /tasks, which takes a title (string) and priority (integer) as body keys.
    5. DELETE /tasks/:id

If you'd like to test out the app locally before you deploy to Elastic Beanstalk, you can do that with these commands:

  1. npm install
  2. npm install -g sequelize-cli
  3. npm install -g nodemon
  4. createdb taskmanager, which will create a Postgres database locally
  5. sequelize db:migrate, which creates the tasks table in the database from the migration file
  6. sequelize db:seed:all, which will create three rows of dummy data
  7. npm run dev
  8. Navigate to localhost:3000/tasks in your browser. You can also try out the routes listed above either in the browser or using the Postman app for POST and DELETE requests.

AWS RDS Logo

Setting up the RDS Database

First, we'll want to set up our RDS database so that we have the endpoint, DB name + password, and port to use when setting up our Elastic Beanstalk environment.

  • In your AWS Management Console, open RDS.
  • Click the orange Create Database button.
  • Don't click the "Easy Create" slider, as there are two custom configurations that we want for our database that this option takes away from us.
  • Select a Postgres database for Engine Type.
  • Screen shot for selecting RDS engine
  • Scroll down and select Free Tier for Template.
  • In the input fields for DB instance identifier, master username, and master password, type "taskmanager". This is (obviously) not best practice for a production database, but for now, this will make it easier for you to remember the inputs since we'll keep all of them the same.
  • Screen shot for setting up RDS security
  • Scroll down to Storage and uncheck Enable storage autoscaling. We won't need that anyway, but we also dn't want to get charged if for some reason we do go over that limit.
  • Scroll to Connectivity and click Additional connectivity configuration. Make sure that NO is checked off in the Publicly Accessible section (it should be, but just double check).
  • In that same section, find VPC Security Groups and select the box that says "Create New".
  • For New VPC security group name, enter "rds-taskmanager". This is an arbitrary name, but it will help us identify it later.
  • Screen shot for connecting RDS
  • Scroll down to Additional Configuration and open up the options.
  • In Initial database name, type taskmanager.
  • Screen shot for naming RDS database
  • Uncheck Enable automatic backups. This would be good to have on if we were creating a real app, but to save on S3 storage, we'll skip backups.
  • Scroll down to the bottom and click the orange Create database button at the bottom.

Your database is being created. In the meantime, open up the Node app that you downloaded and change the config/config.js file by making the production section look like this:

  production: {
    dialect: "postgres",
    host: process.env.RDS_HOSTNAME,
    username: process.env.RDS_USERNAME,
    password: process.env.RDS_PASSWORD,
    port: process.env.RDS_PORT,
    database: process.env.RDS_DB_NAME,
  }

This sets the database name, username, password, host, and port to be environment variables that we'll set in our EB environment.


AWS Elastic Beanstalk Logo

Preparing an Elastic Beanstalk App and Environment

For this exercise, we'll just create a development environment; however, it is possible to have many (ie. dev, prod).

  • Open the Elastic Beanstalk console in a new tab. Keep your RDS dashboard open because we'll need to reference values from here later.
  • Click the orange Create Application button.
  • For application name, type "taskmanager".
  • For platform, choose Node.js, and for platform branch, select "Node.js 12 running on 64bit Amazon Linux 2"
  • For Application Code, keep "Sample application" selected. We won't use the sample app, but we also don't want to upload our code through a zip file or S3. Eventually, we'll set up a pipeline to push our code from Github to EB, but that comes later.
  • Don't click the blue button! Instead, click the grey Configure more options button. This is what your screen should look like:
  • EB create app screenshot

This configuration dashboard shows you what Elastic Beanstalk is about to be setup for you. You can customize anything that needs to be added or changed. For now, we'll do the following:

  • Under the Software box, click "Edit". NOTE: this is a screenshot from a Rails environment, so ignore those environment variables.
  • Screen shot for modifying software config
  • Scroll to the bottom where you'll see inputs for Environment properties. In here, you'll need to add five keys, which are the database environment variables we referenced in our config/config.js file earlier plus one more variable:
    • You'll first need to set NODE_ENV: use the value production
    • RDS_PORT: use 5432 (this is the Postgres port, which you can also find on your RDS dashboard)
    • RDS_DB_NAME: use taskmanager
    • RDS_USERNAME: use taskmanager
    • RDS_PASSWORD: use taskmanager
    • RDS_HOSTNAME: go back to your RDS dashboard and find the value for endpoint which should be on the Connectivity & security tab. It should look like a URL. Copy that and paste it as the hostname value in your Elastic Beanstalk configuration window. If it's blank, that just means AWS hasn't finished provisioning the database. Just keep refreshing.
    • RDS endpoint screenshot
  • This is what your environment properties should look like (with your own values). The order does not matter. If everything looks right, click Save.
  • EB environment properties screenshot

Next, click "Modify" under the Security configurations box. Choose a keypair from the dropdown (make sure this is a keypair that you have access to!), then save. You do not need to put anything for service role or IAM instance profile.

Screen shot for modifying ssh key

The rest of the settings are beyond the scope of this exercise. It may be tempting to click on the Database setting, but we'll skip this for now, as we want our database to be separated from our Beanstalk environment. This is best practice so that your data is preserved even if you delete your app's environment.

Click on the orange Create app button at the bottom of the main screen.

Compared to Heroku, AWS Elastic Beanstalk takes a surprisingly long time to provision and configure your resources. This process will probably take about 5-10 minutes.


Coffee and Donuts

Take a Break

Whew. Before we create the CodePipeline (which will deploy our app), we need to fix one security group issue, but we can't do that until our security group is provisioned from Beanstalk. Take a short break and come back when your Beanstalk dashboard looks like this:

Successful Elastic Beanstalk dashboard

AWS Security Group Logo

Fixing Security Group Issues

We created a new security group, rds-taskmanager, for our RDS database, but right now it will not accept any incoming traffic from our Elastic Beanstalk app. Let's fix that:

  • In a new tab, open up the EC2 dashboard.
  • On the left-hand side, click Security Groups under "Network & Security".
  • Screen shot for security group location
  • Find the security group that says rds-taskmanager under Group Name and select it.
  • Click on the tab that says Inbound. You should see that it has a rule for inbound PostgreSQL, but the source is YOUR personal IP address (check this by Googling "what is my IP address" and comparing the two), not the IP of the EC2 instance that will need to access it.
  • Click the Edit button.
  • Delete the current source (which is your computer's IP address), and start typing "sg". You should see all of your security groups pop up. Select the one that has "awseb" in its name -- this is the security group that AWS Elastic Beanstalk made for us, which is the one that our EC2 instance will be in. We want to accept PG traffic from that instance, so we'll reference its security group.
  • Screenshot selecting correct security group
  • Click the orange Save button.

AWS CodePipeline Logo

Setting up a Pipeline

CodePipline can integrate build and test tools in addition to deploy, but for now, we'll create a pipeline that just watches for a new version of code to be pushed to GitHub, then deploys that code automatically to our Beanstalk environment.

First, we'll need to initialize our Node project as a git project and create a repository on GitHub for the code.

  • From the command line, do git init, git add ., and git commit -m 'Initial commit'.
  • In your GitHub account, create a new repository and push your code to it.

Next, from your AWS Management console, open up CodePipeline in a separate tab and follow these steps:

  • Click the orange Create Pipeline button.
  • For pipeline name, enter "taskmanagerdevpipeline" (or something to that effect).
  • Screen shot for code pipeline settings
  • Leave everything else as is, and click Next.
  • For source provider, choose GitHub and connect your account.
  • In Repository, select the repository you created earlier. If nothing appears in repository, wait a bit, then re-click in the box. Your repos will appear eventually.
  • Select your master branch, and use GitHub Webhooks.
  • Screen shot for code pipeline source settings
  • Click Next.
  • We will skip the build stage, so select the grey button on bottom that says Skip build stage.
  • Screen shot for code pipeline skip build
  • Don't worry about the scary-sounding message about not skipping the deploy stage. For this stage, select AWS Elastic Beanstalk in the dropdown.
  • Choose your taskmanager application with your environment.
  • Screen shot for code pipeline deploy stage
  • Click Next and review all of your settings. If these are correct, click Create pipeline.

Again, this process will probably take a while to deploy your code. In the meantime, this is a good chance to check out the .ebextensions directory that came with the project. You can read more about .ebextensions here, but at a high level, the files inside of this directory automate commands that we want to run on our EC2 instances. In our case, the first two commands (00 and 01) fix a weird problem with the name of the node and npm commands on Elastic Beanstalk. The next command (02) migrates the database, and the last command (03) seeds the databse. In actuality, you wouldn't want to see the database in this file since it will get re-run every time you push to Github. Instead, you would remove the line here and seed the database directly from the EC2 instance. If you want more information on how to do that, check out this gist.

To see your app in action, go back to your Elastic Beanstalk dashboard. If you see a large green checkmark you're ready! You can click the URL at the top of the page:

Screen shot of url from elastic beanstalk

Trying Routes

Try out the following routes in your app in the browser:

  • GET /
  • GET /tasks
  • GET /tasks/:id (which takes an id, like 1)

In the Postman app, try out:

  • POST /tasks, which takes a title (string) and priority (integer) as body keys.
  • DELETE /tasks/:id

Mop and bucket

Cleanup

There are a lot of resources that we created during this workshop, and they need to be stopped/deleted in a very specific order. DO NOT TRY TO DELETE YOUR EC2 INSTANCE MANUALLY. Follow the instructions below and Elastic Beanstalk will delete everything it provisioned for you.

CodePipeline

In the CodePipeline landing page, select your pipeline, then click "Delete Pipeline". Confirm the deletion.

Screen shot for deleting code pipeline

EC2

In your EC2 panel, click on Security Groups. Find the security group for your RDS database. It should be called "rds-taskmanager". Delete the inbound rule that links it to the Beanstalk security group by clicking the "X" so it has no rules, then click save.

Screen shot for deleting RDS security group source

Elastic Beanstalk

Go to your Elastic Beanstalk dashboard and select "Delete application" from the "Actions" dropdown. This will take a while.

Screen shot for deleting Elastic Beanstalk application

RDS

Go to your RDS dashboard and click on "Databases" on the left-hand side. Select your taskmanager database, then choose "Delete" from the "Actions" dropdown.

Screen shot for deleting RDS database

Click on "Snapshots" on the left-hand side and delete any snapshots. It is possible that you may not have any snapshots.

Screen shot for deleting RDS snapshots

Back to EC2 for Security Groups

Now that your RDS database is gone, you can remove your RDS security group. Click on "Security Groups" from the left-hand side, select the "rds-taskmanager" group, and select "Delete Security Group" from the "Actions" dropdown.

Screen shot for deleting RDS security group

S3 Artifacts

CodePipeline and Elastic Beanstalk both create S3 buckets to store artifacts related to your application versions.

In the S3 dashboard, select the CodePipeline bucket and delete it. You'll need to click the "empty bucket configuration" link in order for AWS to allow you to do this. Then, go back to the dashboard, select the same bucket, and you should be able to delete it this time.

Screen shot for deleting RDS security group

Next, click into the Elastic Beanstalk bucket. This one has a bucket policy attached to it that does not allow you to delete it. So first, we'll need to delete that bucket policy (see below):

Screen shot for deleting RDS security group

Then, repeat the same steps that you did above with the CodePipeline bucket.