id:kizashi1122 です。お久しぶりです。
前提
弊社では Rails 製のサービスを運用しています。非同期ジョブの処理には Sidekiq を使っています。 そしてそのジョブを処理するワーカは AWS ECS で Fargate で動かしています。
ワーカは役割ごとにいくつかのECSサービスにわけています。そのサービス中には CPU の使用率に応じてスケールするようにしているサービスもあります。
このあたりが参考になります。
ECSServiceAverageCPUUtilization
というメトリクスをつかって、例えば CPU 使用率が 80% に維持するようにスケールするように設定することができます。
この ECSServiceAverageCPUUtilization
というメトリクスは予め定義されているメトリクスとなります。
ここまではわかりやすい話です。 よくあるオートスケーリング設定です。
課題
しかしまた別のECSサービスでは、CPU 使用率ではなくて、Sidekiq の滞留ジョブ数でスケールしたいなと思いました。溜まってるんだからたくさんのワーカで処理しなきゃと思うのは自然な発想です。しかし当然、滞留ジョブ数というメトリクスがあるわけもないので困ります。
そうだカスタムメトリクスがあるじゃないか!
おさらい
つまり、
- なんらかの仕組みでカスタムメトリクスに Sidekiq の滞留ジョブ数を定期的につっこむ
- そのメトリクスを参照してオートスケールする
ということができればいいわけですね。
カスタムメトリクスに Sidekiq の滞留ジョブ数を定期的につっこむ
ここについては
- Sidekiq の滞留ジョブ数を取得する
- それらを CloudWatch に送りつける
の処理にわかれることになります。
Sidekiq の滞留ジョブ数を取得する
sidekiq の API を使えば滞留しているジョブの数やレイテンシーをとってくることができます。
require 'sidekiq/api' def collect_sidekiq_stats Sidekiq::Queue.all.each_with_object({}) do |q, hash| hash[q.name] = { latency: q.latency, size: q.size } end end
こんなイメージです。
それらを CloudWatch に送りつける
こんな感じでしょうか。
require "aws-sdk-cloudwatch" # # cloudwatch にメトリックデータを送信する # def put_metric_data(hash) client = Aws::CloudWatch::Client.new hash.each do |q_name, info| metric_data = { namespace: "SidekiqJobMetric", metric_data: [ { metric_name: "Retention", dimensions: [{ name: "Environment", value: "environment_here" }, { name: "QueueName", value: q_name }], timestamp: Time.zone.now.utc, value: info[:size], unit: "Count" }, ] } res = client.put_metric_data(metric_data) end end
そして、こんな感じでつなげると。
put_metric_data(collect_sidekiq_stats)
これでカスタムメトリクスを送りつけることができるので、参照してオートスケールの判断材料に使うことができます。
ここについては省略します。 このあたりの情報が参考になるかもしれません。
ちなみに SQS と SQSワーカの関係だと SQS の ApproximateNumberOfMessagesVisible
を使いたくなりますがこれは自分で投げつけたメトリクスではないのですが、事前定義メトリクスではなくカスタムメトリクスになりますね。
課題
うちのプロジェクトではこれを Rake タスクにまとめて、5分おきに起動しているのですが、Rake なのでどうにも重くていけません。 Sidekiq の API が呼び出せればいいので、Rails は読み込む必要はありません。 となると Lambda でさくっと定期実行するのがいいかなと思っています。
実現できればまたブログ記事にしたいと思います。