ALB ターゲットグループのバランシングアルゴリズムを LOR にする

この記事ははてなエンジニア Advent Calendar 2022の27日目のエントリです。

問題

社内で運用している Fargate サービス (Perlplack アプリケーションが動いている) において、以下のような問題が発生していました。

  • リクエストごとの処理の重さ (計算リソースの使用量や所要時間) に違いがあり、特定の task に重いリクエストの割り振りが偏ることがある
  • Fargate task は表向きのコア数は同じでも、割り当たるハードウェアの世代によって処理能力が異なるケースがある (いわゆるインスタンスガチャ)
  • このような偏りのある状態でアクセス増など全体の負荷が増加し、一部 task のワーカプロセスが全て busy な状態が一定時間続くと ALB のヘルスチェックに落ちて殺される
    • 各リクエストを1つのプロセスが処理する、いわゆる prefork アーキテクチャのサーバのため、比較的簡単にワーカプロセスを使い尽くしてしまう
    • また、 task の並列実行数が上がることによりコンテキストスイッチのオーバヘッドやリソースの競合が増え、処理能力が低下してリクエストが溜まりやすくなる
  • 殺された task の代わりが立ち上がるまでの間、一時的に全体の処理能力が低下するため他の task で同様のことが起きやすくなり、次々と task が殺されていく悪循環に陥る

解決策: ALB のバランシングアルゴリズムの変更

オートスケール設定を見直したり台数を増やしたりしても一定の効果は得られますが、 負荷や処理能力のアンバランスを緩和した方が効率が良くなると考え、バランシングアルゴリズムを変更することにしました。

ALB の (ターゲットグループの) デフォルトのバランシングアルゴリズムは Round Robin で、単純にすべてのターゲットに均等にリクエストが割り振られます。今回の ECS Service も Round Robin になっていました。 2016年に ALB が登場した当初はこれが ALB で使える唯一のバランシングアルゴリズムでしたが、2019年11月に LOR (Least Outstanding Requests) が選べるようになりました。 LOR では処理中のリクエスト数が最も少ないターゲットに優先的にリクエストを割り振る動作になります。
(そもそも、旧世代の ELB である CLB (Classic Load Balancer) も HTTP(S) プロトコルの(唯一の)バランシングアルゴリズムは LOR です)

リクエストごとの粒度 (レスポンスタイムや消費する計算リソース) のばらつきが大きいアプリケーションや、ターゲットごとの処理能力に違いがある場合には LOR を選んだ方が安定すると考えられます。先に書いたように Fargate では task ごとにハードウェア性能が異なる事があるので、むしろ通常は LOR を選ぶくらいでよいのではと個人的には思っています。

変えてみた結果

ターゲットグループの設定から無停止でサクッと変更できます。 https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#modify-routing-algorithm

その結果、各種メトリクスが目に見えて変化しました。

リクエスト数

Round Robin で各 task に同じ数のリクエストが割り振られていたのがバラバラになりました。 表面上は同一スペックの Fargate task ですが、処理能力の違いにより傾向の似た3つほどのグループに分かれたことが見て取れます。

busy workers

一部の task にリクエストが滞留することがなくなり、全体的に均されています。 変更前は、特定の task に重いリクエストが割り当たるなどしてスループットが落ちたところに容赦なく新しいリクエストが割り振られて値が跳ね上がるなどしていましたが、 LOR にしたことで各 task の負荷状況に応じて自動でリクエスト数が調整されるようになりました。

CPU 使用率

CPU 使用率の極端なバラツキが緩和されました。 バランスが良くなったことでより大きな負荷に耐えることが期待できます。

まとめ

  • ALB のターゲットグループではバランシングアルゴリズムがデフォルトの Round Robin の他に LOR (Least Outstanding Requests) も選べる
  • リクエスト毎の粒度のばらつきの大きなアプリケーションやターゲットごとの処理能力に違いがある場合には LOR を選んだ方が安定すると考えられる
  • Fargate は同じサイズの task でも個々の処理能力が異なる事があるので通常は LOR でよさそう