コネヒト開発者ブログ

コネヒト開発者ブログ

A/Bテスト標準化へ取り組んだ話

みなさんこんにちは!機械学習チームのたかぱいです。

半年ほど前からA/Bテストの標準化に取り組んでいたので、本日はその背景やプロセスについてご紹介しようと思います。

尚、以下メルカリさんの事例を参考にさせていただいています(この場を借りて御礼申し上げます。ありがとうございます!)

note.com

標準化に取り組んだ背景

コネヒトでは日常的にさまざまなチームでA/Bテストが行われていました。

しかし、以下のような課題があると感じていました。

  • A/Bテストに関する知識にバラつきがある
  • チームごとにA/Bテストのドキュメントの書体が異なるので、読み解くのにコストがかかる
  • 「どのような実験」が「どのくらいの期間行われていたか(いるか)」という情報が一目で把握できない

etc...

上記のような課題は時間が経てば経つほど負債が大きくなると判断し、一度テコ入れした方が良いと思い、標準化に取り組みました。(後述する書籍の日本語版が発売になったことも大きなトリガーでした)

ここで言う「標準化」 is 何?

標準化によって得たい効果は、以下のようなものを想定していました。

  • 実験計画書のテンプレートをつくり、レビュー文化も導入することで、人依存による実験設計・検証品質の分散を減らす
  • 書体が揃うことで過去のドキュメントを読み解くコストが下がる
    • いつ、どんなものが誰によって検証されたかを振り返りやすく・見やすくする
  • 実験文化そのものの醸成を促す
  • 他のチームでどんな実験をやっていて、どんなメトリクスが使われているのかを共有知とする
  • 過去に何がうまくいって、何がうまくいかなかったのかを振り返ることができる
    • 未来へKnowledgeリレーを繋いでいきたい

そのため、まずは誰でも使える標準化テンプレートを作成し、それに沿って実験を行うフローにアップデートしようと考えました。

以降で、標準化プロセスでポイントだと感じた以下2点について紹介できればと思います。

  1. さまざまな職種の方と輪読会を行い、A/Bテストに対する視座を揃える
  2. 先んじて活用事例を作成し、今後社内で行われる実験で利用するイメージを沸かせる

最終的に作成したテンプレートは冒頭で述べたメルカリさんのテンプレートに若干アレンジを加えたものになりました。(最後にサンプルを載せています)

1. さまざまな職種の方と輪読会を行い、A/Bテストに対する視座を揃える

まず最初に行ったことは、いろんな職種の方を交えて輪読会をしました。

読んだ本は界隈で有名な A/Bテスト実践ガイド 真のデータドリブンへ至る信用できる実験とは という書籍です。

社内slackで参加者の募集を行い、ML、インフラ、サーバーサイド、ネイティブエンジニアやPdMといったいろいろな専門性を持った方々に参加していただきました。

個人的にはPdMを含めた様々な職種の方に参加してもらえたのはとても良かったなと思っています。

例えば、本を読んでいく中で「インフラでこの辺の数値って計測できるんですか?」といった議論や、「ネイティブでこの辺のログって取得できるんですか?」といった議論をその場で行うことができました。
また、PdMにも参加してもらったので「今のコネヒトだとこの辺の検証はどうやっているのか」や「この辺注意した方が良いよね」といった共通認知を取ることもできました。

本を読み終えたあとは、輪読会で議論した内容をもとにテンプレートに落とし込み、関係者にレビューしてもらいブラッシュアップしていきました。

レビュー時の様子

2. 先んじて活用事例を作成し、今後社内で行われる実験で利用するイメージを沸かせる

無事にテンプレートが作成できても、実際に使うまでにはハードルがあると思います。

そのため、機械学習のプロジェクトで過去に行ったABテストをこのテンプレートに落とし込み、いくつかサンプルを作成しました。

これにより少しでも活用イメージを沸かせてもらい、「自分も使ってみようかな」というアクションを促しました。

作成したテンプレート

冒頭で紹介したメルカリさんの事例にあるように、「Background」「Test setting」「Metrics Details」「Action plan」という項目は、細部は異なりますが概ね同じ項目を採用しました。

大きな差分としては「Result」という項目を追加しています。

