Chapter 27

Deployment

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.

28.1. Deploying to Heroku

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!

28.1.1. Creating an Account

First, we need to create an account on the Heroku website.

Signing Up

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.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/01
Figure 1: Heroku Sign up Page.

Confirm Your Email

Check your mailbox and you should have received something from Heroku. Open it and click on the confirmation link.

Enter a Password

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/04
Figure 2: Strong password > Weak password

Proceed to Your Account

Proceed to your account to finally create a new application.

28.1.2. Adding a New Application

Alright, we’re getting to the exciting part! Let’s create a new application through the Heroku interface.

“Create New App”

Click on the “Create New App” button.

Give It a Cool Name

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.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/07
Figure 3: Alexandria is coming!

After clicking on “Create App”, you will be taken to a page summarizing your new application and how to deploy code to it.

28.1.3. Setting Up Alexandria

Let’s switch back to our local application. Before we can deploy it, we need to add the Heroku toolbelt software.

Install the Heroku Toolbelt

Head over to the download page and install the toolbelt for your OS (Figure 4).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/09
Figure 4: So Many Options!

Log In

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!

Add a Procfile

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'

Point Alexandria to Heroku

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

Deploy!

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

Configuring Environment Variables

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/10
Figure 5

Enter the environment variables you want to add (Figure 6). We need to set STRIPE_API_KEY and ALLOWED_CORS_ORIGINS here.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/11
Figure 6

After adding them, you should see what is shown in Figure 7.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/12
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

Preparing Some Data

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.

Create an API Key and an Admin User

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

Testing the Live Version

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.

Debugging

If you ever need to debug something with Heroku, you can access the logs for the current application with the following command.

heroku logs

28.2. Continuous Integration Setup

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.

28.2.1. Preparing

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/13
Figure 8

From now on, developers will have a new flow to work on Alexandria.

  1. Pull the development branch.
  2. Create a new branch, feature_1, for some feature.
  3. Push the feature_1 branch to GitHub.
  4. Create a pull request highlighting the changes between the feature_1 and development.
  5. Once other developers have provided feedback, merge the feature_1 branch into development. The code should be deployed to staging.
  6. Delete the feature_1 branch.
  7. Merge the development branch into master.
  8. The code should automatically be deployed to production.

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.

28.2.2. Testing and Deploying Through CircleCI

Signing Up

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/14
Figure 9
https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/15
Figure 10

Following the GitHub Project

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/16
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.

Fixing SSH Keys Errors

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.

Getting the Heroku API key

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.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/18
Figure 12

Adding Environment Variables to CircleCI

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.
https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/23
Figure 13

Adding the CircleCI Configuration in Alexandria

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

Deploying (Again)

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

Checking out CircleCI

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).

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/27/24
Figure 14

28.3. Wrap Up

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.