blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2024.09.26 技術記事

トラフィックスパイクに対するサーバー台数の調整 [DeNA インフラ SRE]

by negimaq

#infrastructure #sre #aws #terraform #pococha

はじめに

こんにちは、IT 本部 IT 基盤部第一グループの横田です。 IT 基盤部では、組織横断的に複数のプロダクトのインフラ運用を行っており、インフラの安定的な稼働やコスト削減に取り組んでいます。

今回は、独自に設計・実装した Autoscale Multiplier Scheduler という仕組みにより、トラフィックスパイクに対してサーバー台数を適切に調整できるようにした事例をご紹介します。

解決したい課題

私たちがインフラ運用を担当する Pococha では、API サーバーを Amazon EC2 上で運用しています。 この API サーバーでは、レスポンス時間を低い水準で維持しつつ、可能な限りコストを下げるという目的を達成するため、平均 CPU 使用率を一定に維持することを目指して台数を調整しています。 具体的な方法の一つとして、 EC2 Auto Scaling Simple Scaling を導入 1 し、実際の平均 CPU 使用率が事前に設定した CPU 使用率の閾値を超えた場合に自動で台数を増減させることで、緩やかにリクエスト数が推移するほとんどの時間帯で過不足のない台数を維持できています。

一方で、Pococha では 配信者ランク変動の締め時間 である毎日 13:00 / 22:00 / 24:00 前後の時間帯にアクセスが増加する傾向にあります。

Pococha のリクエスト数

Pococha のリクエスト数

リクエスト数のグラフから分かるように、22:00 と 24:00 ちょうどのタイミングで急激なトラフィックスパイクが発生するため、実際の平均 CPU 使用率が上昇してから動作する閾値ベースの Simple Scaling ではサーバーのプロビジョニングが間に合わず、結果的に平均 CPU 使用率が上昇してしまうという問題があります。 これに対処するため、トラフィックスパイクの直前に毎日同じ台数を追加する Scheduled Scaling の Scheduled Action(台数追加のスケジュール)を以前から設定していました。 しかしながら、Pococha でイベントなどを実施した日は、普段と比べてピーク時間帯のリクエスト数が大きくなることがあり、事前に設定した Scheduled Action で追加される台数が不十分となるケースがありました。 また、ピーク以外の時間帯で不定期にトラフィックスパイクが発生することもあるため、Pococha のイベント情報をもとにした台数追加スケジュールの一時的な設定が必要なケースもありました。

これらの課題に対して、Simple Scaling において台数を追加する CPU 使用率の閾値を低く設定する、Scheduled Scaling の Scheduled Action であらかじめ多めに見積もった台数を設定する、といったシンプルな解決策も考えられますが、いずれもコストが大きく増加するため、別のアプローチを検討することにしました。

アプローチの検討

まず、リクエスト数のグラフから、トラフィックスパイクの時間帯が近づくにつれて緩やかに単位時間あたりのリクエスト数が増加していき、直前のリクエスト数に対して一定の割合でリクエストが急増加する傾向にあることが分かっていました。 これは、イベントなどを実施し、他の日と比べてトラフィックスパイクの時間帯以外のリクエスト数が多い日にも共通していました。 そのため、トラフィックスパイクの直前の台数を取得し、それを一定の割合で増加させることで、過不足のない台数を設定できると考えられます。

続いて、EC2 Auto Scaling の既存機能でこのような台数調整を実現できないか調査しました。 前述した Scheduled Scaling では、定時に台数を増減するスケジュールを設定できますが、追加する台数は定数で指定するため、日によって動的に変動させることはできません。 また、Simple Scaling では スケーリング調整タイプ として PercentChangeInCapacity を指定すると、現在の台数に対して事前に指定した割合だけ台数を増減させることができますが、Scheduled Scaling でこのような調整を行うことはできません。 そのため、EC2 Auto Scaling の既存機能では実現できず、これらの機能を統合した「現在の台数に対して事前に指定した割合だけ台数を増減させるスケジュールの仕組み」を独自に作成する必要があるという結論になりました。

要件の整理