この項目は、実験が終了した後に記載する項目で、実験が複数のメトリクスにどの程度の影響を与えたかを記録しておく項目です。
数値が改善した理由(成功した理由)や、数値に変化がなかった or 改悪した理由(失敗した理由)などの考察も記入します。
ここの記載内容をもとに、A/Bテストの成功 or 失敗を判断し、事前に決めておいたAction plan**に則ってNext actionに移行します。

Resultは以下のような項目でテンプレート化しました。

# Result

## OEC metricsの結果
- hoge

## Guardrail metricsの結果
- hoge

## Debugging metricsの結果
- hoge

## 考察
- hoge

## Next action
- hoge

この項目を追加した意図としては、実験計画書とその実験の結果や得られた知見をセットで記録しておくことで、後から振り返りやすくしたい、という想いがあります。

標準化に取り組んでどうだったか?

運用を始めて半年ほど経過しましたが、このテンプレートを使って数十件の実験計画書が作成されています。

実験計画書を作成する際も項目に沿って埋めるだけで良くなったので、準備コストは削減できているなぁと感じています。

また「こんな検証してるんだな〜」ということも分かりやすくなり、各チームの簡易的な活動の可視化にもなっていると感じています。

最後に

前提として、実験の成功率は高くないと思っています。

BingやGoogleでも、アイデアの成功率は約10%〜20%程度、Slackでも30%程度しかポジティブな結果を得られないことが公開されています。

つまり、70%以上は失敗するわけですが「失敗=負け」だとは思っていません。失敗から何も学べない状況こそが本当の敗北だと思っています。

そうならないためにも、失敗前提で標準的なプロセスに則ってサイクルを簡単に回していける仕組みを作っていくはとても大切だと思います。

実験計画書を溜めておくことによるメリットを享受できるのはもう少し先になると思いますが、確実に未来へ資産を残していけていると思うので、形骸化しないように今後もブラッシュアップしていこうと思います!

We Are Hiring !!

コネヒトでは一緒に働く仲間を募集しています!

もしご興味があれば、カジュアルにお話しさせてください!

www.wantedly.com

コネヒトにおける機械学習関連業務の紹介資料を公開します

更新履歴💡
2022-07-25:初版作成

コネヒトでは Tech vision の1つに 1 to 1 AI というStrategyを掲げているように、今後パーソナライズやレコメンデーションなどの分野に積極的にチャレンジしていくため、機械学習エンジニアの採用を強化しています。

それに伴い、コネヒトにおける

  • 機械学習の活用事例
  • 今後どんなチャレンジをしていく予定なのか
  • 機械学習エンジニアの働き方

などについて、より多くの方に知っていただきたいと思い本資料を公開しました。
(※資料中のリンクについては、speakerdeckページの説明欄や本記事の後半にも記載しております)

本資料を読んでより詳しく話を聞きたいと思った方は、ぜひカジュアルにお話しをさせてください!(各メンバーの Twitter DM や Meety経由でお気軽にご連絡ください!)

採用選考にご興味を持っていただいた場合は、下記よりご応募ください。

bit.ly

上記資料にも添付しておりますが、メンバーが公開している登壇資料やブログ記事もぜひご覧ください。

機械学習の活用事例

MLプロジェクトの進め方

tech.connehito.com

tech.connehito.com

サービス内での活用事例

tech.connehito.com

tech.connehito.com

tech.connehito.com

社内での活用事例

tech.connehito.com

機械学習基盤

分析環境

tech.connehito.com

基盤のスコアリング

tech.connehito.com

SageMakerやStep Functionsを用いたMLOps

tech.connehito.com

tech.connehito.com

tech.connehito.com

tech.connehito.com

産学連携

connehito.com

働いている人・チームのインタビュー記事

www.wantedly.com

eh-career.com

www.wantedly.com

www.wantedly.com

テクノロジーで「家族像が実現できる社会をつくる」という価値提供をしていきたい、皆さんのご応募をお待ちしております!!

Firebase AnalyticsからBigQueryへの日次データ同期が突如不規則になった事象に対応した話

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、Firebase AnalyticsからBigQueryへの日次データ同期処理の時間が大幅にずれた際に取った対策について書こうと思います。

