[002.4] Deployment

Deploying a Rails app to Heroku that uses Sidekiq for background processing.

Subscribe now

Deployment [05.13.2016]

Up til now, we've been working with Sidekiq using a plain Ruby project. In today's episode, we're going to look at deployment. To make it a bit more realistic, I've provided a basic Rails application that we're going to be deploying in the resources section. Let's get started.

Project

In the resources, you can find an archive containing a basic Rails application with a single controller that can be used to send an email. We're going to deploy this application to Heroku, and set it up to use Sidekiq to process background jobs.

We'll start off by untar'ing the application.

tar zxvf sidekiq_rails_demo.tar.gz

Let's look at what the controller looks like:

class VisitorsController < ApplicationController
  def index
  end

  def contact
    VisitorMailer.contact_email(params[:name], params[:email], params[:message]).deliver_later

    redirect_to :root
  end
end

So here we're using Rails ActiveJob. We've configured ActiveJob to use sidekiq, here:

vim config/application.rb
#...
module SidekiqRailsDemo
  class Application < Rails::Application
    #...
    config.active_job.queue_adapter = :sidekiq
  end
end

So now Rails will use sidekiq to manage its job queues.

Next, let's deploy it to heroku:

git init .
git add .
git commit -m "initial commit"
heroku create
git push heroku master
# Ensure you're running at least one web worker
heroku ps:scale web=1

Now we can open it up in our browser, and we can see our form. If we fill this out, we expect it to send us an email. However, we will get an error. This is due to there being no redis configured for storing jobs - when we call deliver_later it tries to store a new job in Redis, but there was no Redis server to talk to.

We can add the RedisToGo addon in Heroku to provide a Redis server for us to use.

heroku addons:create redistogo:nano
# Now we'll set the appropriate ENV variable on heroku.  RedisToGo provides us
# with an environment variable - we just need to set it to the one that Sidekiq
# expects for the redis provider.
heroku config:set REDIS_PROVIDER=REDISTOGO_URL
# Finally, our code expects an environment variable to define where the emails
# go.  I'll set that:
heroku config:set EMAIL_RECIPIENT="josh+sidekiq@dailydrip.com"

This should be sufficient for us to enqueue jobs. Let's redeploy and try it out.

heroku restart

Let's try to send an email. It should be able to enqueue a job. But we'll never receive the email as it stands. There are a few reasons for this, but the first one that we want to deal with is the fact that we have no worker processes running. We want to run a sidekiq server on a heroku dyno. We'll add a Procfile for this:

vim Procfile

Since we're adding a Procfile, it will take the place of the implicit Procfile. We'll describe how to run web workers:

web: bundle exec rails server -p $PORT
worker: bundle exec sidekiq -e production

We also described how to run our sidekiq worker. Let's commit the Procfile and push it up:

git add Procfile
git commit -m "Added Procfile, to describe how to run worker processes."
git push heroku master

Now we'll want to spin up a worker process:

heroku ps:scale worker=1

That should have spun up a worker, and he should get to work on our emails, right? Let's check the logs.

heroku logs --tail

Now let's try again. No errors, so that's good! However, we still didn't see anything in the logs. I happen to know that we would see some content written to the logs if it had worked. So why wouldn't an email be sent? We're running a sidekiq server, we've queued up the job, but still nothing's happening. Let's figure out why.

We'll run a Rails console to check out the Sidekiq Queues:

heroku run console
require 'sidekiq/api'
Sidekiq::Queue.all

Oh! We have a job, but it's not in the default queue. It's in one Rails uses, called mailers. Starting in Rails 5, mailers will use the default queue. Let's just go ahead and make ActionMailer start queueing them into the default queue in our app as well. This will make us future-proof.

Make a new initializer, at config/initializers/action_mailer.rb:

class ActionMailer::DeliveryJob
  queue_as :default
end

OK, let's commit that:

git add .
git commit -m"Configure ActionMailer to queue mails in the default queue."
git push heroku master

OK, let's watch the logs and try it one more time.

heroku logs --tail

OK, so we sent an email, but the logs indicate a failure when they try to send the email. We've configured the SMTP server in rails, but we haven't yet set it up as a heroku addon. First, let's look at how ActionMailer is configured:

vim config/environments/production.rb
  config.action_mailer.smtp_settings = {
    :address        => 'smtp.sendgrid.net',
    :port           => '587',
    :authentication => :plain,
    :user_name      => ENV['SENDGRID_USERNAME'],
    :password       => ENV['SENDGRID_PASSWORD'],
    :domain         => 'heroku.com',
    :enable_starttls_auto => true
  }
  config.action_mailer.delivery_method = :smtp

We've got this app configured to use SendGrid. Let's add that heroku addon:

heroku addons:create sendgrid:starter

Alright, we'll restart the app:

heroku restart

This time, we saw the mailer output into our logs. This means it was successfully sent over to SendGrid to deliver.

Summary

In today's episode, we saw how to configure Sidekiq on a newly-deployed Rails application, configured it to play nicely within the parameters of our Redis instance, and ensured that it executed Rails mailer jobs. I hope you enjoyed it. See you soon!

Resources

Attachments