はじめに
初めまして。BASEのエンジニアの田中大貴です。お客様の安心安全な購入を実現するためデータ分析や不正決済検知モデルの開発・運用を頑張っています。
今回は、チームのより良い開発環境を作るために行ってきた施策の事例をご紹介します。(機械学習に特有の問題ではない施策が多いです。)
開発フロー
BASEでは、ショップの開設から購入に至るまで、様々なシーンで機械学習モデルが運用されています。私が所属するData Strategyというチーム(以下弊チーム)ではこのような機械学習モデルの開発運用をしています。
機械学習モデルによる推論は、APIによるリアルタイム推論もしくはワーカーによるバッチ推論によって提供します。弊チームではAPI・バッチをECSやLambdaを使って実装していることが多いです。典型的な推論部の開発の流れは以下の通りです。例としてECS上で動いているAPIに何かしらの変更を行うケースを示しています。

APIのコードとIaC(Terraform)のコードは別のレポジトリで管理しており、APIコード変更とTerraformコード変更の2段階によって本番環境への変更を行います。
開発環境改善の取り組み
このような開発フローのもと、開発環境の改善のために以下のような取り組みを行ってきました。
APIコード変更レビュー・Terraformコード変更レビュー (手間★☆☆・効果★★☆)
以前は、以下のような課題がありました:
- branch protection ruleが一部のレポジトリにのみ導入されていた
- 意図しないmainブランチへのpushが発生する可能性があった(ブランチを切るのを忘れてmainにpushしかけてヒヤッとした経験は誰しもあるはず…笑)
- mainブランチのコードがフォーマットチェックやユニットテストを通過していない場合でもマージされてしまう可能性があり、コード品質の低下に繋がるリスクがあった
- プルリクエスト作成時に、レビュワーの指定を逐次行う必要があった
- レビュー依頼漏れやレビュワーの選定に時間がかかる場合があった
これらの課題を解決するために、以下の施策を実施しました。
- Branch Protection Ruleの設定
- 全てのレポジトリに対して、「Require a pull request before merging」と「Require status checks to pass before merging」を有効化
- Code Ownerの設定
.github/CODEOWNERS
ファイルを作成し、チームをCode Ownerとして指定- branch protection ruleに「Require review from Code Owners」を設定
これにより、以下の利点が得られました。
- 意図しないmainブランチへのpushを防ぐことができるようになり、コード管理の安全性が向上
- mainブランチのコードがフォーマットチェックやユニットテストを通過していることを保証できるため、コード品質を保つことができる
- プルリクエストが完成すると、自動でチームにレビュー依頼が飛ぶ仕組みを作ることで、レビュー依頼漏れや手間を削減
ECRへのイメージプッシュ自動化 (手間★★☆・効果★★★)
以前は、コード変更のapprove後、開発者のローカルPCでlatestタグをつけて手動でビルドを行い、ローカルPCからECRへイメージプッシュを行っていました。
このプロセスには以下のような課題がありました。
- イメージプッシュのし忘れの可能性があった
- 最新バージョンのコードがECRにプッシュされているかが開発者に依存しており、チーム内で最新のイメージが使用されているか不明確な状況があった
- 本番環境で稼働しているコードのバージョンを直ちに把握できなかった
- ローカルPCの環境差異によって、プッシュしたコードが動かない場合があった(特にIntel製CPUからApple製CPUへの移行期において問題が顕著でした)
- ビルドプロセスが煩雑になると、オンボーディングコストも高くなった
これらの課題を解決するために、以下の施策を実施しました。
- Github Actionsによる自動化
- mainブランチへのマージをトリガーに、Github Actions上でイメージをビルドし、ECRへ自動でプッシュする仕組みを導入
- Gitコミットハッシュを用いたタグ付け
- ECRへプッシュするイメージには
latest
タグに加え、Gitのコミットハッシュをタグとして付与することで、バージョン追跡性を向上
- ECRへプッシュするイメージには
これにより、以下の利点が得られました。
- ビルドプロセスをCIに集約することで、属人的な作業を削減し、手間やミスを防止
- 最新バージョンのコードが常にECRにプッシュされている状態を保証
- 本番環境で稼働しているコードのバージョンを即座に把握できるようになり、トラブル時の調査効率を向上
- ローカルPCの環境依存による問題を排除し、安定したビルド環境を提供
監視
サービスを安定的に運用するためにはサービスの状態を監視することが重要です。ここからは監視の改善について取り組んだことをご紹介します。
環境・ログレベル別のアラーム管理 (手間★★★・効果★★★)
以前は、以下のような課題がありました。
- 1つのSlackチャンネルに重要度高~低の様々なアラームが混在していた
- 重要なアラームが見過ごされる可能性があり、インシデント対応が遅れるリスクがあった
- 不要なアラームが多く、必要十分なアラーム設定ができていなかった
これらの課題を解決するために、以下の施策を実施しました。
- アラーム通知先の整理
- 全社向けのアラートチャンネル運用指針(緊急度高・中・低の3段階で環境別にチャンネルを作成)に従い、アラーム通知先を整理
- アラームのレビューと整理
- 現状設定されているアラームを1つずつレビューし、必要なアラームは新たに作成したアラートチャンネルへ通知するよう設定
- 特に緊急度の高いアラームについては、発砲後に何のアクションも取られていないアラームは廃止
これにより、以下の利点が得られました。
- 緊急度の高い重要なアラームが集約され見過ごしリスクが低下
- アラームが緊急度別に適切なチャンネルに通知されることで、認識負荷の軽減
- 不要なアラームを削除することで、運用負荷の軽減
アラームは放置していると基本的にはどんどん増え続けていくものです。定期的にレビューし、メンテナンスを行うことが重要です。
ECSサーキットブレーカーの設定 (手間★☆☆・効果★☆☆)
こちらは個別の設定になってしまいますが、ECSを利用している方の参考になればと思い紹介します。
ECSをローリングアップデートで更新する場合に、デプロイが何らかの原因で失敗した際に、ECSはデプロイを繰り返します。そのため、デプロイしたと思っていた変更が実はデプロイ出来ていなかった…という事態が発生する可能性がありました。
ECSサーキットブレーカーはそのようなデプロイ失敗を自動で検知し、一定回数のリトライ後にデプロイを中断・ロールバックする機能です。ECSタスク定義でフラグをtrueにするだけで、導入はとても簡単です。
弊チームでは、サーキットブレーカーを導入した上で、EventBridgeでサーキットブレーカー発動イベントを拾い、Slackに通知を行っています。これにより、開発者はデプロイ後AWSコンソールに張り付いていなくても、デプロイ失敗に気がつくことができるようになりました。
(参考までに、EventBridgeのイベントパターンは以下のように設定しています)
{ "source":[ "aws.ecs" ], "detail-type":[ "ECS Deployment State Change" ], "detail":{ "eventName":[ "SERVICE_DEPLOYMENT_FAILED" ] } }
おわりに
BASEの機械学習チームでより良い開発環境の実現のために取り組んできたことを紹介しました。もう少し機械学習っぽい中身にできれば良かったのですが、いわゆるMLOps的な内容はまた別の機会にご紹介できればと思います。
このような施策は地味なものが多いですが、誰かが一度設定すればチーム全員が恩恵を受けることができるので、レバレッジ効果が大きいです。また、潜在的なインシデントを減らしたり、時間的・金銭的コストカットにも繋がったりするものも多いです。時間ができた時にぜひ諸々の設定や開発フローを見返してみてください。
BASEではメンバーを採用中です!ご興味のある方は是非以下をご覧ください。