内容はざっくり下記3点です。

  • 背景説明
  • Firebase AnalyticsからBigQueryへのデータ同期
  • 解決策

背景説明

コネヒトでは、家族ノートという「ママリ」内の検索データとQ&Aデータを可視化したデータ分析サービスを運営しています。

info-kazokunote.mamari.jp

家族ノート内で使っている検索データは、FirebaseAnalyticsを使ってトラッキングしている行動ログをベースにしています。

サービス性質上FirebaseAnalyticsの行動ログは大切で、元のデータがうまく出来ていないとデータの欠損等にも繋がってしまいます。

※もちろんそう簡単に欠損を出すわけにはいかないので、後続の集計処理のスケジュールを工夫したりしてデータ欠損が出ない取り組みはしていますが。

Firebase AnalyticsからBigQueryへのデータ同期

データ同期のフロー

今回の事象の説明に入る前に、FirebaseAnalyticsからBigQueryへのデータ同期のフローをざっくり書くと下記のようになっています。

デバイス
↓ ①行動ログを送信
FirebaseAnalytics
↓ ②リアルタイムにデータ同期
BigQuery(events_intraday_yyyymmdd)
↓ ③日次テーブルとして書き出し
BigQuery(events_yyyymmdd)

ちなみにFirebaseAnalytics上の設定は下記のキャプチャのように「毎日」「ストリーミング」同期の両方をオンにしています

今回課題になった点

※再掲

デバイス
↓ ①行動ログを送信
FirebaseAnalytics
↓ ②リアルタイムにデータ同期
BigQuery(events_intraday_yyyymmdd)
↓ ③日次テーブルとして書き出し
BigQuery(events_yyyymmdd)

家族ノートでは、③の日次テーブルを起点に集計データを作って最終的に表示するBigQueryのビューを作成しています。

ただ、③の日次テーブルの書き出し時間は不定期で、BigQueryの events_yyyymmdd テーブルが何時に出来るのかは利用者側ではハンドリング出来ません。

これまでの経験則から、ある程度この時間までにはデータが出来ているというバッファをもったタイミングで後続の集計処理をスケジュールベースで動かしていました。

そんな中、今年の6月1日のタイミングから時間のズレが大きくなり、元のスケジュールではカバーできない状況が発生しました。(これまでの時間帯に日次テーブルが出来る日もあれば大幅にタイミングが遅れる日も散見され始めました)

具体的に言うと、集計テーブルに対するデータのテストでアラートが出る日が増えてきており何かしらの対応が必要になりました。

解決策

解決策は簡単で、集計テーブルの対象を③ events_yyyymmdd のみから、③events_yyyymmdd or ② events_intraday_yyyymmddという形に変えました。

events_intraday テーブルの仕様がよくわからず、結果としてデータ重複等予期せぬことが起きるのは怖かったので、1ヶ月ほど併行して出来上がるデータに差分がないかをチェックしました。

結果、うまく動いた日は同じ数のイベントがあり、旧来の処理がコケた日も新処理ではデータがうまく出来ていることが確認出来たので正式に新方式に切り替えることにしました。

これまでのクエリ:

FROM
    `hogehoge.analytics_99999999.events_*`
WHERE (_TABLE_SUFFIX = FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))

新方式のクエリ:

FROM
    `hogehoge.analytics_99999999.events_*`
WHERE (_TABLE_SUFFIX = FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))
      OR _TABLE_SUFFIX = CONCAT('intraday_', FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))))

どうしようか困っていた時に、下記ツイートのアイデアに非常に助けられました。

この場でお礼を伝えさせていただきます!ありがとうございます。

今回はニッチなネタでしたが、このブログを読んでたまたま同じ悩みに遭遇している誰かの救いになれば幸いです。

CakePHP Fixture Factories を導入しました

こんにちは。プロダクト開発部の @su-kun1899 です。

今回はママリの CakePHP アプリケーションに Fixture Factories を導入した事例を紹介します。

Fixture Factories とは何か

Fixture Factories は、モデルやデータベースに依存するテストコードにおいて、テーブルの作成やデータ初期化を行うためのプラグインです。

github.com

CakePHP には元々 Fixture という仕組みが提供されていますが、 Fixture Factories はより柔軟に扱うことができます。

