Fargate上のRailsアプリを高速でオートスケールさせてみます。
FargateをCPUやメモリなどの標準メトリクスでオートスケールさせると、CloudWatchにメトリクスが送られるまで1~2分程度の遅延が発生すると思います。
さらにCloudWatchアラームでの評価に1分程度かかり
さらにコンテナ起動まで(オートスケール完了まで)1~2分程度かかります。
そのため、オートスケールを設定していてもスケールまで5分程度かかります。
このままですと急なアクセス増に対応できないため、オートスケールの時間を減らしたいと思います!!
どのようにオートスケールを早くするか
以下のような感じで対応していきます。
- コンテナからカスタムメトリクスの空きスレッド数をCloudWatchへ送る
- Fargateのタスク定義・クラスター・サービスを作成
- CloudWatchアラームで高解像度の監視をする
- アラームに応じてFargateのオートスケールをする
これで、1~3分程度でスケール可能となります。(起動の時間がアプリケーションによって異なると思いますが、大体この範囲に収まると思います!)
やってみる!
RailsコンテナからカスタムメトリクスをCloudWatchへ送る
Gemfileの修正
以下を追記します。
gem 'aws-sdk-cloudwatch'cloudwatch_metrics.rbの作成
「config/initializers/cloudwatch_metrics.rb」を以下の内容で作成します。
# config/initializers/cloudwatch_puma_metrics.rb
require 'aws-sdk-cloudwatch'
require 'net/http'
require 'json'
CLOUDWATCH = Aws::CloudWatch::Client.new(region: 'ap-northeast-1')
Thread.new do
loop do
begin
sleep 10
Rails.logger.info("Sending Puma metrics to CloudWatch")
uri = URI('http://127.0.0.1:9293/stats')
res = Net::HTTP.start(uri.host, uri.port, open_timeout: 1, read_timeout: 1) do |http|
http.get(uri.request_uri)
end
stats = JSON.parse(res.body)
Rails.logger.info(stats.inspect)
capacity = stats['pool_capacity']
CLOUDWATCH.put_metric_data(
namespace: 'Custom/Puma',
metric_data: [
{
metric_name: 'ThreadPoolCapacity',
value: capacity,
unit: 'Count',
storage_resolution: 1,
dimensions: [
{ name: 'Cluster', value: ENV['ECS_CLUSTER'] || 'default' },
{ name: 'Service', value: ENV['ECS_SERVICE_NAME'] || 'rails' }
]
}
]
)
rescue => e
Rails.logger.error("CloudWatch Puma metric error: #{e.class}: #{e.message}")
end
end
endlib/request_counter.rbの作成
lib/request_counter.rbを以下の内容で作成します。
class RequestCounter
def initialize(app)
@app = app
end
def call(env)
REQUEST_COUNTER.increment
@app.call(env)
end
endconfig/puma.rbの編集
config/puma.rbに以下を追記します。
activate_control_app 'tcp://127.0.0.1:9293', { no_token: true }RailsのコンテナイメージをECRにPush
以下のコマンドで、AWSの接続設定をしておきます。
aws configure次にECRログインします。
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {AWSアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com以下のコマンドでリポジトリを作成します。
aws ecr create-repository \
--repository-name rails \
--image-scanning-configuration scanOnPush=true \
--region ap-northeast-1Railsアプリのタグ作成します
docker tag rails-demo:latest {AWSアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/rails:latestRailsアプリのpushです
docker push {AWSアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/rails:latestFargateのタスク定義・クラスター・サービスの作成
タスク定義
以下の設定でタスク定義を作成します。
- イメージ URIにECRのURI
- コンテナポートを3000
クラスター
以下の設定でクラスターを作成します。
- コンピューティングキャパシティの取得方法を選択でfargateのみを選択
サービス
以下の設定でサービスを作成します。
- タスク定義ファミリーで作成したタスク定義を指定
- ロードバランシング – オプション
- ロードバランシングを使用にチェック
- 新しいロードバランサーの作成にチェック
- 新しいリスナーを作成にチェック
- 新しいターゲットグループの作成にチェック
- ポートは3000に設定
CloudWatchアラームで高解像度の監視をする
アラームの作成を以下の設定で作成します
- メトリクス:ThreadPoolCapacity
- 統計:最小
- 期間:10秒
- 条件
- ThreadPoolCapacityが3より小さい
アラームに応じてFargateのオートスケールをする
Fargateのサービスの画面からサービスの自動スケーリングの設定をします。以下のような流れで設定します。
- スケーリングポリシーを作成
- ステップスケーリングを指定
- カスタムオプションを指定
- スケールアウトを指定
CloudWatch アラームで作成したアラームを指定
- 「調整」でスケールするタスク数などを指定
動作確認
JmeterでロードバランサのエンドポイントにHTTPリクエストを送りアラーム状態にすると、90秒程度でコンテナ起動する(ロードバランサのターゲットに登録されるところまで90秒程度)ことが確認できました。
通常5分程度かかるスケーリングが90秒程度でできたので効果ありと思っています。
なお、本検証は以下のSOCI対策を入れた状態で行いました!


