Session 3 | Node Elastic Beanstalk Workshop
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:
- GET /
- GET /tasks
- GET /tasks/:id
- POST /tasks, which takes a
title(string) andpriority(integer) as body keys. - 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:
npm installnpm install -g sequelize-clinpm install -g nodemoncreatedb taskmanager, which will create a Postgres database locallysequelize db:migrate, which creates the tasks table in the database from the migration filesequelize db:seed:all, which will create three rows of dummy datanpm run dev- 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.
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.
- 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.
- 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.
- Scroll down to Additional Configuration and open up the options.
- In Initial database name, type
taskmanager. - 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.
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:
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.
- 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.jsfile earlier plus one more variable: - You'll first need to set
NODE_ENV: use the valueproduction RDS_PORT: use5432(this is the Postgres port, which you can also find on your RDS dashboard)RDS_DB_NAME: usetaskmanagerRDS_USERNAME: usetaskmanagerRDS_PASSWORD: usetaskmanagerRDS_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.- This is what your environment properties should look like (with your own values). The order does not matter. If everything looks right, click Save.
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.
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.
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:
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".
- 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.
- Click the orange Save button.
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 ., andgit 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).
- 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.
- Click Next.
- We will skip the build stage, so select the grey button on bottom that says Skip build stage.
- 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.
- 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:
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) andpriority(integer) as body keys. - DELETE /tasks/:id
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.
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.
Elastic Beanstalk
Go to your Elastic Beanstalk dashboard and select "Delete application" from the "Actions" dropdown. This will take a while.
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.
Click on "Snapshots" on the left-hand side and delete any snapshots. It is possible that you may not have any 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.
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.
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):
Then, repeat the same steps that you did above with the CodePipeline bucket.