https://book.cakephp.org/4/en/development/testing.html#test-fixtures

導入したきっかけ

アプリケーションの規模が大きくなり、機能が増えてくると、テーブル(モデル)ごとに一律データを管理する Fixuture は管理や運用の負荷が高まっていきます。

ママリでもテストケースとデータの依存関係が強くなり、テストデータを少し変更すると既存のテストが壊れて修正が必要になるなど、気軽に変更できないことが多くなってきていました。

Fabricate を導入するなど対応は行っていましたが、関連データの生成やシーケンシャルな値の発行等でママリでのユースケースにはマッチせず、十分な解決策には至っていませんでした。

GitHub - sizuhiko/Fabricate: PHP data generator for Testing inspired on Fabrication and factory-girl from the Ruby world.

そこで、Fixture Factories を導入することにしました。

Fixture Factories のいいところ

一部ですが、特に便利だなと思っているところを紹介します。

公式が推している

公式ドキュメントでも明確に Fixture 肥大化時の解決手段として言及されています。

https://book.cakephp.org/4/ja/development/testing.html#id20

github.com

API が直感的かつ柔軟

主観を多分に含みますが、かなり使いやすく読みやすいと思います。

<?php
// Entity を生成するとき
$article = ArticleFactory::make()->getEntity();
<?php
// Entity を永続化するとき
$article = ArticleFactory::make()->persist();
<?php
// フィールドを書き換えるとき (未指定のフィールドはデフォルト値で生成される)
$article = ArticleFactory::make(['title' => 'Foo'])->getEntity();
$article = ArticleFactory::make()->setField('title', 'Foo')->getEntity();
$article = ArticleFactory::make()->patchData(['title' => 'Foo'])->getEntity();
<?php
// テストデータを複数件まとめて作るとき
$articles = ArticleFactory::make(2)->getEntities();
// フィールド書き換えと同時に行うこともできます
$articles = ArticleFactory::make(['title' => 'Foo'], 3)->getEntities();
$articles = ArticleFactory::make(3)->setField('title', 'Foo')->getEntities();

関連データの生成が柔軟

モデルの Association に従って、スムーズに関連データの生成が行なえます。

# Bake コマンドで -m オプションを指定すると、関連データ生成のメソッドも生やしてくれます
bin/cake bake fixture_factory -m Articles
<?php
// 関連データをまとめて作る
$country = CountryFactory::make()->withCities()->persist();
<?php
// 関連データの値や件数も柔軟に変更できる
$country = CountryFactory::make()->withCities(3)->persist();
$country = CountryFactory::make()->withCities(['is_capital' => true])->persist();
$country = CountryFactory::make()->withCities(['is_capital' => true], 3)->persist();

Faker が使える

初期データは Factory の setDefaultTemplate メソッドで定義するのですが、 Faker が使えるのでテストデータ生成がスムーズです。

<?php
class ArticleFactory extends BaseFactory
{
    // ~~~~ 略 ~~~~~
    protected function setDefaultTemplate(): void
    {
          $this->setDefaultData(function(Generator $faker) {
               return [
                    'title' => $faker->text(30),
                    'body'  => $faker->text(1000),
               ];
          })
          ->withAuthors(2);
    }
    // ~~~~ 略 ~~~~~
}

呼び出し元で callback を使って利用することも可能です。

<?php
$article = ArticleFactory::make(
    fn(ArticleFactory $factory, Generator $faker) => [
        'title' => $faker->text,
    ]
)->persist();

ちなみに Faker は CakePHP 側の defaultLocale を参照するので、 ja_JP を指定しておくと日本語のテストデータ生成もできます。

その他 Tips

使用するケースは限られるかもしれませんが、参考までに。

データを組み合わせごと複製する

特定の組み合わせのデータを、そのまま任意の数複製することができます。

下記の例では、 is_admin が true / false で 5 件ずつ、合計 10 件のテストデータが生成されます。

<?php
$users = UserFactory::make(
    [
        ['is_admin' => true],
        ['is_admin' => false],
    ],
    5
)->persist();

テストのときだけ Association を書き換える

