We are reaching the end of the second module. In this last chapter, we are going to push Alexandria live to Heroku.
Once our application is live, we will work on setting up continuous integration to have Alexandria be deployed automatically once the tests have been run.
We want to use Heroku to deploy Alexandria mostly because they offer a free plan and a reliable service. The signup process is super easy, and we should have Alexandria running in less than 5 minutes.
Start the timer!
First, we need to create an account on the Heroku website.
Head over to the Signup page and enter your details (Figure 1). After submitting the form, you will end up on a page telling you to confirm your email.
Check your mailbox and you should have received something from Heroku. Open it and click on the confirmation link.
The link will take you to a new page where you need to enter a password. Use something secure to have the Strong password
indication (Figure 2).
Proceed to your account to finally create a new application.
Alright, we’re getting to the exciting part! Let’s create a new application through the Heroku interface.
Click on the “Create New App” button.
You now need to name the application (Figure 3). I’m calling mine alexandria-mwra (for Master Ruby Web APIs) so you’ll need to find something else - for instance, use “alexandria-“ and append something at the end. You can also leave it empty to get a name generated by Heroku.
After clicking on “Create App”, you will be taken to a page summarizing your new application and how to deploy code to it.
Let’s switch back to our local application. Before we can deploy it, we need to add the Heroku toolbelt software.
Head over to the download page and install the toolbelt for your OS (Figure 4).
Got the Heroku toolbelt installed? Good, now let’s use it to log in with Heroku. Open a terminal and enter the following command.
heroku login
Enter your email and password when prompted.
heroku-cli: Installing CLI... 21.08MB/21.08MB
Enter your Heroku credentials.
Email: secret.email@example.com
Password (typing will be hidden):
Logged in as secret.email@example.com
If everything went well, you should now be logged in - congratulations!
To avoid using Heroku’s default running configuration, we need to tell it to use puma
using a Procfile
.
Create a new file, named Procfile
, at the root of the Alexandria project.
touch Procfile
Put the following content in it:
web: bundle exec puma -C config/puma.rb
This will tell Heroku to use puma
with the configuration defined in config/puma.rb
. Below you can see the default configuration in Alexandria that was generated by Rails when we created the application.
# config/puma.rb
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i
threads threads_count, threads_count
port ENV.fetch('PORT') { 3000 }
environment ENV.fetch('RAILS_ENV') { 'development' }
plugin :tmp_restart
Update the production
environment:
# config/environments/production.rb
Rails.application.configure do
# Hidden Code
default_url_options[:host] = 'alexandria-mrwa.herokuapp.com'
end
We need to stage and commit those changes.
git add .
git commit -m 'Add Procfile and ruby version'
Before we can deploy, we need to use the Heroku toolbelt to define the Heroku application we created as a Git remote
repository.
From within the Alexandria project, run:
heroku git:remote -a YOUR_APPLICATION_NAME
You should see something similar to the output below.
set git remote heroku to https://git.heroku.com/alexandria-mrwa.git
It’s ready, time to deploy. As you may know, deploying to Heroku is super, super simple. All we have to do is push the code using Git!
git push heroku master
Output
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 477 bytes | 0 bytes/s, done.
Total 5 (delta 4), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Using set buildpack heroku/ruby
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.5.0
remote: -----> Installing dependencies using bundler 1.11.2
remote: Running: bundle install [HIDDEN]
remote: Fetching gem metadata from https://rubygems.org/.........
remote: Fetching version metadata from https://rubygems.org/...
remote: Fetching dependency metadata from https://rubygems.org/..
remote: [INSTALLING GEM - HIDDEN]
remote: ...
remote: Bundle complete! 23 Gemfile dependencies, 57 gems now installed.
remote: Gems in the groups development and test were not installed.
remote: Bundled gems are installed into ./vendor/bundle.
remote: Post-install message from money:
remote: Bundle completed (40.98s)
remote: Cleaning up the bundler cache.
remote:
remote:
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote: Default types for buildpack -> console, rake, worker
remote:
remote: -----> Compressing...
remote: Done: 26.1M
remote: -----> Launching...
remote: Released v9
remote: https://alexandria-mrwa.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy.... done.
To https://git.heroku.com/alexandria-mrwa.git
6912f4a..f16337a master -> master
We need to define our environment variables. We can either do it by using Heroku web interface or with the command line.
Access the application settings (Figure 5).
Enter the environment variables you want to add (Figure 6). We need to set STRIPE_API_KEY
and ALLOWED_CORS_ORIGINS
here.
After adding them, you should see what is shown in Figure 7.
As mentioned earlier, we can also use the command line to do this.
heroku config:set STRIPE_API_KEY=YOUR_API_KEY \
ALLOWED_CORS_ORIGINS='*'
Output
Setting STRIPE_API_KEY, ALLOWED_CORS_ORIGINS and restarting ⬢ alexandria-mrwa...
done, v9
Before we try to access https://alexandria-mrwa.herokuapp.com/
(or the URL of your application), let’s migrate the database and seed some data.
The command below will migrate the database on Heroku.
heroku run rails db:migrate
We can use the seeds we have to generate some data.
heroku run rails db:seed
Wait a few minutes, and you will get a brand new database full of books.
We need to have at least one API key to use Alexandria. Let’s also create an admin user so we can play around with the different endpoints.
We can simply use the rails console
to create those two records.
heroku run rails c
Create a new API key with ApiKey.create!
. Don’t forget to note the generated key; we are going to need it.
Running rails c on ⬢ samurails-alexandria... up, run.9443
Loading production environment (Rails 5.x.x)
irb(main):001:0> ApiKey.create!
[HIDDEN LINES]
=> #<ApiKey id: 1, key: "22e47a3a83744dd669a80658d41a8048", active: true,
created_at: "2016-05-23 08:44:02", updated_at: "2016-05-23 08:44:02">
Now create an admin user with only the required fields: email
, password
and role
.
u = User.create(email: "bo@samurails.com", password: "password", role: 'admin')
u.confirm
That’s all we need. Leave the console by typing exit
.
exit
Well, that’s all, folks. Alexandria is now working correctly in production. Let’s give it a try by running a curl
request.
curl -i \
-H "Authorization: Alexandria-Token api_key=1:YOUR_API_KEY" \
http://alexandria-mrwa.herokuapp.com/api/books/1
HTTP/1.1 200 OK
Server: Cowboy
Date: Sat, 09 Jul 2016 10:36:41 GMT
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding, Origin
Etag: W/"548c2e5a77f0028b6ead7d5b6152dff7"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: b0d2822d-7458-4079-8c35-d49aa43a9831
X-Runtime: 0.008755
Transfer-Encoding: chunked
Via: 1.1 vegur
Connection: keep-alive
{
"data": {
"id":1,
"title":"Ruby Under a Microscope",
"subtitle":"An Illustrated Guide to Ruby Internals",
"isbn_10":"1593275617",
"isbn_13":"9781593275617",
"description":"Ruby Under a Microscope is a cool book!",
"released_on":"2013-09-01",
"publisher_id":1,
"author_id":1,
"created_at":"2016-07-09T10:26:36.692Z",
"updated_at":"2016-07-09T10:26:36.692Z",
"cover":"[HIDDEN]",
"price_cents":0,
"price_currency":"USD"
}
}
Looks good! We’ve successfully deployed Alexandria.
If you ever need to debug something with Heroku, you can access the logs for the current application with the following command.
heroku logs
We can now easily deploy Alexandria. But the deployment itself could really use some optimization. Firstly, we spent a lot of time writing tests so ensuring that they are run and that no buggy code gets to production is of the utmost importance. Secondly, we have to push the code to GitHub AND Heroku which means we can get desynchronized.
Let’s fix those two issues with continuous integration.
The master
branch will now be used only to represent what’s in production currently. To avoid having people merging new features in this branch directly, we are going to create an intermediate branch for development named development
.
git checkout -b development
Push that new branch to GitHub.
git push origin development
Now we need to ensure that nobody will use the master
branch directly. To do that, we need to change the default branch on the GitHub project. Access the settings of your project on GitHub, click on “branches” and change the default branch to development
(Figure 8).
From now on, developers will have a new flow to work on Alexandria.
development
branch.
feature_1
, for some feature.
feature_1
branch to GitHub.
feature_1
and development
.
feature_1
branch into development
. The code should be deployed to staging.
feature_1
branch.
development
branch into master
.
While we are not going to setup a staging environment, we still want the code to be automatically deployed to the production we’ve setup, running on Heroku.
To deploy the code, and run the tests, we are going to use CircleCI. Head over to their homepage and click on “Sign Up” (Figure 9). On the following page, log in with GitHub (Figure 10).
Once logged in, you should have access to all your GitHub projects. Click on “Add Project” in the left sidebar and you will see all your GitHub projects. Follow the Alexandria project you’ve built in this module (Figure 11).
CircleCI is going to start a build right away which is going to fail. Don’t worry about it; we’ll fix that soon enough. To make it work, we need to configure a few things.
If you have any error mentioning SSH keys with CircleCI, go to the project, then “Settings for the project” (top-right corner), “Permissions” and “Checkout SSH Keys.” There, you can click on “Authorize w/ Github” or add your SSH key manually.
Next, we need to get an API key from Heroku so CircleCI can easily communicate with it.
Head over to your Heroku account and locate the “API Key” section at the bottom of the page. Click on “Show API Key…”, enter your password, and copy the key Heroku has generated for you. See Figure 12 for reference.
To make our tests pass on CircleCI, we need to define some environment variables. In the project settings for the application on CircleCI, click on “Build Settings,” then “Environment Variables.”
Here are the ENVs you need to add:
ALLOWED_CORS_ORIGINS
: *
STRIPE_API_KEY
: Your Stripe API Key.
HEROKU_API_KEY
: The Heroku API key you got above.
Let’s go back to our local version of Alexandria. We need to create a script to handle the deployment to Heroku and a circle.yml
configuration file to tell CircleCI how to run the tests and deploy.
Create a new folder, named scripts
, as well as two files.
mkdir scripts .circleci && touch scripts/heroku_deploy.sh .circleci/config.yml
The heroku_deploy.sh
script needs to be executable, so let’s add that permission.
chmod +x scripts/heroku_deploy.sh
Here is the deploy script that will push the changes to Heroku. It will also put the application in maintenance and stop the workers if there are any migrations to be run.
# scripts/heroku_deploy.sh
#!/bin/sh -e
APP_NAME=$1
git remote add heroku https://heroku:$HEROKU_API_KEY@git.heroku.com/$APP_NAME.git
git fetch heroku
MIGRATION_CHANGES=$(git diff HEAD heroku/master --name-only -- db | wc -l)
echo "$MIGRATION_CHANGES db changes."
PREV_WORKERS=$(heroku ps --app $APP_NAME | grep "^worker." | wc -l | tr -d ' ')
# migrations require downtime so enter maintenance mode
if test $MIGRATION_CHANGES -gt 0; then
heroku maintenance:on --app $APP_NAME
# Make sure workers are not running during a migration
heroku scale worker=0 --app $APP_NAME
fi
# deploy code changes (and implicitly restart the app and any running workers)
git push heroku $CIRCLE_SHA1:refs/heads/master
# run database migrations if needed and restart background workers once finished
if test $MIGRATION_CHANGES -gt 0; then
heroku run rake db:migrate db:seed --app $APP_NAME
heroku scale worker=$PREV_WORKERS --app $APP_NAME
heroku restart --app $APP_NAME
fi
heroku maintenance:off --app $APP_NAME
Here is the .circleci/config.yml
file that will run the tests with rspec
before calling the script above to deploy the application to Heroku.
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5.0-node-browsers
environment:
PGHOST: 127.0.0.1
PGUSER: alexandria
RAILS_ENV: test
- image: circleci/postgres:9.6-alpine
environment:
POSTGRES_USER: alexandria
POSTGRES_DB: alexandria-test
POSTGRES_PASSWORD: ""
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: |
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# Wait for DB to be ready
- run:
name: Waiting for Postgres to be ready
command: |
for i in `seq 1 10`;
do
nc -z localhost 5432 && echo Success && exit 0
echo -n .
sleep 1
done
echo Failed waiting for Postgres && exit 1
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# run tests!
- run:
name: run tests
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" |
circleci tests split --split-by=timings)"
bundle exec rspec --format progress \
--format progress \
$TEST_FILES
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
deploy:
machine:
enabled: true
steps:
- checkout
- run:
name: Deploy
command: |
./scripts/heroku_deploy.sh alexandria-5-2
workflows:
version: 2
build-deploy:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only: master
Everything is finally ready! Every time we push the master
branch to GitHub, CircleCI will pick it up, run the tests and deploy to Heroku.
Add all the changes we’ve made…
git add .
and commit them.
git commit -m "Set up Continuous Integration"
Switch to the master
branch.
This should normally happen through a pull request after having received some feedback.
git checkout master
Merge the development
branch.
git merge development
Push the master
branch to GitHub.
git push origin master
If you check the last build in CircleCI, you should see it complete successfully. If you have any problem at that point, check out the details of the failing step and try to debug it (Figure 14).
In this chapter, we deployed Alexandria to Heroku and configured CircleCI to handle the deployment for us from now on. All the tests will also be run through CircleCI before a deploy, prevent bad code from getting to production.
Now that our application is live, we should configure SSL, ASAP. Using SSL is the only way to ensure that nobody can open the HTTP requests and take a look inside. This won’t be covered here, but there are many great resources out there to get a SSL certificate. You can get a free certificate with Let’s Encrypt, for example.
This was the last chapter of the second module. In the next one, we will review what we’ve learned in this big module before talking about REST and how to improve Alexandria.