独自の仕組みを設計する上で、まず最低限の要件を整理しました。

  • 指定した時間に現在の台数を取得し、その台数に倍率を乗算した値を新たな台数として設定するスケジュールの追加と削除ができること
  • 事前に日時と倍率、スケール対象を指定して、日次か一回限りで実行できること

また、他のプロダクトを含め、定時に毎回同じ台数を増減するスケジュールや、アプリケーション開発チームが不定期で開催されるイベントに備えて台数を積み増しするためのスケジュール設定用 Slack ボットなどを既に導入していました。 しかし、方法が統一されておらず、運用上の認知負荷が高いという問題があったため、私たちがインフラ運用を担当する全てのプロダクトで利用できるようにすることも考慮しました。

  • 他のプロダクトにも展開できるように Terraform モジュール として IaC (Infrastructure as Code) 化すること
  • Terraform モジュールの Input Variables として定期的なスケジュール(インフラチームが管理)を設定でき、Slack 経由で一回限りのスケジュール(アプリケーション開発チームが管理)を設定できること

設計の詳細

整理した要件から、以下の AWS サービスを使用した仕組みを設計し、Autoscale Multiplier Scheduler と呼ぶことにしました。

  • AWS Lambda (以下、Lambda)を使用して単一の Python スクリプトを実行
  • AWS Systems Manager Parameter Store (以下、Parameter Store)にスケール対象とスケジュール情報のパラメータを保存
  • Amazon EventBridge Scheduler (以下、EventBridge Scheduler)を使用して Lambda の関数実行をトリガーし、適切な台数を計算してスケール対象に設定
  • AWS Chatbot (以下、Chatbot)を使用して、Slack から Lambda の関数実行をトリガーし、スケジュールを取得・追加・削除

上記の AWS サービスを選択した主な理由は、サーバーレスアーキテクチャを採用することにより、追加で常時稼働のサーバーを運用する必要がなく、移植性が高いことと、Lambda における関数の実行回数や Parameter Store のパラメータ取得・更新数、EventBridge Scheduler のイベント数に対して従量課金が発生するものの、想定される利用量を考えるとランニングコストが非常に安いためです。

ランニングコストの試算

この仕組みを運用している現在の Pococha を例に月額コストを試算してみます。

  • Lambda の関数の実行は、1 回あたり約 5 秒、メモリ 128 MB で約 200 回/月

$$ 200 \times 5 \times 0.0000166667 \times \frac{128}{1024} + 0.2 \fallingdotseq 0.20 \ \mathrm{[USD / month]} $$

  • Parameter Store の Standard パラメータの取得・更新数が約 20,000 回/月

$$ 20000 \times 0.0001 \times 0.05 = 0.10 \ \mathrm{[USD / month]} $$

  • EventBridge Scheduler のイベント数が約 200 回/月

$$ 200 \times 0.000001 \times 1.25 = 0.00025 \ \mathrm{[USD / month]} $$

  • Chatbot は追加料金なし

合計すると、ランニングコストは約 0.30 ドル/月 2 となります。

定期的なスケジュール

定期的なスケジュールは、リクエスト増加の時間的規則性が把握できている場合に、インフラチームが設定することを想定しています。

定期的なスケジュールを設定する際は、スケール対象とスケジュールの情報を保存する Parameter Store のパラメータと、必要な台数を計算・反映する Lambda の関数、その関数実行をトリガーするための EventBridge Scheduler のスケジュールといったリソースを Terraform モジュールとして設置します。

定期的なスケジュールのインフラ構成

定期的なスケジュールのインフラ構成

また、EventBridge Scheduler は cron ベースのスケジュール を設定できるため、スケール対象と cron 式、倍率を Terraform モジュールの Input Variables として指定できるようにしました。

  targets = {
    pococha_api = {
      type = "ec2"
      asg  = module.pococha_api.autoscaling_group_name
    }
  }

  schedules = {
    daily_pococha_api_22_00_traffic_spike = {
      target       = "pococha_api"
      multiplier   = 1.35
      expression   = "cron(50 21 * * ? *)"
      grace_period = 10
    }
    daily_pococha_api_00_00_traffic_spike = {
      target       = "pococha_api"
      multiplier   = 1.35
      expression   = "cron(50 23 * * ? *)"
      grace_period = 10
    }
  }