あまりないと思いますが、 Factory で getTable をオーバーライドすることで、 テストのときだけ有効な Association を定義することができます。

何らかの事情でプロダクションコードでは Association を定義できないが、テストの場合は Association をあるものとしてテストデータ生成を効率化したいケースなどで利用できます。

<?php
class ArticleFactory extends BaseFactory
{
    // ~~~~ 略 ~~~~~
    public function getTable(): Table
    {
        $table = parent::getTable();

        // 一時的に Association を定義
        $table->hasOne('Authors')->setForeignKey('author_id');

        return $table;
    }
    // ~~~~ 略 ~~~~~
}

おわりに

今回は Fixture Factories の導入と、その便利な機能の一部について紹介しました。

かなり便利さを実感しており、どんどん活用していこうと思います!

PR

コネヒトでは一緒にテストを改善していく仲間を募集しています!

hrmos.co

2022年7月最近のスマイル制度活用事例

コネヒトには「スマイル制度」という制度があります。これは開発組織でのインプットとアウトプットの活性化を促進する制度です。とてもコネヒトらしい制度になっているので、詳しくはスマイル制度 - Connehito Tech Visionや、制度が生まれた当時の記事をご覧ください。

tech.connehito.com

その後、カジュアル面談などで社外の方から「実際どういったものに使われているんですか?」と質問いただくケースがちらほらあるため、どういったインプット・アウトプットが行われたかを発信していければと思います!

インプット事例

アウトプット事例

イベント開催、LT、個人ブログ、OSSコントリビュートと色々な種類のアウトプットがありました。

speakerdeck.com

connehito.connpass.com

zenn.dev

masatakashiwagi.github.io

github.com

以上です!

社内Go勉強会を始めました

こんにちは!Webエンジニアのaboyです。最近はママリの検索体験を最高にする仕事をしています。

今回は、最近コネヒト社内でやっているGo勉強会の取り組みを紹介します。

なぜGo勉強会を始めたか

コネヒトが掲げるTech Visionの戦略の内のひとつ「Let’s Go」を盛り上げるためです。

コネヒトは、エンジニア組織及び技術領域において、何を大事にし何に投資していくかと言った未来の構想や方向性をまとめたTech Visionを掲げています。その中に、Go言語を新たに武器にしていくLet’s Goという戦略があるのですが、まだ社内にGo言語の事例も少なく、Goに関わる機会もほとんどありませんでした。

そんな中で、4月から新しく構築するシステムでGoを使うことになり、コネヒトでGoを盛り上げていくのに良いタイミングでもあったので、組織としてGoに触れる量を増やすために勉強会を始めました。コネヒトにはすでにWeb全般のテーマを扱う社内勉強会があったのですが、特定技術の話に集中しづらいため、Goに特化したものとして始めることにしました。

最初の案内

やってみてる感想

計5回開催した

4月から計5回やってみて、とりあえず社内に「Goの催しをやってるぞ」という空気は出せているかなと思います。「Tech Visionにはあるけどあんまり盛り上がっていない」状態からは少し改善したかなと。

会の内容は、気になった記事などのネタを持ち込んで、それについて雑談するという感じです。例えば以下のようなテーマはGoに全く触れたことがない人でも話題に入りやすく、盛り上がりました。

  • ディレクトリ構成どうしてる?
  • コーディング規約はある?Linterとかは?
  • Webアプリケーションフレームワークどういうのある?どう選ぶ?
  • ロギング、スタックトレースどうする?

あとは、外部の記事を紹介するのも学びが多いですが、自分が学んだことを紹介するほうが自分の言葉で話せることが多く、盛り上がる傾向があると感じます。

参加人数は5〜8人程度で、元々Goに興味がある人、以前の職場でGoを扱っていた人等が参加してくれています。コネヒトのエンジニアのうち3分の1程度が参加してくれているのでまずまずかなと思っています。参加してくれるメンバーは皆チャットをたくさん盛り上げてくれるのでとても助かっています。

一方で、人はそれなりに集まりますが、ネタが集まりにくいことが課題です。業務でGoを扱っているメンバーがまだ少ないため、意図的にネタを仕入れるようにしないと集まりにくい構造になっています。これについては今業務でGoを書いている僕が、前回開催時から今回までに学んだことを話すコーナーを設けることで一定確保するようにしてみています。

