Quantcast
Channel: spacelyのブログ
Viewing all articles
Browse latest Browse all 32

ECS でオートスケーリングを設定しました!

$
0
0

はじめに

はじめまして、スペースリーでインフラエンジニアをしている久下です。
アーロンチェアを買おうかずっと迷っていたのですが先日、中古品を購入しました。
想像より状態も良くて、リモートワーク環境がより整って良い感じになりました。
さて、この記事では ECS のオートスケーリングについて書いていきたいと思います。

なぜやったか

2024 年 6 月にスペースリーの Railsアプリケーションを EC2 から ECS へ移行しました。
EC2 時代は負荷が高くなった際はスケールアウトを手動で行っていました。
ECS では自動でスケールイン、スケールアウトを行いたいということになりました。

やったこと

負荷によるオートスケーリング

ECS へ移行した初期段階から Railsアプリケーションのコンテナと、ジョブを実行する Sidekiq のコンテナをそれぞれオートスケールするように設定しました。
Railsコンテナは CPU 使用率によってオートスケールするようにしました。
また、Sidekiq コンテナは Redis のキューサイズによってオートスケールするように設定しました。
CPU 使用率は ECS のメトリクスがあるのでそちらを使うことができました。
しかし、Redis のキューサイズは Sidekiq の管理画面から確認することはできましたが、CloudWatch のメトリクスとしては取得していませんでした。
そこで EventBridge で Lambda を定期実行し Redis のキューサイズを CloudWatch のカスタムメトリクスに登録することにしました。
環境変数や Sidekiq の設定ファイルは別途必要となりますが、下記のように実装しました。

require'logger'require'time'require'json'require'yaml'require'sidekiq/api'require'aws-sdk-cloudwatch'moduleLambdaFunctionsclassHandlerclass<< selfdefprocess(event:, context:)
        logger = Logger.new($stdout)
        logger.formatter = procdo |severity, datetime, _, msg|
          request_id = context.aws_request_id
          # ISO 8601 形式の日時に変換する# 2024-05-07 05:43:09 +0000 -> 2024-05-07T05:43:09.399Z
          formatted_datetime = datetime.utc.iso8601(3)
          {
            timestamp: formatted_datetime,
            level: severity,
            requestId: request_id,
            message: msg
          }.to_json
        end

        configure_sidekiq

        total_queue_size_result = total_queue_size
        logger.info({ total_queue_size: total_queue_size_result })

        put_metric_data(total_queue_size_result)
      endprivatedefconfigure_sidekiq
        redis_url = "redis://#{ENV['ELASTICACHE_PRIMARY_ENDPOINT']}:6379"Sidekiq.configure_client do |config|
          config.redis = { url: redis_url, namespace: 'sidekiq' }
        endenddeftotal_queue_size
        config = YAML.load_file('config/sidekiq.yml')
        config[:queues].map do |queue|
          Sidekiq::Queue.new(queue).size.to_i
        end.sum
      enddefput_metric_data(queue_size)
        cloudwatch = Aws::CloudWatch::Client.new(region: ENV['REGION'])

        cloudwatch.put_metric_data(
          {
            namespace: 'App/Redis',
            metric_data: [
              {
                metric_name: 'TotalQueueSize',
                dimensions: [
                  {
                    name: 'SidekiqProcessName',
                    value: 'Web'
                  }
                ],
                value: queue_size
              }
            ]
          }
        )
      endendendend

スケジュールによるオートスケーリング

第 2 段階として、夜間はアクセスが少ないためタスクの最小数を減らすことにしました。
スケジュールに基づくスケーリングは、本記事の執筆時点ではマネージメントコンソールからは設定できません。
弊社では AWSのリソースは Terraform で管理しているため、特に問題はありませんでした。
Terraform では下記のようなイメージで設定が可能です。

resource"aws_appautoscaling_scheduled_action""scale_in"{name               = "scale-in"service_namespace  = "<service_namespace>"resource_id        = "<resource_id>"scalable_dimension = "<scalable_dimension>"# 毎日23:00 JSTにスケールインするschedule = "cron(0 23 * * ? *)"timezone = "Asia/Tokyo"scalable_target_action{min_capacity = 2max_capacity = 10}
}

resource"aws_appautoscaling_scheduled_action""scale_out"{name               = "scale-out"service_namespace  = "<service_namespace>"resource_id        = "<resource_id>"scalable_dimension = "<scalable_dimension>"# 毎日09:00 JSTにスケールアウトするschedule = "cron(0 9 * * ? *)"timezone = "Asia/Tokyo"scalable_target_action{min_capacity = 3max_capacity = 10}
}

検討したこと

Lambda をどの言語で実装するか

Python

  • 既存の Lambda 関数で使われていることが多いです。

Ruby

  • Web アプリケーションで使っている言語です。

今回は同じライブラリが使えることを重視し Rubyを採用しました

キャッチアップしたこと

今年スペースリーに入社した状況で、本対応を進めるにあたっていくつかキャッチアップが必要でした。

lambroll

スペースリーでは Lambda のデプロイに lambrollを使っています。 lambroll を使うのが初めてだったため、解説本(https://zenn.dev/fujiwara/books/ecspresso-handbook-v2)を購入してキャッチアップしました。

GitHub Actions

これまでの現場では CI/CD ツールは Jenkins や GitLab CI/CD を使うことが多かったです。 既存の Lambda 関数のワークフローの yamlファイルの内容を理解することでキャッチアップしました。

Ruby

過去に何回か使った程度だったため、Rubyの入門書をざっと読みました。

つまづいたこと

Lambda 関数をパブリックサブネットに関連付けてもインターネットに直接接続できない

Lambda をテスト実行したところタイムアウトが発生しました。原因の調査に時間がかかりましたが、最終的には VPCなしの Lambda 関数で成功したため、本事象に気づきました。

Redis で namespace を指定しておらず、メッセージが溜まっている状況でもキューのサイズが 0 となった

redis-cliで調査をしたところ namespace を指定していなかったことが原因ということが分かりました。

どうなったか

下記は Datadog で作成している Railsの ECS サービスの CPU 使用率とタスク数のグラフです。
CPU 使用率が上昇すると ECS のタスク数が増加し、夜間の負荷が低い時間帯はタスク数が減少していることが確認できます。

Datadogのダッシュボード

また、下記は ECS のコストのグラフですが、夜間にタスクの最小数を減らすことによって 1 日あたり約 $17 のコストが削減できました。

Cost Explorer(サービス: Elastic Container Service)

最後に

EC2 から ECS への移行によって、リソースの柔軟な管理が可能になり効率的なシステム運用ができています。
ECS のオートスケーリングは設定することが多いと思いますので、何かの参考になれば幸いです。
最後に、スペースリーでは一緒に働いてくださる方を大募集中です。
少しでもご興味がある方は上記よりご連絡ください!


Viewing all articles
Browse latest Browse all 32

Latest Images

Trending Articles