エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

Application Auto Scalingを安全に、段階的に適用する

【デジカルチーム ブログリレー2日目】

デジカルチームの瀬越です。エムスリーデジカルという医療SaaS(クラウド電子カルテ)を日々開発しています。

突然ですが、AWSのApplication Auto Scaling、便利ですよね。一方で以下のような課題もあり、どのように本番環境に適用するかは悩ましいところです。

  • どの指標を使ってAuto scalingしたらよいのか分からない
  • ユーザーに影響を与えず安全にAuto scalingを適用したい

今回は、サービスの特性に合わせてApplication Auto Scalingを段階的に適用した話をします!

概要

デジカルではインフラの水平分割*1と責務に応じたコンポーネントの分割を実施していますが、今回はほぼすべてのリクエストが通るRailsアプリケーション (ECS Fargateで稼働) のAuto scalingを考えます。

デジカルは医療機関で利用される業務システムであり、日中にアクセスが集中します。実際にメトリクスを見てみましょう。

とある平日のApplication ELB RequestCount

このグラフを見るだけでAuto scalingさせたくてウズウズしてきますよね。

一般的な医療機関が概ね以下のような1日の流れになっているのは、皆さんもイメージしやすいのではないかと思います。

  • 8:00~ 診療の準備を始める
  • 10:00~12:00 ピーク時間帯
  • 12:00~14:00 お昼休み
  • 18:00~ 診療が終了

Auto scaling適用のステップ

診療を支える基幹システムなので利用者に影響が出るのは避けたいのと、Auto scalingを最初から完璧に実装するのは難しいので、以下の順番で段階的に適用することを考えました。

  • まずはScheduled scaling
  • 最大容量を増やして、適切にScale outさせる
  • 最小容量を減らして、安全にScale inさせる

まずはScheduled scaling

Application Auto Scalingには以下の3つの方法を指定できます。

  • Target tracking scaling
  • Step scaling
  • Scheduled scaling

Target trackingは考えることが少なく使ってみたくなるのですが、8時ごろにリクエスト数が急増することを考えると、Scale outが間に合わず、利用者に影響が出る可能性がありそうです。

時間帯によってリクエスト数に顕著な差があることを考えると、まずScheduled scalingを適用して様子を見るのが妥当と判断し、朝の負荷急増に間に合うようにScale out、負荷が下がりきる20時ごろにScale inするようにしました。

RequestCount と RunningTaskCount

しかし、Scheduled scalingではタスク数を直接指定するため、以下の課題があります。

  • お昼休み時間帯や18時以降など、リクエスト数が減少するときにタスクが減らない
    • 本当はScale inしてほしい
  • 機能開発や利用者増加などでRailsアプリケーションの負荷が上昇したときにタスクが増えない
    • 本当はScale outしてほしい

最大容量を増やして、適切にScale outさせる

上記の課題を解決するために、まずはScale outの検証から始めました。 Scheduled scalingで設定したタスクの最小容量は変えないことで利用者へのマイナス影響を最小限にとどめ、最大容量を増やしてAuto scalingする幅をもたせます。

今回主に検討した指標は以下の2つです。

ALB ActiveConnectionCount

Application Load Balancer の CloudWatch メトリクス

同時接続数が増えるとサーバーはbusy状態になり、ある程度TargetResponseTimeとの相関も見られました。

一方で、タスク数が時間帯によって変動することを考えると、現在のタスク数と組み合わせたメトリクスを用意する必要があり、直感的ではなく運用に適していないと判断しました。

ECS CPUUtilization

Amazon ECS CloudWatch メトリクス

TargetResponseTime p99 と ECS Service CPUUtilization

グラフにある通り、CPUUtilizationが一定の水準を超えるとTargetResponseTimeが悪化していることが確認できました。 タスク数と組み合わせる必要もなく直感的に理解できるため、今回はECS ServiceのCPUUtilizationを指標とし、Step scalingでタスクを増減させる設定を追加しました。

最小容量を減らして、安全にScale inさせる

負荷上昇時に適切にScale outし、Scale inのときも性能が劣化していないことが確認できたら、次のステップです。 Scheduled scalingの最小容量を減らして、日中に負荷が下がる時間帯にScale inして余剰タスクをできるだけ減らします。

これで、利用者へのマイナス影響を最小限にしつつ、Auto scalingを適用できました!

今後の課題とまとめ

今後の課題として、水平分割を考慮したタスク数の最適化が挙げられます。

現時点では、考えることを減らすためにScheduled scalingで設定するタスク数をすべてのシャード*2でほぼ同一の設定にしているのですが、すべてのシャードで同程度の負荷というわけではないので、過不足が出てしまいます。 開発・運用時の認知負荷を下げつつ、タスクの過不足をできるだけ最小限にする仕組みも今後検討の余地がありそうです。

それに加え、負荷試験の環境が整っていれば上記のような安全策を取らなくても、安全にAuto scalingを適用できるかもしれません。

しかし、検証のためにはデータ量の再現やAPIリクエストのシナリオ*3の再現などが必要であり、簡単に環境を用意できない、というケースがあるのも事実です。

段階的な適用は多少時間がかかってしまいますが、解決したい課題を分割して着実に前進させる方法の一つとして参考になれば幸いです。

We are Hiring!

デジカルチームでは、"僕が考えた最強のAuto scaling"を実現したい!というエンジニアを募集しています!

話を聞いてみたいなと思った方は、ぜひ以下のページからカジュアル面談等お申し込み下さい。お待ちしております!

jobs.m3.com

デジカルのシステム構成図や開発スタイルなどについては、以下のチーム紹介資料をご覧ください。

speakerdeck.com

*1:BIT VALLEY 2020 にて『 Infrastructure as "型付き" Code - 急成長する事業のインフラ再構築 』を発表しました に詳しく書かれています

*2:一定数の医療機関ごとに切り分けられた、独立した環境

*3:デジカルのRailsアプリケーションはPDF出力やファイルダウンロードなど、重めの処理もあります