最近のaboyコーナー

また、参加者によってGoへの興味や習熟度が異なるため、扱うテーマは悩みどころです。今のコネヒトはこれからGoを導入していくフェーズなので、PHPなど既存のアプリケーションと共通の話題を混ぜた方が興味を持ってもらいやすいのではないかと感じています。前述したテーマの他には、例えばテストや静的解析のような話題ですね。

おわりに

社内でやっているGo勉強会について紹介しました!継続して、社内を盛り上げていきたいと思います ԅ( ˘ω˘ԅ)

それから、GoのLT会も開催しているので、興味がある方はぜひご参加ください〜。

connehito.connpass.com

最後に、コネヒトでは成長中のサービスを支えるために一緒に働く仲間を様々な職種で探しています。 少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来ると嬉しいです!

www.wantedly.com

ECS×Fargateのオートスケールをチューニングしてサービス運営費を削減した話

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、ECS×Fargateで運用しているサービスの「ターゲット追跡ServiceAutoScalling」をチューニングをしたことで、費用が約半分になるという大きな成果を残すことが出来たのでその内容を経緯と共にまとめています。

内容はざっくり下記3点です。

  • なぜオートスケールのチューニングをしたのか?
  • 「ターゲット追跡ServiceAutoScalling」のチューニング方法
  • どんな結果になったか?

なぜオートスケールのチューニングをしたのか?

コネヒトではWebのアーキテクチャはほとんどECS×Fargateの基盤で動かしています。そして、オートスケールとして「ターゲット追跡ServiceAutoScalling」を使うことで、Fargateのメリットを最大限活かす形で運用コスト低くサービス運用を実現しています。

ここらの話は下記のスライドやブログに詳しく書いているので興味がある方はご覧ください。

ECS×Fargateで実現する運用コストほぼ0なコンテナ運用の仕組み/ ecs fargate low cost operation

speakerdeck.com

ECS×Fargate ターゲット追跡ServiceAutoScallingを使ったスパイク対策と費用削減 - コネヒト開発者ブログ

tech.connehito.com

これまで約1.5年ほど初期に設定したオートスケールのしきい値で問題なくサービス運用出来ていたのですが、ある時を境にレイテンシ悪化のアラートが頻発するようになりました。

よくわからないレイテンシ悪化だったので、アプリケーション側やインフラ側のレイテンシ悪化前後での変更、そしてアクセス特性の分析といった調査をして原因切り分けを行っていきました。

その中で、現状のオートスケールのしきい値 (CPU使用率を30%程に収束させるような「ターゲット追跡ServiceAutoScalling」の設定)では、特定コンテナのLoadAverage(LA)が跳ね上がり、そのコンテナの処理が遅くなることで平均レイテンシが上振れしているという事象を突き止めました。

スパイク的なアクセスに今までは耐えられていたのですが事象がしばらく続いてしきい値を調整しても事象は収まらなかったので、 「ターゲット追跡ServiceAutoScalling」1本で行くのは限界と判断し、新たにECSの「ステップスケーリングポリシー」を導入しました。

ECSのステップスケーリングポリシーとは何か?

「ターゲット追跡ServiceAutoScalling」がCPU使用率/メモリ使用率/リクエスト数の3つから追跡するメトリクスと値を選択することで、ECS側でタスク必要数 (起動するコンテナ数)をしきい値に収束するように動的にコントロールしてくれる機能なのに対して、「ステップスケーリングポリシー」は特定のトリガが発生した時に一気にスケールアウトして タスク必要数 を増やすような用途に使えます。

ステップスケーリングポリシー

docs.aws.amazon.com

今回の対応だと、 CPU Utilization Max の値がしきい値を超えたタイミングで、タスクを3つスケールアウトするような設定を入れました。具体の挙動としては、「ステップスケーリングポリシー」を設定すると、裏でCloudWatchAlarmが作られて、そのアラームをトリガーとしてECS側でスケールアウトが走るような仕組みになっています。

これを入れることで、スパイクの初動の段階で一気にスケールアウトが走るようになりレイテンシ悪化のアラームを抑えることに成功しました。

次なる課題