このとき、Parameter Store のパラメータは、以下のような形式で保存されます。

{
  "autoscale-multiplier-scheduler": {
    "target": {
      "pococha-api": {
        "type": "ec2",
        "asg": "pococha-api-asg"
      }
    },
    "schedule": {
      "daily-pococha-api-22-00-traffic-spike": {
        "datetime": "cron(50 21 * * ? *)",
        "grace-period": "10",
        "multiplier": "1.35",
        "target": "pococha-api"
      },
      "daily-pococha-api-00-00-traffic-spike": {
        "datetime": "cron(50 23 * * ? *)",
        "grace-period": "10",
        "multiplier": "1.35",
        "target": "pococha-api"
      }
    }
  }
}

一回限りのスケジュール

一回限りのスケジュールは、不定期で開催されるイベントなどによるリクエスト増加が事前に予測できる場合に、Slack 上でアプリケーション開発チームが設定することを想定しています。

一回限りのスケジュールの場合は、スケール対象の情報を保存する Parameter Store のパラメータと Lambda の関数、Chatbot といったリソースをあらかじめ Terraform で設置しておきます。 そして、Slack から Chatbot 経由で Lambda の関数実行をトリガーすることで、スケジュールの取得と追加、削除を行います。 スケジュールの追加と削除を行う際は、スケジュール情報を保存する Parameter Store のパラメータと、EventBridge Scheduler のスケジュールを同時に作成または削除します。

一回限りのスケジュールのインフラ構成

一回限りのスケジュールのインフラ構成

Slack から Chatbot 経由で Lambda 関数実行をトリガーしてスケジュールを追加する場合、Slack 上で以下のようなメッセージを送信する必要があります。

@aws lambda invoke --chatbot-replace-curly-quotes enable --region ap-northeast-1 --function-name autoscale-multiplier-scheduler --payload {"action": "set", "targets": "pococha-api", "multiplier": 1.3, "startTime": "2024/09/01 12:00", "gracePeriod": 10, "duration": 0}

これを手動で入力するのは容易でないため、フォーム上で必要な情報を入力し、メッセージを投稿できる Slack ワークフロー を設定しています。

スケジュール作成の入力フォーム

スケジュール作成の入力フォーム

台数の計算

Lambda の関数上では、天井関数を用いた以下の式で新しい台数を計算し、スケール対象に反映しています。

$$ \mathrm{新しい台数} = \lceil \mathrm{現在の台数} \times \mathrm{倍率} \rceil $$

これにより、日毎に変わるリクエスト数に応じて、トラフィックスパイクに対処するために必要な台数を計算することができます。

導入の成果

この仕組みを Pococha の API サーバーで導入し、22:00 と 24:00 のトラフィックスパイクの直前に台数を増加させるスケジュールを設定しました。

Pococha のリクエスト数(上)と API サーバーの台数(下)

Pococha のリクエスト数(上)と API サーバーの台数(下)

結果として、トラフィックスパイクの時間帯でも平均 CPU 使用率は変わらず横ばいで、平均レスポンス時間も 150 ms 以下の水準を維持できています。

Pococha API サーバーの平均 CPU 使用率

Pococha API サーバーの平均 CPU 使用率

Pococha API サーバーの平均レスポンス時間

Pococha API サーバーの平均レスポンス時間

また、副次的な課題解決として、複数存在していた台数調整スケジュールの仕組みを統一し、チーム内の認知負荷を軽減することができました。

おわりに

今回は、独自に設計・実装した Autoscale Multiplier Scheduler という仕組みにより、トラフィックスパイクに対してサーバー台数を適切に調整できるようにした事例をご紹介しました。

今後もサーバーの安定稼働やコストコントロール、運用負荷の軽減といった観点での課題解決や運用改善を進めていきたいと考えています。


  1. Simple Scaling を導入してから現在まで、ほとんどの時間帯で問題なく台数を調整できているという経緯から Simple Scaling を使用していますが、より合理的な台数調整を目指すため Step Scaling Target Tracking Scaling の導入も検討しています。 ↩︎

  2. 東京リージョン(ap-northeast-1)で、AWS の無料枠は考慮しない場合 ↩︎

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。