Auto scale Sidekiq workers on Amazon EC2
We use Sidekiq to process messages from images conversion to shipping tickets' generation.
The total size of our queues decreases and increases drastically during the day. When it happens we have to increase or decrease our workers by adding or removing new EC2 instances.
Amazon Auto Scale
I will not get into the details of Amazon Auto Scaling, as it deserves an entire post about it.
Roughly you have to create an Auto Scaling Group, Launch Configuration and Scaling Up/Down policies.
Remember that Amazon Auto Scaling will not remove your instances automatically. If you create Scaling Up policies, you will need to create Scaling Down as well to remove them.
Netflix Asgard
Instead of creating your own Auto Scale scripts with AWS-SDK, I recommend Netflix Asgard. It isn’t a killer tool, but it works.
Asgard considerations
It doesn’t work well with other Tomcat apps. To work properly on Tomcat, it must be the ROOT app. Another option is to run it as a standalone app, this is how we use it.
JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home java -Xmx1024M -XX:MaxPermSize=128m -jar asgard-standalone.jar "" localhost 8888
(it works better on Java 6)
Queue Size Metric
To trigger your Scale Up/Down policies based on the Queue Size, you have to publish a new Custom Metric on CloudWatch.
Get the Queue Size
require "sidekiq"
class SidekiqMetric
def queue_size
stats = Sidekiq::Stats.new
stats.queues.values.inject 0, :+
end
end
Publish the Queue Size
require "aws-sdk"
class QueueSizeMetric
def initialize
AWS.config access_key_id: "…", secret_access_key: "…"
end
def put
metric = AWS::CloudWatch::Metric.new "Worker", "QueueSize"
size = SidekiqMetric.new.queue_size
metric.put_data [{value: size}]
end
end
That’s it… When you create your Scale Policies, you just have to set up the metric namespace to “Worker” and name to “QueueSize”.
Update the metric
We use the whenever gem to create a cron job to update the queue size every minute.
# schedule.rb
every 1.minutes do
command "cd /home/ubuntu/queues/current && bundle exec rake update_queue_size_metric"
end
# RakeFile
task :update_queue_size_metric do
QueueSizeMetric.new.put
end
Another options is to use a worker to update the metrics.
# app/workers/queue_size_metric_worker.rb
class QueueSizeMetricWorker
include Sidekiq::Worker def perform
QueueSizeMetric.new.put
QueueSizeMetricWorker.perform_in 1.minute
end
end