「ステップスケーリングポリシー」を導入したことによってレイテンシアラームは抑えることに成功したのですが、思想的に十分な余裕を持ってリクエストを迎えるための設定になるのでタスクの起動数が上振れ(費用増)するようになりました。「ターゲット追跡ServiceAutoScalling」のしきい値はそのままに、「ステップスケーリングポリシー」でオートスケールを追加したので当然の結果です。

ここからは、「ターゲット追跡ServiceAutoScalling」のチューニングをして、Fargateタスクの平均起動数を下げることで費用削減にチャレンジしました。

「ターゲット追跡ServiceAutoScalling」のチューニング方法

チューニングの方針として、「タスクあたりのリクエスト数(1コンテナが捌くリクエスト数)」を上げるのを目的に、「ターゲット追跡ServiceAutoScalling」でしきい値としているCPUUtilization avgを上げていくアプローチをとりました。

下記のキャプチャは、CloudWatchで見られる RequestCountPerTarget の推移なのですが、短期で見ると振れ幅ありつつも長期トレンドでは徐々に下がっていることがわかります。これが下がるということは、全体のリクエスト数は少しずつ成長しているとした場合に、1タスクあたりのコスト効率が悪化していることを意味します。

「ターゲット追跡ServiceAutoScalling」のチューニング方法としては、下記を繰り返して最適値を探りました。

  1. しきい値とするCPUUtilization avgの値を1%インクリメント
  2. 2日間問題が起きないかという様子を見る
    1. ガードレールメトリクスとして、アラートならずともレイテンシ悪化がゆるやかに起きていないかをチェックする
  3. 問題なければ1に戻る。該当期間にアラートが出たらしきい値を元に戻して2の様子見期間へ。

この繰り返しで1ヶ月程かけて最適値を探りました。チューニング方針が慎重すぎないか?という感想をもしかしたらもたれるかもしれません。

ただ、本番運用サービスでチューニングを行うので出来るだけリスクを抑えたいという判断から時間はかかりますが、1%ずつの調整を行うことにしました。

また、しきい値調整のオペレーション自体は軽く、監視も基本的にはアラートがならない限りはアクションしないという方針で取り組んだので、長い時間をかけることが出来たというのも正直なところです。

モニタリングには、CloudWatchを使ってモニタを作り、チューニングが与える変化をウォッチしていました。

下記の4項目を一つのモニタにまとめています。

  • ALBのReqCount(該当サービスのリクエスト数)
  • ECS CPUUtilization avg(該当ECSサービスのCPU使用率 平均)
  • ECS CPUUtilization max(該当ECSサービスのCPU使用率 最大)
  • ECS RunningTaskCount(該当ECSサービスで起動しているタスク数)

例えば、このモニタからこんなことが読み解けるような作りになっています。

  • ALBのReqCountが急激に上がった時に、ECS CPUUtilization maxがしきい値を超えてECS RunningTaskCountが3タスクスケールアウトしており、ECS CPUUtilization avgはしきい値前後で収束している
  • 夜間でALBのReqCountが落ちてくると、ECS CPUUtilization avgが収束し、RunningTaskCountが最小値の値までスケールインした

また、CloudWatchは水平/垂直の注釈を入れれるので設定変更のタイミングやしきい値に注釈を入れることでグラフを見ただけで読み取れる情報が格段に増えます。この機能は便利だなと思っています。

どんな結果になったか?

チューニングは、アラートが鳴るギリギリのところまでレイテンシが悪化する状況が見えてきたところで一旦インクリメントは終わりとして、しきい値から少しゆるめた落ち着いたところでFixさせました。

結果として目標においていた、以前の水準まで「タスクあたりのリクエスト数(1コンテナが捌くリクエスト数)」を上げることに成功しました。また、それに応じてSavingsPlansで購入する Compute Savings Plans を半分ほどに削減することが出来、システム運営費の削減に成功しました。

※この半年後のSavingsPlansの切れるタイミングで、測定したところ従来の半分くらいの購入で良いことが判明した

最後に宣伝です! コネヒトでは一緒に成長中のサービスを支えるために働く仲間を様々な職種で探しています。 少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来るとうれしいです。

コネヒト株式会社

hrmos.co