Session 3 | Rails Elastic Beanstalk Workshop
Rails Elastic Beanstalk Workshop
In this workshop, you'll deploy a pre-built, database-backed Rails 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 Rails 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 Rails project.
A few quick notes about this Rails app:
- This app uses
ruby 2.6.6andrails 5.2.3. - Because of Beanstalk's defaults, we are using
bundler 1.17.3. There is a way to use v2 of Bundler with Elastic Beanstalk, but custom platform hooks are beyond the scope of this exercise. To bundle using this specific version of Bundler, you will need to typebundle _1.17.3_from your command line instead ofbundle. - This app has one resource (tasks), and the functionality was built out with rails generate scaffold. The app does not have tests. Yolo.
Although it's not necessary, if you'd like to test out the app locally before you deploy to Elastic Beanstalk, you can do that with these commands:
- First, if you aren't already using ruby 2.6.6, install that version. With rbenv, first upgrade your rbenv packages with
brew upgrade rbenv ruby-build, then install 2.6.6 withrbenv install 2.6.6, then set it locally withrbenv local 2.6.6. bundle _1.17.3_rake db:create db:migraterails s- Navigate to localhost:3000 in your browser
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 rails app that you downloaded and change the database.yml file (it lives in the config folder) by making the production section look like this:
production: <<: *default database: <%= ENV['RDS_DB_NAME'] %> username: <%= ENV['RDS_USERNAME'] %> password: <%= ENV['RDS_PASSWORD'] %> host: <%= ENV['RDS_HOSTNAME'] %> port: <%= ENV['RDS_PORT'] %>
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 Ruby, and for platform branch, choose "Puma with Ruby 2.6 running on 64bit Amazon Linux"
- 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".
- Scroll to the bottom where you'll see inputs for Environment properties. In here, you'll need to add six keys, five of which are the database environment variables we referenced in our
database.ymlfile earlier plus one more variable: - You'll first need to set
SECRET_KEY_BASE: you can get this by typing$ rake secretin your terminal while you're in the Rails app. The output is what you'll paste in for the value. 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 which will bring you back to the main configuration screen.
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 -- AWS will generate a role for you.
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 blue Create app button at the bottom.
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 rules 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 Rails 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.
Take a Break
Again, this process will probably take a while to deploy your code. If you go to your Elastic Beanstalk dashboard, you'll know the deployment is done and successful when you see the large green checkmark.
Trying Your App!
Once your deployment is done and has succeeded, you can check out your live app. To see your app in action on AWS, click on the url at the top of the page:
Take a minute to try out everything in your app. Can you create new tasks? Edit them? Delete them?
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". Click on "Edit inbound rules", then delete the inbound rule that links it to the Beanstalk security group, then click "Save rules".
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. Uncheck "Create final snapshot?" since you don't need one (and they cost money!)
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.
If you get an error about a network interface, just wait a moment, as that means your Elastic Beanstalk application has not finished being deleted.
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.