こんにちは。SRE部プラットフォームSREブロックの高塚です。 6月20日、21日の2日間に渡って幕張メッセで開催された AWS Summit Japan に、SRE部から10名以上のエンジニアが参加しました。この記事では熱気あふれる会場の様子と面白かったセッションについてご紹介します! AWS Summit Japanとは 会場の様子 セッションレポート おわりに AWS Summit Japanとは www.youtube.com AWS Summit Japanは延べ3万人以上が参加する日本最大の「AWSを学ぶイベント」です。今年は昨年に引き続き幕張メッセで2日間にわたり開催されました。ライブ配信も行われたほか、2024年7月5日まではオンデマンド配信を視聴できます。 aws.amazon.com ちなみに2023年と2019年以前はAWS Summit Tokyo、2020年から2022年まではAWS Summit Onlineという名称でした。 会場の様子 入口 朝から大賑わいの幕張メッセ。 撮影:花房 撮影:花房 EXPO 250以上のブースが並びます。 撮影:花房 撮影:高塚 撮影:高塚 基調講演 今年は座席指定券が配布されました。 撮影:鈴木 撮影:鈴木 セッション 2日間で150以上のセッションが行われます。 AWS Summit Japan 2024公式サイトのスケジュール より引用 撮影:高塚 撮影:高塚 お弁当 両日先着4,000名にはお弁当が配られました。 撮影:鈴木 撮影:鈴木 AWS認定者ラウンジ AWS 認定 の保有者が使える休憩スペースです。 撮影:高塚 保有資格に応じたSWAGがもらえます。写真は全冠の江島のもの。 撮影:江島 その他 自由に描けるボード。 撮影:江島 AWS Deep Racer の日本一を決める戦い。 撮影:江島 QuizKnockによるクイズ大会は大盛況でした。 撮影:鈴木 セッションレポート ここからはSRE部のメンバーが気になったセッションを紹介します。 AWS 環境におけるセキュリティ調査の高度化と生成 AI 活用(AWS-18) AWSコスト管理の最前線(AWS-05) Amazon Aurora Limitless Database 内部アーキテクチャ詳解 ~ スケーラビリティと高可用性の秘密 ~(AWS-40) アーキテクチャ道場 2024!(AWS-59) ゼンリンデータコム様のAMDインスタンス導入による効率化事例(AP-24) AWS 環境におけるセキュリティ調査の高度化と生成 AI 活用(AWS-18) 基幹プラットフォームSREブロックの鈴木です。普段はZOZOの持っている倉庫システムやブランド様が触る管理ページなどのサービスのSREとして活動しつつ、社内のAWS管理者としてGuardDutyやOrganizationsの対応を担っています。 内容のまとめ セキュリティインシデントが発生した際の対応について、AWSの「高い可視性」と「柔軟かつ多様な自動化と機能連携」を活かして、セキュリティ調査の高度化を図ることができます。 これまではセキュリティの調査においてログを分析し、頭の中で攻撃の流れをイメージしながら分析していく必要がありました。生成AIの活用によって、このログ分析の際に発生する調査の本質ではない「クエリの作成」がAWS Config、CloudWatch Logs Insightにおいてサポートされました。 またAmazon Detectiveがあらゆるサービスを統合し、いままで分析者の頭の中で構築する必要があった攻撃の流れを構築し、調査をサポートしてくれるようになりました。この調査に関しても生成AIによって内容を要約して担当者の理解をサポートしてくれるため、セキュリティ調査の高度化が図れるようになりました。 感想 ログの分析において、自身がクエリの作成に時間を費やしてしまうことがあったため、生成AIによってこの部分をサポートしてくれるのは非常に有用だと感じました。Athenaのサポートも早く来てほしいと思います。 インシデントの対応はかなり専門性が高く、専任のチームではなく社内でAWSの管理をサービス運用の片手間で行っている弊社においては「どのように知見を共有していくか」「有事の際、いかにラクに調査ができる状態にするか」などが課題だと感じていました。 サービスの進化と生成AIによって、これらの課題に対して一歩解決に近づいたことを感じることができました。Detectiveに関しては正直費用もかかるものとなるのでこれから検討していくことになりますが、セキュリティの重要性を考えると検討する価値はあると感じます。 もしものときのためにどれだけ備えられるかがこの分野では重要だと考えているので、現在AWSが提供する備えについて幅広くどのような連携があるかを知れた点でよいセッションでした。 AWSコスト管理の最前線(AWS-05) カート決済SREブロックの伊藤です。私はコスト最適化に興味があったため、コスト管理についてのセッションに参加しました。こちらはCloud Financial Management(CFM: クラウド財務管理)を実現するにあたり、どのようなツールを使えば良いのかをユースケースを基にして紹介するセッションです。 クラウド財務管理(CFM、財務業務、FinOps とも呼ばれる)とは、クラウドリソースのプロビジョニング、デプロイ、モニタリングに対するコスト意識を浸透させ、説明責任を推進するための一連の原則や実践を指します。 S&P Global 2022年11月 Discovery Report P.5 より引用。 具体的には「5月分の利用料金が上昇した原因調査とコスト最適化」を題材として、以下のようにして各問題の提起と解決方法について紹介しています。 カテゴリ 問題提起 ツール 結果例 可視化 コストが増えた要因は何か? AWS Cost Explorer 全体でどのサービスの費用が増加しているかがわかる EC2インスタンスの費用が増加していることがわかったが、管理しているのはどこなのか? コスト配分タグ ○○というアプリによる利用料金が増えていることがわかる もっと細かく見るには? AWS Cost and Usage Reports リソース単位や時間単位での分析が可能となる 最適化 コスト最適化をどのように行えばいいか? EventBridgeスケジューラ など 土日、夜間の開発環境停止によるコストの削減 コスト最適化ハブ インスタンスタイプの見直しやストレージの見直しによるコスト削減 予測・計画 もっと早く気づくにはどうすればよかったか? 予算 の設定・ 予算アラート ・ 異常検知アラート の設定 コストが想定以上にかかっている場合に気付けるようになる この中で私が本セッションを通じて初めて知ることができたのはコスト最適化ハブです。無料で使えるということもあり、早速、開発環境用アカウントで有効化してみました。有効化後、データの収集が完了してから(24時間以上が経過後)、確認した結果が次の画像です。 実際に試したコスト最適化ハブのスクリーンショット それぞれのリソースに対してどのようなアクションが取れるかと、それによるコスト削減率、実装作業負担の高さやリソースの再起動が必要になるかなどを一覧で見ることができました。 今まで認識できていない部分もあったのでこれらを活用してコスト削減を加速させていきたいと思います。 Amazon Aurora Limitless Database 内部アーキテクチャ詳解 ~ スケーラビリティと高可用性の秘密 ~(AWS-40) 商品基盤ブロックの佐藤です。私はAmazon Aurora Limitless Databaseの背景と内部アーキテクチャについて解説するセッションに参加しました。 まず興味深かったのは、GroverとCaspianについての説明です。Aurora専用ストレージであるGroverは「The log is the Database」のコンセプトに基づき、データベースの更新ログを集め処理することでディスクI/Oを80%削減しました。Caspianはハードウェア上でオーバーサブスクリプションによりインスタンスを起動し、負荷状況に応じてリソースをダウンタイムなしでミリ秒単位で割り当てることができます。この処理中、データベースのワークロードには一切影響を与えず、さらにキャパシティが枯渇した場合は別のハードウェアへライブマイグレーションを行う機能もあります。 配布資料(PDF) のP.9より引用 配布資料(PDF) のP.10より引用 Amazon Aurora Limitless Databaseはマネージドシャーディングによりライトスケーラビリティとストレージサイズを拡張できるサービスです。アプリケーション側での開発は不要で、Limitless Database用のエンドポイントにクエリを投げるだけで透過的にシャーディングを行うことができます。数百万件のトランザクションを処理してペタバイトクラスのストレージを使用でき、複数のシャードをまたいだ分散トランザクション、シャードスケールの自動調整など、そのすべてがAuroraのプラットフォームとして提供され、既存のAuroraの機能も使うことができます。 配布資料(PDF) のP.19より引用 Limitless Databaseには2つのテーブルタイプがあります。1つはシャードキーをもとにして各シャードにテーブルが分配されるシャードテーブルです。同じシャードキーを持つ関連テーブルは同じシャードに格納され、ネットワークのスループットを減らすことでパフォーマンスを最適化します(コロケーション)。もう1つはすべてのシャードにコピーが作られるリファレンステーブルです。更新が少なくテーブルサイズも小さいテーブルを設定します。この2つのテーブルタイプを活用することでシングルシャードクエリが実行しやすくなり、パフォーマンスが向上します。 配布資料(PDF) のP.25より引用 Limitless Databaseの内部アーキテクチャは2つのレイヤーで構成されています。1つは分散トランザクションを制御するルーターで、どのシャードにどのデータがあるかというメタデータを管理し、各シャードから返ってきたデータを集約してアプリケーションに返します。もう1つはデータアクセスシャードレイヤーです。ここもメタデータだけを保持し、最終的な実行プランを立てて自身のシャードに対してクエリを実行し、結果をルーターに返します。シングルシャードトランザクションでは、コミットやロールバックの処理もこのレイヤーで行います。これにより、複数のシャードで並列クエリ実行が可能になり、スループットを向上させます。このように、いかにシングルシャードトランザクション構成にできるかがチューニングポイントのようです。 配布資料(PDF) のP.34より引用 配布資料(PDF) のP.35より引用 水平スケール(シャードスプリット)はユーザー側で設定することも、Aurora側に任せることもできます。Groverによりデータのクローンは高速に行われるので、ルーターやデータアクセスシャードは軽量なメタデータのやり取りだけで完結します。そのため高速なスプリットが可能になります。普段の運用でインスタンスのスケール変更を頻繁に行う自分としては非常に関心を引いた話でした。 シャード間の整合性を取る仕組みとして、EC2 TimeSync ServiceとPostgreSQLのアーキテクチャを組み合わせたバウンディッドクロックが紹介されました。EC2 TimeSync Serviceは現在時刻の他にEarliest Possible TimeとLatest Possible Timeという時間情報の信頼区間も配信しています。あるタイムスタンプでコミットしたというルーターからの情報があっても、クエリが影響を及ぼすすべてのシャードのEarliest Possible TimeとLatest Possible Timeの信頼区間内でなければコミットは待機します。これをマイクロ秒単位で処理することで、高速にトランザクション分離レベルで制御します。 配布資料(PDF) のP.48より引用 全体を通しての感想として、導入と運用のコストが低く、煩雑なタスクを減らしマネージャビリティ(管理・運用性)を向上させるマネージドデータベースの基本コンセプトに沿った魅力的なプロダクトだと思いました。ただし、料金コストがどれくらいになるか気になりました。今後、書き込みを多く行うプロダクトでのPoCを検討したいと思います。 アーキテクチャ道場 2024!(AWS-59) フロントSREブロックの江島です。ZOZOTOWNのエンドユーザーに近い部分(フロントエンド、BFF等)を担当領域としています。私は「アーキテクチャ道場 2024!」というセッションについて紹介します。 アーキテクチャ道場について 撮影:江島 SAの方が実際に設計したアーキテクチャを題材に、チーフテクノロジストの内海さんが聴講者の前でレビューするセッションです。今回のテーマはレジリエンスでした。2つのお題に対してレジリエンスを高めるためのアーキテクチャ改善を検討します。 セッションの内容(1つ目のお題) 撮影:江島 1つ目のお題は、マルチAZ構成のシステムにおいてグレー障害やブラウンアウトが発生した場合にユーザ影響を最小化することでした。AZ障害を検知した場合にはワークロードから当該AZを切り離すという方針で検討されました。また、それを実現するためにAZ毎の独立性を高めるように設計がなされました。 セッションの内容(2つ目のお題) 撮影:江島 2つ目のお題は、信頼性の低いサードパーティサービスへ依存したアプリケーションにおいて、依存度を緩和させることにより可用性を高めることでした。両者の間に追加の緩衝機構を設ける方針で検討されました。緩衝機構は、レートリミット、リトライ、サーキットブレーカー、キャッシュや非同期処理などの機能を組み合わせる形で設計がなされました。 2つのセッションを通じて まとめとして、クラウドにおけるレジリエンスの基本的なアプローチは障害の「原因」ではなく「影響」をコントロールすることだと説明がありました。その手段として「障害の範囲を限定するための隔壁(バルクヘッド)を設けること」と「障害の波及を遮断するための緩衝機構を設けること」が推奨されました。 感想 エキサイティングで学びの多いセッションでした。深く考え抜かれたアーキテクチャに対して、様々な視点から質疑が繰り広げられる光景に心を奪われました。私自身、マルチAZでの負荷分散を前提とした構成は幾度となく見てきましたし、それがベストだと思っていました。これをあえて崩すことによりAZ毎の独立性を高めるというアプローチには驚きを覚えました。また、緩衝機構に相当する仕組みは弊社でも導入しておりますが、その必要性や重要性を再認識する良い機会となりました。本サミットで学んだことを生かして、ZOZOTOWNのレジリエンスをより高めていきたいと思います。 ゼンリンデータコム様のAMDインスタンス導入による効率化事例(AP-24) 検索基盤SREブロックの花房です。普段はZOZOTOWNの検索関連マイクロサービスにおけるQCD改善やインフラ運用を担当しています。 現在、SRE部ではコスト削減に注力しています。その中でインスタンスタイプ変更によるリソース最適化は有用な手段の1つです。システムの用途に合わないインスタンスタイプを利用しているとコストパフォーマンスが悪いまま、無駄なコストを払い続けることになります。EC2には多様なインスタンスタイプが用意されていますが、今回は特にコストパフォーマンスに優れたM7aおよびM6aインスタンスを比較・導入した事例について学びました。そのセッションについて紹介します。 M7a・M6aインスタンスタイプ 本セッションでは、先に日本AMD社からM7aとM6aについて説明がありました。M7aとM6aにはそれぞれ下記のAMD CPUが搭載されています。 インスタンスタイプ CPU M7a 第4世代 AMD EPYC "Genoa" M6a 第3世代 AMD EPYC "Milan" それぞれのインスタンスタイプに搭載されているCPUのスペックと特徴を下記に示します。 配布資料(PDF) のP.9より引用 第4世代のCPUはハイパースレッディングをオフにしているためスレッドの記載はありません。第4世代のCPUの処理速度はスレッドを使用しない方が速くなるそうです。 コストパフォーマンスについて、M6iと比較すると下記のような差があります。 配布資料(PDF) のP.7より引用 M7aの単価はIntel CPUを搭載したインスタンスタイプよりも高くなったようです。しかし、CPU性能の向上に伴い必要な台数を少なくできるため結果的にコストを抑えられます。 M7aとM6aにおいて、最適なリソースを選択するには下記の図が役に立ちます。 配布資料(PDF) のP.11より引用 M7a・M6aインスタンス導入事例 次にゼンリンデータコム社からの事例紹介がありました。 ゼンリンデータコム社では、インフラ沿革の中でコストが課題になっており、コストを抑制するための取り組みとしてリソース最適化を実施しました。パフォーマンスとコストに優れるAMDインスタンスへの変更を検討し、実際に試してみた結果、M7aとM6aの導入に至ったようです。下記にコスト抑制の取り組みの図を示します。 配布資料(PDF) のP.24より引用 AMDインスタンスを導入する際の考慮点について下記が挙げられていました。 手軽さ:簡単に導入できるか 金額面:利用料金が妥当か 性能面:処理能力に問題がないか それぞれについて詳しく見ていきます。 まずは1つ目の「手軽さ」についてです。使用中のインスタンスタイプのCPUアーキテクチャがx86互換であれば、AMDインスタンスを簡単に試すことができます。プログラムの改修なしでインスタンスタイプのみ変更すればよいためです。下記に図を示します。 配布資料(PDF) のP.27より引用 次に2つ目の「金額面」についてです。SavingPlansを使用した場合、x86互換のインスタンスタイプの利用料金は下記のようになります。 配布資料(PDF) のP.28より引用 スライドにも記載されているようにユースケースに応じて使い分けることが重要です。リソース最適なインスタンスタイプを選択すればコストは抑えられますが、そうでない場合は反対にコストが上がる可能性もあります。 最後に3つ目の「性能面」についてです。性能比較では下記の内容を実施したようです。 配布資料(PDF) のP.29より引用 性能比較の詳細を記載すると本レポートに収まらないため記載しません。性能比較の結果は、AMDインスタンスのスコアが一番高くなり、新しい世代の方が古い世代よりスコアが向上したようです。 APIサーバについてはM6aからM7aに変更したことにより、約40%の台数削減を実現したようです。性能向上に伴う台数削減により、単価が高いM7aの方がコストパフォーマンスを改善できるケースとなりました。 一方、BatchサーバについてはCPUが高負荷になる処理が少ないため、Batch処理時間は大きく変化せず、M6aの方が良いコストパフォーマンスを見込めるケースになりました。 まとめ セッションを通して、M7aとM6aのAMD CPUについて学ぶことができました。これらのCPUはx86互換であるため、同じx86系のインスタンスを使用していればAMDインスタンスを簡単に試すことができます。 ゼンリンデータコム社のシステムを使用した性能比較においても、AMDインスタンスはコストパフォーマンスに優れていることが分かりました。性能が向上するインスタンスタイプを選択した場合、本当に性能が向上するのか試すことは大事だと感じました。また、性能が向上したとしても、コスト面でのメリットが得られるかを確認することも重要です。今回学んだことを考慮して、今後のインスタンスタイプ変更によるコスト削減に取り組んでいきたいと思います。 おわりに セッションや展示ブースで多くのことを学べるのはもちろん、AWSのエキスパートや他社のエンジニアの方々と交流し、多くの刺激を受けられるのが現地参加の醍醐味です。 今回得た知見を社内外に共有しながら、これからもAWSを活用してプロダクトとビジネスの成長に貢献していきます。 ZOZOではAWSが大好きなエンジニアを募集しています。奮ってご応募ください! corp.zozo.com
はじめに こんにちは、CISO部の 兵藤 です。日々ZOZOの安全のためにSOC対応を行なっています。普段のSOCの取り組みについては以下「フィッシングハントの始め方」等をご参照ください。 techblog.zozo.com 6/10〜6/12にアメリカのフィラデルフィアで開催されたAWS re:Inforce 2024に参加してきました。この記事ではその参加レポートをお届けします。 フィラデルフィア現地会場前の様子 目次 はじめに 目次 AWS re:Inforce 2024とは セッションの紹介 Builder's Session TDR355 Detecting ransomware and suspicious activity in Amazon RDS TDR352 How to automate containment and forensics for Amazon EC2 Breakout Session TDR305 Cyber threat intelligence sharing on AWS おわりに AWS re:Inforce 2024とは re:Inforce はAmazon Web Services(AWS)が主催する大規模な技術カンファレンスであり、特にセキュリティへフォーカスしたものとなっています。 セキュリティに関する最新のAWSでの動向や、サービスのアップデートなどがセッションやワークショップを通じて紹介されます。 ZOZOのサービスをよりセキュアにするヒントを得るために、本カンファレンスに参加しました。 今年のre:Inforceは生成AIが流行していたこともあり、そのAIに対するセキュリティをどう担保していくかといったテーマが多く取り上げられていました。 re:Inforce 2024 会場 また以下のようなGuardDutyのS3対応や、IAMのPasskey対応など新機能の発表もあり、現地でも大きな話題となっていました。 aws.amazon.com aws.amazon.com 最後にClosing Receptionとして、パーティが開催されました。 Closing Receptionの様子 セッションの紹介 re:Inforceでは多くのセッション、ワークショップなどが開催されており、その中からいくつか気になったものをピックアップして紹介します。 自分は脅威情報やインシデントレスポンスに関わるセッション、ワークショップを見ることが多かったのでそちらに偏りがちな内容ですが、ご了承ください。 Builder's Session TDR355 Detecting ransomware and suspicious activity in Amazon RDS TDR352 How to automate containment and forensics for Amazon EC2 Breakout Session TDR305 Cyber threat intelligence sharing on AWS Builder's Session TDR355 Detecting ransomware and suspicious activity in Amazon RDS このBuilder's Sessionの概要は以下の通りです。 In this builders’ session, acquire skills that can help you detect and respond to threats targeting AWS databases. Using services such as AWS Cloud9 and AWS CloudFormation, simulate real-world intrusions on Amazon RDS and Amazon Aurora and use Amazon Athena to detect unauthorized activities. The session also covers strategies from the AWS Customer Incident Response Team (CIRT) for rapid incident response and configuring essential security settings to enhance your database defenses. The session provides practical experience in configuring audit logging and enabling termination protection to ensure robust database security measures. You must bring your laptop to participate. 本セッションでは AWSが公開しているWorkshop を活用してRDSに対するランサムウェア攻撃のシミュレーションとインシデント対応を実施しました。具体的には以下のような手順で実施しました。 AWS Cloud9を利用してシミュレーション環境を構築 RDSの構築 攻撃のシミュレーション ログ分析 分析に関しては攻撃者による「AWSアカウントの列挙」「データベースの列挙」「データベースの操作」などをAmazon Athenaを利用して調査しました。 Amazon Athenaは使用したことがなく、クエリベースでログ調査ができるのでとても面白いと感じました。 ランサムノートを確認した画面 TDR352 How to automate containment and forensics for Amazon EC2 このBuilder's Sessionの概要は以下の通りです。 Automated Forensics Orchestrator for Amazon EC2 deploys a mechanism that uses AWS services to orchestrate and automate key digital forensics processes and activities for Amazon EC2 instances in the event of a potential security issue being detected. In this builders’ session, learn how to deploy and scale this self-service AWS solution. Explore the prerequisites, learn how to customize it for your environment, and experience forensic analysis on live artifacts to identify what potential unauthorized users could do in your environment. You must bring your laptop to participate. 本セッションでは AWSのドキュメント のソリューションを活用します。以下のようなアーキテクチャの構成はすでにセッションでは構築されていました。 ソリューションのアーキテクチャ図 この 自動フォレンジックオーケストレーター を利用してEC2でのインシデントレスポンス対応におけるフォレンジックを行いました。 フォレンジック対応としては、ディスクのスナップショットとメモリダンプの取得になります。AWSの機能ではEBSスナップショットなどディスクデータを取得できますが、メモリダンプの取得は提供されていないため、このオーケストレーターではメモリダンプを LiME を利用して取得していました。取得したダンプはS3の方に飛ばしていることが コード からわかります。 では実際にセッションで実行した内容を紹介します。 インシデントのAlertはSecurity Hubからキャッチし、Security Hubのカスタムアクションを利用して、フォレンジック対応をスタートします。以下の画像で「Forensic Triage」を実行しました。 Security Hubでのカスタムアクション そこからEvent Bridge経由でStep Functionが起動されます。最初のFunctionは以下のように起動されます。 最初のStep Function このStep Functionを実行した際にはメモリダンプやディスク取得しませんでした。インスタンスTagを判断して次のStep Functionを実行するかしないかを判断しているようです。 次の2つのStep Functionでは以下のようにディスクスナップショットとメモリダンプを取得しています。 ディスクフォレンジックのStep Function メモリフォレンジックのStep Function メモリフォレンジックのStep Functionの中にEC2のネットワーク分離のStep Functionがあります。これは コード を確認するとセキュリティイベントタイプによって実行するのか判断しているようです。 本番環境における分離の実行は慎重に行う必要があると思うので、この部分は組織によって要検討の箇所だと考えます。 セッション中はここまでしか時間がなかったのですが、実際のフォレンジックで何をみているかというのはSSMドキュメントを参照すればある程度わかりました。 例えば メモリフォレンジックの内容 を覗いてみると、volatility2を利用していくつかコマンドを打っていることがわかります。 また、メモリダンプも取得できているので自前でフォレンジックも可能だと思います。Meterpreterなどファイルレスマルウェアの抽出だったりはこのダンプがあれば可能となります。あれば嬉しいメモリダンプですね。 EC2のフォレンジック対応は初めてだったのでとても学びの多いセッションでした。 Breakout Session TDR305 Cyber threat intelligence sharing on AWS このBreakout Sessionの概要は以下の通りです。 Real-time, contextual, and comprehensive visibility into security issues is essential for resilience in any organization. Join this session to learn about cyber threat intelligence informed security, including lessons learned from the Australian Cyber Security Centre (ACSC) Cyber Threat Intelligence Sharing (CTIS) program, built on AWS. With the aim to improve the cyber resilience of the Australian community and help make Australia the most secure place to connect online, the ACSC protects Australia from thousands of threats every day. Learn the technical fundamentals that can help you apply best practices for real-time, bidirectional sharing of threat intelligence across all sectors. このセッションでは、オーストラリアサイバーセキュリティセンター(ACSC)のサイバー脅威インテリジェンス共有(CTIS)プログラムについて紹介。そして、AWS上でどのように脅威情報を活用しているかを紹介していました。 どのように脅威情報を扱って攻撃を予防、検知しているかは以下スライドのように概要を説明されていました。AWSというより、一般的な脅威情報の扱い方の紹介ですね。真ん中のTrust CommunityがACSCで、MemberがASD 1 (Australian Signals Directorate)partnerです。JPCERTと似た雰囲気を感じました。 具体的に脅威インテリジェンス(Tactical相当)をAWSでどう活用していくかは以下のように説明されていました。 上記は脅威インテリジェンスをAWSのセキュリティサービスにどのように活用しているかを示すアーキテクチャ図です。ただAWSは公開サービスで利用していることが多いと思うので、鮮度がイマイチな情報をそのまま投入すると過検知が多くなる懸念はあります。 次は脅威インテリジェンスを利用した脅威ハンティングといったところです。利用する組織はこういった使い方が多いのではないでしょうか? これは脅威インテリジェンスをシェアする場合です。 脅威インテリジェンスをAWSで活用していくヒントを得たセッションで、とても参考になりました。 おわりに 私自身、参加当時はAWS初心者レベルでありビクビクしながら参加したre:Inforceでしたが、面白く、学びが多いセッションばかりでいい経験になりました。 本カンファレンスの経験を生かして、社内でのセキュリティ強化をより一層実施していければと思います。 ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com ASD ↩
こんにちは、ブランドソリューション開発本部で WEAR by ZOZO のiOSアプリの開発を担当している新卒2年目の山田( @gamegamega_329 )です。 今年もWWDCはオフラインとオンラインの同時開催となり、私は昨年に引き続き、2年連続で現地参加しました。昨年の現地レポートは下記の記事をご覧ください。 techblog.zozo.com 本記事では、現地ならではのイベント内容や雰囲気などを中心に、可能な範囲でご紹介します。現地参加の魅力や、実際に体験した貴重な瞬間をお伝えできればと思います。 WWDCとは? スケジュール Day 0 - 6月9日 Welcome Reception 今年のノベルティ 受付を終えた後 デベロッパーとの交流 Apple Design Awards Day 1 - 6月10日 Keynote Platforms State of the Union Discover Apple Park In-person Labs 提供されたご飯たち Day 2 - 6月11日 Evening Session Appleスタッフ&デベロッパーとの交流 その他 現地での過ごしやすさ 日本の開発者たちとの交流 現地参加するなら用意・準備しておくと良いこと 最後に 「WWDC24 報告会 at LINEヤフー, ZOZO」を開催します WWDCとは? WWDC(Worldwide Developers Conference)は、Appleが毎年開催している開発者向けの重要なカンファレンスです。このイベントでは、OSのアップデートや新しい開発ツールが発表され、開発者にとって大変有益な情報が提供されます。昨年に引き続き、今年もオフラインとオンラインのハイブリッド形式で開催されました。 スケジュール 日付 時間(Pacific Time) コンテンツ 場所 6/9 3:00 PM ・Early Check-in ・Design Award Infinite Loop 6/10 8:00 AM ・Check-in Apple Park Visitor Center 10:00 AM ・Keynote Apple Park 1:30 PM ・Platforms State of the Union Apple Park 2:00 PM ・Discover Apple Park Apple Park Inner Ring 2:00 PM ・In-person labs Apple Park Caffè Macs 6/11 6:00 PM ・Evening Session Developer Center Day 0 - 6月9日 Welcome Reception 今年もイベントの前日にInfinite LoopでWelcome Receptionが開催されました。 Infinite Loopの入口に立つ筆者 このイベントでは、事前にチェックインができるため、翌日の入場がスムーズになります。さらに、早めにイベントの雰囲気を楽しめるだけでなく、ノベルティも一足先にゲットできます。 Infinite Loop内にあったWWDC24のオブジェ 今年のノベルティ 今年のノベルティはピンバッジ、バッグ、水筒、レジャーシートでした。これから日本は暑くなるので、これらはBBQなどで大いに活躍しそうです。さらに、Apple Vision Proのピンバッジを手に入れられ感動しました。帰国したらバッグにつけようと思います。 WWDC24のノベルティはピンバッジ、バッグ、水筒、レジャーシートの4種類 受付を終えた後 軽食やドリンクを楽しみながら、参加者同士での会話を楽しみました。提供された食べ物や飲み物は、唐揚げ・春巻き・アイスクリーム・コーラなど多岐にわたり、食べ放題なので時間いっぱい楽しめます。ただし、次の日の胃もたれや顔のむくみには注意が必要です。 軽食コーナー ドリンクステーション 暑さを和らげてくれたアイスクリーム デベロッパーとの交流 アメリカ・インド・中国・韓国・ブラジルなど、多くの国籍のデベロッパーやデザイナーと交流しました。特に私は個人でリリースしたアプリの話で盛り上がり、ヘルスケアアプリや大学案内アプリ、ジェスチャー認識アプリなど、様々なアプリについて語り合いました。 現地で出会ったデベロッパーとの記念写真 私が開発した「腹筋ローラーアプリ」を紹介すると、多くの人が「Amazing」と称賛してくれました。この日に話した多くの人が個人でアプリをリリースしていて、熱い想いを共有できたのは忘れられない思い出になりました。 Apple Design Awards Infinite Loopの食堂内には、 Apple Design Awards のトロフィーが展示されていて、このトロフィーには受賞者の名前が刻まれていました。サンプル品を手に取ると、見た目よりも重量感があったので驚きました。Welcome Receptionが終わった後、表彰者のみで表彰式が行われるとのことでした。 Apple Design Awardsの受賞者に贈られるトロフィーのサンプル 気づけば7:00 pmを過ぎ、気温が下がって羽織ものがないと寒いくらいになっていました。こうしてDay 0が終了しました。 Day 1 - 6月10日 開場は8:00 amからですが、最前列を確保するために6:30 amから並び始めました。グループ会社であるLINEヤフーのエンジニアをはじめとする日本のデベロッパーたちと一緒に並びました。早朝なので少し肌寒く感じました。 待ち時間には、ポンデリングのようなモチモチのドーナツや、香り高いコーヒーが提供されました。おいしいスナックを楽しみながら、開場を心待ちにする時間はとても充実していました。 開場するまでの待ち時間にはドーナツやコーヒーが提供された 昨年は最前列に座っていましたが、今年は最前列から3列目までがEnterprise席として確保されていたため、私の席はそのすぐ後ろの4列目となりました。実質的には最前列に近い席で、安心しました。 昨年と異なり前から3列目まではEnterprise席だったので4列目に着席 席を取った後、Keynoteが始まるまでの間に用意されていた朝食を楽しみました。WWDCに参加すると、朝から夜まで食事が提供されます。どんな食事が提供されたのかについては、最後にご紹介します。 Keynoteが始まるまでの時間は周りを散策して過ごしました。OpenAI社のCEOであるサム・アルトマン氏にも遭遇しました。この日の最高気温は34度で、Keynoteが始まる直前には日差しが強くなったため、サングラスをかけて待機していました。 Keynote www.youtube.com CEOのティム・クック氏とSenior Vice Presidentのクレイグ・フェデリギ氏が壇上に立ち、短い時間トークしました。その後、Keynote本編のビデオが会場内のスクリーンで再生されました。 CEOのティム・クック氏とSenior Vice Presidentのクレイグ・フェデリギ氏 今回の目玉は、発表前から噂されていたAI「Apple Intelligence」でした。 www.apple.com 発表の瞬間、会場は拍手喝采で大いに盛り上がりました。このAIは独自のモデルであり、ChatGPTとの連携も可能ですが、単独でも機能するのが特徴です。その他にも、コントロールのカスタマイズやMath Notesなど、様々な新機能に胸を躍らせました。 会場が沸いたApple Intelligenceの発表 このアップデートによって、アプリ内だけでなく、Apple Intelligenceや他のアプリとの連携を含めて、どのように自社サービスを活用してもらうかを考える必要があると感じました。 Platforms State of the Union www.youtube.com Keynoteに続くPlatforms State of the Unionは、強い日差しを避けるために別の席から観ました。 Platforms State of the Unionは室内で視聴 私が特に注目したのはXcodeの新機能「Swift Assist」でした。この発表でも大きな歓声が上がり、会場は大いに盛り上がりました。GitHub Copilotが発表されて以来、iOSエンジニアたちはこの機能を待ち望んでいたことでしょう。 他にも、 Swift Testing や SwiftDataのアップデート など、デベロッパーにとって魅力的な発表が多く、非常にワクワクする内容でした。 Discover Apple Park セッションが終わった後には、Appleのエンジニアやデザイナーに直接質問できるラボの時間や、Apple Park内を自由に散策できる時間がありました。私はすぐにApple Parkを散策することにしました。その魅力的な見どころをいくつか紹介します。 まず、目を引くのは巨大な虹のオブジェです。まるで夢の中にいるかのようなこの場所で、私はジャンプして記念写真を撮りました。 巨大な虹のオブジェの前でジャンプして記念撮影 次に訪れたのは、まるで本物のような人工池です。波の音が心地よく流れていて、最初は本物の池だと勘違いしてしまいました。波の音も実はスピーカーが巧妙に配置されていて、そこから音が流れているとのことでしたが、スピーカーの場所は全く見つけられませんでした。 本物の池だと勘違いした人工池 さらに、この探索中にバナナチョコレート味のアイスクリームを配っていて、思わず手に取ってしまいました。暑い日にはぴったりのご褒美でした。 また、Download Stationでは、有線接続で最新のXcodeを高速ダウンロードできました。開発者にとっては嬉しい限りです。早速、私も最新のXcodeをその場でインストールしました。 高速な回線が用意されていたDownload Station In-person Labs 散策を終えた後は、Appleの社員に質問できる時間を存分に楽しみました。 Caffè Macsでは、各エリアに担当者が配置されており、その分野のスペシャリストに直接質問できます。オレンジエリアは3階、紫エリアは外で行われていました。 In-person Labsの配置図 スタッフの役割は着ているTシャツの色で分かります。詳細な技術に詳しい担当者や、広範囲にわたってメンバーの役割を把握している案内スタッフなどがいました。 Tシャツの色分けでスタッフの役割がわかりやすくなっていた 私は、セッションビデオで興味を持ったApple Intelligence、SwiftUI、Xcodeに関連する質問を投げかけました。特に、どのような仕組みで動作しているのかや、開発チームの規模について詳しく聞きました。 また、予約制のDesign Labもあり、自分のアプリをデザイナーに見せて直接フィードバックや感想をもらえます。細かなニュアンスなどの説明が難しかったので、翻訳アプリを使ってやりとりしましたが、スタッフは快く対応してくれました。 私は、自分が担当しているリニューアルしたWEARアプリを見せてフィードバックを受けました。良い点や改善点、全体的な所感について話し合いました。デザイナーによっては、絵を描いて提案してくれることもあり、ワイヤーフレームが印刷された用紙を持っているデザイナーもいました。 提供されたご飯たち 朝食には、クロワッサンやパイのような食感のエンパナーダ、フルーツの盛り合わせ、オーバーナイトオーツなど、普段食べることの多くない珍しいメニューが並びました。 朝食 昼食はたくさんメニューがありましたが、その中でも、グリルチキン&キノアサラダ、ティラミスを選びました。 昼食 昼食と同様、夕食も多くのメニューがありました。その中でも、私はクリスピーチキンワッフル、フルーツタルトとチョコレートキャラメルバー、プラリネクリームのシュークリームを選びました。 夕食 食事の時間以外でもスナックやドリンクが配られていました。 豊富に用意されていたドリンク おやつのドライフルーツ Day 2 - 6月11日 この日は予約制のセッションに参加しました。セッションは「Morning」「Afternoon」「Evening」の3つの時間帯から選べます。ただし、予約数に限りがあるため、早めに好きな時間帯を選ぶ必要があります。私は「Evening」のセッションのみ予約できました。 Evening Session 会場はDeveloper Centerで、生のプレゼンテーションを体験できる貴重な機会でした。プレゼンテーションはとてもカッコよく、迫力満点で、いつか自分もこんな発表ができるようになりたいと感銘を受けました。 参加したEvening Sessionの様子 ライブコーディングやデモなど、予約制ならではの特別な内容が盛りだくさんで、参加した甲斐がありました。 Appleスタッフ&デベロッパーとの交流 セッションが終わった後は、軽食を楽しみながら、セッションのプレゼンターやAppleのデベロッパー、さらには各国のデベロッパーたちと交流しました。 Evening Session後の交流会で提供されていた軽食 この時間をフル活用して、初日のIn-person Labsで聞き逃してしまった質問や、セッションを通じて気になったことを、セッションのプレゼンターやAppleのデベロッパーに直接聞きました。それだけでなく、ブラジルの学生とも仲良くなり、セッションの感想を語り合ったり、雑談を楽しんだりしました。 その他 現地での過ごしやすさ WWDC24の会期中は全て快晴に恵まれましたが、一日の寒暖差が激しいものでした。日中は暑く、気温は28〜31度に達しましたが、夜18時頃から急激に冷え込み、20度前後まで下がりました。日中の服装としてはTシャツと長ズボンが適していましたが、夜間の急な冷え込みに対応するため、羽織ものを持参するべきでした。 日本の開発者たちとの交流 iOS界隈で著名な方々が多く参加している印象を受けました。会うたびに情報交換することが多く、有益な時間を過ごせました。また、 Swift Student Challenge の参加者とも交流する機会があり、地域活性化のアプリを開発している方とお互いのアプリを紹介し合う場面もありました。さらに、日本のAppleエバンジェリストの方が親切に案内してくれたので、非常に助かりました。 現地参加するなら用意・準備しておくと良いこと 日本コミュニティに参加して、事前に情報収集しておくこと(例:try! SwiftのDiscord) 日差しが強いので、日焼け止めやサングラスを持っていくこと(私は現地のゴルフショップでサングラスを購入しました) 基本的に現金を持たずにクレジットカードで支払いできるが、紛失や盗難の対策を講じておくとなお良い 自社サービスや個人で開発しているアプリがあれば、アピールできる資料やノベルティなどを準備すると良い 英語が不慣れな場合でも、1か月間英会話の学習を頑張れば、翻訳アプリを活用しながら通用するレベルになる ただし、ラボなどでの技術的な会話は難しいので、事前準備は必須 最後に 今回、ZOZOからは私一人が参加し、2年連続でのWWDC現地参加となりました。昨年にはなかった要素や新たな発見、学びが多く、とても充実した日々を過ごせました。 決して英語が得意とは言えない私でも、Appleスタッフ、日本のエンジニア、そして気さくに話しかけてくれた多国籍のデベロッパーたちのサポートのおかげで、WWDCを存分に楽しめました。Appleコミュニティの素晴らしさを改めて実感しました。 次回以降のWWDCで現地参加することになった方々にとって、この記事が少しでもお役に立てば幸いです。 「WWDC24 報告会 at LINEヤフー, ZOZO」を開催します 6月26日(水)に、WWDCに参加したLINEヤフーとZOZOのエンジニアが、それぞれが興味のある分野について、新しく発表された技術や得た知見、情報などを共有する「WWDC24 報告会」を開催します。昨年に続き私も登壇予定です。ご興味のある方はぜひご参加ください。 lycorptech-jp.connpass.com ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co corp.zozo.com
こんにちは。検索基盤部の橘です。ZOZOTOWNでは、商品検索エンジンとしてElasticsearchを利用し、大規模なデータに対して高速な全文検索を実現しています。 Elasticsearchに関する取り組みは以下の記事をご覧ください。 techblog.zozo.com 検索基盤部では、ZOZOTOWNの検索結果の品質向上を目指し、新しい検索手法の導入を検討しています。本記事ではベクトル検索と呼ばれる検索手法に関して得た知見を紹介します。 ※本記事はElasticsearchバージョン8.9に関する内容となっています。 目次 目次 ベクトル検索とは ベクトル検索に期待すること Elasticsearchを使用したベクトル検索の導入 導入の簡略化 デプロイ可能な埋め込みモデル ベクトル検索のクエリ ハイブリッド検索とは Elasticsearchを用いたハイブリッド検索 RRF(Reciprocal rank fusion)を使うパターン rescoreを使うパターン ベクトル検索の定性評価 ベクトル検索の導入に伴う課題 類似度の閾値設定 モデルの埋め込み精度の問題 クエリごとの類似度のばらつき モデルの変化による類似度の分布の変化 まとめ おわりに ベクトル検索とは ベクトル検索とは、データを高次元のベクトル空間にマッピングし、類似性に基づいて情報を検索する技術です。 一般的なベクトル検索の方法は、ユーザーの検索クエリをベクトル化し、同じベクトル空間にマッピングされた商品データとの類似度を計算します。類似度が高い商品から順に検索結果としてユーザーに表示します。 下図において商品Aは商品Bより検索クエリとの角度が小さく、類似度が高いため、検索結果では商品Aの方が上位に表示されます。 検索クエリや商品データのベクトル化には埋め込み(Embedding)モデルを利用する方法が一般的です。 CLIP などのマルチモーダルモデルを使うことで文字列データに加え画像データも同じ空間にベクトル化できます。 ベクトル検索に期待すること ベクトル検索で特に期待できる点は、曖昧なクエリに対してより良い結果を出力できることです。 例えば、ZOZOTOWNでは次のような曖昧な検索クエリが見られます。 ベビー用品 きれいめワンピース フォーマルなスーツ 例えば「ベビー用品」という検索クエリの場合、「用品」という単語は広い意味を持ちます。全文検索では「用品」という文字が商品情報と完全に一致する必要があるため、検索結果が限定されます。 一方、ベクトル検索では「ベビー用品」という検索クエリで次のような商品を検索できます。 ベビー服 ベビー用おもちゃ ベビーカー その他ベクトル検索に期待できる点は以下です。 テキスト以外のデータでの検索:画像などのデータも検索に利用可能 表記揺れクエリに対して堅牢:検索クエリ「バック」で「バッグ」を検索可能 多言語への対応:検索クエリ「Tshirt」で「Tシャツ」を検索可能 Elasticsearchを使用したベクトル検索の導入 Elasticsearchは上述のベクトル検索をサポートしており、Elasticsearchでのベクトル検索導入には次のメリットがあります。 スケーラビリティ:Elasticsearchは分散アーキテクチャを採用しており、大規模なデータに対しても効率的にスケールアップできます。 高速な検索性能:ベクトル検索を含む大量のデータに対する複雑な検索を高速で処理できます。特にZOZOTOWNのように検索対象の商品数が多い場合の使用に適しています。Elasticsearch8.0以降では HSNW をベースとした近傍検索による高速な検索が可能です。 統合された検索環境:Elasticsearchは全文検索とベクトル検索の両方をサポートしています。全文検索とベクトル検索の組み合わせで検索精度の向上が期待できます。 導入の簡略化:埋め込みモデルをElasticsearchにデプロイし、ベクトル検索導入を簡略化できます。 導入の簡略化 ベクトル検索を実現するためには検索クエリと商品データを埋め込みモデルによりベクトル化する必要があります。これらのベクトル化処理のためのバッチやAPIの開発は、ベクトル検索導入の際のハードルとなります。 Elasticsearchでは、埋め込みモデルをElasticsearchにデプロイする方法が用意されており、これによりベクトル検索の導入を簡略化できます。 詳しい導入手順は以下の記事をご覧ください。 Elasticsearchで日本語NLPモデルを利用してセマンティック検索を実現する | Elastic Blog 簡単な手順としては次の通りです。 Elasticが提供するPythonのElasticsearchクライアントライブラリ eland を使用して、 Hugging Face 上のモデルをアップロードしデプロイ インジェストパイプライン を作成し、 推論プロセッサー でフィールドのデータをベクトル化しインデックスを作成 knnクエリ(後述)の query_vector_builder オプションを使うことで検索クエリをベクトルに変換し、類似度の高い順の検索結果のレスポンスを取得 このように、埋め込みモデルをElasticsearchにデプロイすることで、ベクトル化処理のためのバッチやAPIの開発を省略してベクトル検索を導入できます。 デプロイ可能な埋め込みモデル ベクトル検索に使うモデルはHugging Faceに登録してあるモデルの中から Feature Extraction のタスクに対応しているモデルを選ぶ必要があります。例えば、 multilingual-e5-large が該当します。 Hugging Face上にある事前学習済みモデルをファインチューニングしたモデルも使用できます。その場合、ファインチューニングしたモデルをHugging Face上のリポジトリにアップロードし、elandを使ってElasticsearchにアップロードします。 ベクトル検索のクエリ knnクエリ を使うことにより、Elasticsearchでベクトル検索できます。 クエリ例は以下の通りです。 { " knn ": { " field ": " text_embedding.predicted_value ", " query_vector_builder ": { " text_embedding ": { " model_id ": " cl-tohoku__bert-base-japanese-v2 ", " model_text ": " shoes " } } , " k ": 10 , " num_candidates ": 100 , " similarity ": 0.8 , " boost ": 1 , " filter ": { フィルタリング条件を記載 } } } knnクエリのパラメータは次の通りです。 パラメータ名 説明 field 検索対象のフィールド query_vector_builder ベクトル化に使うモデルidとベクトル化するテキスト k 結果出力数 num_candidates 各シャードから抽出する候補数 similarity 類似度の下限閾値 boost 類似度(スコア)に掛ける重み filter フィルタリング条件 ハイブリッド検索とは 上記でベクトル検索の利点を紹介しましたが、全文検索にもいくつか利点があります。 実装や導入が比較的容易 大規模な情報に対して高速な検索が可能 検索クエリとドキュメントのマッチ方法が明確で、結果の解釈が容易 全文検索とベクトル検索はそれぞれ利点が異なります。両方を組み合わせることで、それぞれの利点をうまく活かし、より高い検索精度を期待できます。全文検索とベクトル検索を組み合わせる検索手法は「ハイブリッド検索」と呼ばれます。 Elasticsearchにはハイブリッド検索を実現する機能が用意されています。 Elasticsearchを用いたハイブリッド検索 ここではElasticsearchを使ったハイプリッド検索のパターンをいくつか紹介します。 RRF(Reciprocal rank fusion)を使うパターン RRF は異なる検索結果のランキングを統合し、ランキングを生成するアルゴリズムです。 RRFは以下の数式で表されます。 パラメータ名 説明 統合するランキングの数 ドキュメントのランキング順位 調整定数 ランキング対象となるドキュメントの集合 RRFを利用し、全文検索のランキングとベクトル検索のランキングを統合するクエリ例は以下の通りです。ベクトル検索と全文検索のランキングスコアのスケールは異なりますが、RRFを使うことでスコアのスケールを合わせる必要なくランキングを統合できます。 { " size ": 10 , " query ": { " term ": { " text ": " shoes " } } , " knn ": { " field ": " text_embedding.predicted_value ", " k ": 10 , " num_candidates ": 100 , " query_vector_builder ": { " text_embedding ": { " model_id ": " cl-tohoku__bert-base-japanese-v2 ", " model_text ": " shoes " } } } , " rank ": { " rrf ": { " window_size ": 10 , " rank_constant ": 1 } } } 上記のクエリでは、まずshoesという用語に一致するドキュメントを全文検索で取得し、次に同じshoesを基にベクトル検索で最も近いドキュメント上位10件を取得します。その後、全文検索とベクトル検索の結果を統合し、RRFを適用して最終的なランキングを生成しています。 RRFクエリのパラメータは次の通りです。 パラメータ名 説明 window_size 各ランキングにおいてRRFアルゴリズムを適用する上位N件 rank_constant 各ランキング内のドキュメントが最終的なランキングにどれだけ影響を与えるかを決定する調整定数(値が高いほど、低ランクのドキュメントがより大きな影響を与える) rescoreを使うパターン rescore クエリによって全文検索とベクトル検索の結果をリランキングし統合できます。 rescoreを利用し、全文検索のランキングとベクトル検索のランキングを統合するクエリ例は以下の通りです。 { " size ": 10 , " query ": { " term ": { " text ": " shoes " } } , " knn ": { " field ": " text_embedding.predicted_value ", " k ": 10 , " num_candidates ": 100 , " query_vector_builder ": { " text_embedding ": { " model_id ": " cl-tohoku__bert-base-japanese-v2 ", " model_text ": " shoes " } , " boost ": 0.0001 } } , " rescore ": [ { " window_size ": 10 , " query ": { " rescore_query ": { リスコアのロジックを記載 } , " query_weight ": 0 , " rescore_query_weight ": 1.0 , " score_mode ": " total " } } ] } 上記のクエリでは、全文検索の結果とベクトル検索の結果を組み合わせてリスコアを行い、最終的に上位10件の結果を取得しています。また、knnクエリのboostの値を低く設定することで、全文検索の結果が優先してリスコアされるようにしています。 クエリ内でRRFとrescoreクエリは併用できないので注意が必要です。 ベクトル検索の定性評価 社内で全文検索の結果とベクトル検索の結果をオフライン定性評価しました。定性評価では、被験者がいくつかの評価用クエリに対する全文検索の結果とベクトル検索の結果を見比べて、どちらの検索結果が良いかを評価します。 ZOZOTOWNで実施している定性評価の詳細は以下の記事をご覧ください。 techblog.zozo.com 以下は、オフライン定性評価でベクトル検索が良い結果を出したクエリと悪い結果を出したクエリの例です。 ベクトル検索が良い結果となったクエリ ベクトル検索が悪い結果となったクエリ ペット用品 きれいめドレス 小さめリュック スポーツウェアー いちご柄 卒業式 女の子 小学生 フォーマル ブランド名全般 キャラクター名全般 これらのクエリと検索結果を考察し、ベクトル検索が適用可能だった語と適用困難だった語は次の通りでした。 ベクトル検索が適用可能だった語 説明 曖昧な語 「用品」「きれいめ」など曖昧なクエリに対しては近い意味の商品を検索可能 多言語 「小さめ」のクエリに対して「small」という語を含んだ商品を検索可能 表記揺れ 「ウェアー」のクエリに対して「ウエア」「ウェア」という語を含んだ商品を検索可能 ベクトル検索が適用困難だった語 説明 明確な語 「いちご柄」のクエリに対してドット柄のような商品を誤って検索 複雑な語 「卒業式 女の子 小学生 フォーマル」のような様々な意味を含む複雑なクエリに対して「ネクタイ」を誤って検索 ドメイン性が強い語 ブランド名やキャラクター名などドメイン性が強いクエリに対して似たブランドやキャラクターの商品を検索することは不可能 このようにベクトル検索が適用可能な語と適用困難な語を評価することで、どのような検索クエリに対してベクトル検索が効果的かを理解できました。 ベクトル検索の導入に伴う課題 ベクトル検索の導入を検討するにあたり、いくつかの課題がありました。代表的な内容をいくつか紹介します。 類似度の閾値設定 類似度の低い商品はクエリとは無関係である可能性があります。クエリとは無関係な商品が含まれることを防ぐために、適切な類似度の閾値を設定する必要があります。 この閾値の設定方法はいくつか考えられますが、1つの方法としてアノテーションによる閾値設定の方法が考えられます。 以下はアノテーションの例で、アノテーション結果にはクエリと商品の関連性がある場合に正解、関連性がない場合に不正解を付与します。 クエリ 商品 類似度 アノテーション結果 きれいめワンピース 商品A 0.94 正解 きれいめワンピース 商品B 0.85 正解 きれいめワンピース 商品C 0.65 不正解 この結果を基に、適切な類似度を設定することが可能になります。 モデルの埋め込み精度の問題 以下の表はある事前学習済みモデルを用いて、検索クエリ「ダウン」と商品名のcosine類似度を算出した結果です。 商品名 類似度 ダウンコート 0.67 下駄 0.64 ダウン ティペット 0.61 「ダウン」と検索された際に、「ダウンコート」と「ダウン ティペット」が検索結果に表示されるのは適当ですが、「下駄」が表示されることは望ましくありません。この場合、類似度の閾値を0.65に設定し「ダウンコート」のみを検索結果に表示するか、閾値を下げて「下駄」を含めすべて検索結果に表示するかの判断が必要です。 モデルの埋め込みに問題がある場合、検索結果に意図しない商品が含まれてしまいます。この問題を解決するには、モデルのファインチューニングなどにより出力ベクトルを最適化する必要があります。 クエリごとの類似度のばらつき 以下の図は、いくつかのクエリに対してcosine類似度が高いZOZOTOWNの商品TOP100を示しています。 クエリごとにcosine類似度の分布にばらつきが見られます。例えば「スウェット」のクエリに対してcosine類似度の高いTop100の商品は全て類似度が0.91以上であるのに対し、「きれいめ」のクエリに対してはTop100商品すべて0.90以下になっています。 この場合、類似度の閾値の設定が難しいです。例えばcosine類似度の閾値を0.88にした場合、「きれいめ」というクエリはベクトル検索で殆ど対応できないことになります。cosine類似度の閾値をより低く設定すると、無関係な商品が検索結果に含まれる可能性が高くなります。 このように、クエリと商品によっては類似度にばらつきが発生し、類似度の閾値を一意に決めることが難しくなることに注意が必要です。この閾値を動的に変える場合は、サービス側でクエリに応じて変更する必要があります。 モデルの変化による類似度の分布の変化 利用しているモデルのパラメータを変更すると、検索クエリと商品の類似度の分布が変わります。 次の図は、 e5-small モデルと universal-sentence-encoder モデルでcosine類似度が高いZOZOTOWN商品TOP100をプロットした図です。 モデルによってcosine類似度の分布が異なります。モデルの変更に応じて類似度の閾値のパラメータを調整する必要があります。 よって、モデルの変更に応じて類似度の閾値のパラメータを調整できるようにしておく必要があります。 まとめ 本記事ではベクトル検索の導入において得た知見をご紹介しました。ベクトル検索の導入に関しては本記事で取り上げた課題があり、現状導入には至っていませんが、引き続き検討を重ねていく予定です。 おわりに ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
こんにちは。MA部の田島です。 弊社では開発ガイドラインというものを用いて、システムの品質を担保しています。今回私がテックリードを務めているということもあり、バッチアプリケーションを開発するためのガイドラインを作成しました。本記事では「開発ガイドライン」と「バッチ開発ガイドライン」を紹介します。 バッチアプリケーション開発に限定したTipsはまとまっているものが多くないため参考にしていただければと思います。 開発ガイドラインについての紹介 冒頭でも紹介した通り弊社では、開発ガイドラインというものを用いてシステムの品質を担保しています。バッチ開発ガイドラインを紹介する前に、まず開発ガイドラインを紹介します。 開発ガイドラインの種類 開発ガイドラインは現在、以下の種類が存在します。 共通 Android iOS Frontend Backend Infra API Batch DB(Database) ML(Machine Learning) 各チームはこの開発ガイドラインに沿うように、システムを構築・改修しています。また、新規システムの構築や大きめのシステムを改修するときは、リリースフローが定められており、その過程で開発ガイドラインに沿っているかのチェックが行われます。 ガイドラインの遵守ルール ガイドラインには項目ごとに以下のようなタグが付いており、タグによってどの程度ガイドラインを遵守すべきかが変わります。 タグ 遵守の必要性 MUST / MUST NOT 必須 RECOMMENDED / NOT RECOMMENDED 推奨 新規ルールが策定された際、既存システムがガイドラインに追従するのは多大な工数が生じることもあります。そのため、完全な遵守は新規システムや新規改修時にのみ適用するようにしています。ただし、既存システムにおいてもガイドラインのルールを守ることで品質の向上を図れるため、遵守することを推奨しています。 バッチ開発ガイドラインの紹介 続いては、本ブログのメインテーマであるバッチアプリケーション開発に特化したバッチ開発ガイドラインについて紹介します。 バッチ開発ガイドライン作成の背景 今回、新たにバッチアプリケーション開発のためのガイドラインを作成しました。こちらはもともと私が所属する部署である、MA部がバッチアプリケーションを大量に開発・メンテナンスしていたため部署向けに作ったものでした。しかし、内容はMA部に限定せず汎用的なものを作成したため、それを社内全体のガイドラインとすることになりました。 バッチ開発ガイドライン 以下がバッチ開発ガイドラインです。実際のガイドラインの内容をほぼそのまま掲載しました。ただし、社内向けの補足情報などが含まれているのでそれらは省略しています。その代わりに、今回各項目において「補足」という項目を追加し具体例や補足情報を追加していますので参考にしていただければと思います。クラウドサービスの利用が前提となっている項目もあるのでその点はご了承ください。 コードベース バッチの設定をアプリケーションと同じリポジトリで管理する(RECOMMENDED) バッチアプリケーションでは以下を同じリポジトリで管理するようにしてください。 項目 具体例 バッチアプリケーション バッチ処理を具体的に行うアプリケーション(Shell/Python/Java/SQLなど) バッチ設定 各バッチのスケジューリングや依存関係など 補足 このガイドラインの前提として、Backendガイドラインにおいて以下が定められています。 アプリケーションの動作に必要な「全て」のコードをGitHubで管理する(MUST) また、このガイドラインの意図は、バッチの設定と動作するアプリケーションを近くに置くことで、システム把握を容易にすることです。RECOMMENDEDにしている理由は、例えばRailsアプリケーションでバッチ専用のAPIを用意し、バッチアプリケーションはそのAPIを叩くだけといった構成にしたい需要もあると考えたからです。 基盤 依存関係があるバッチにはワークフローエンジンを利用する(RECOMMENDED) バッチ同士の依存関係が複雑な処理にはワークフローエンジンを利用してください。 補足 以下にワークフローエンジンの例を掲載します。いずれも弊社で利用実績のあるワークフローエンジンです。 ツール Apache Airflow ( Cloud Composer ) Digdag Argo Workflows Kubeflow Pipelines ( Vertex AI Pipelines ) AWS Step Functions GCP Workflows 処理ごとにコンピュートリソース(CPU/メモリ/ストレージ)を選択できる(RECOMMENDED) バッチ処理毎にコンピュートリソースを選べるようなアーキテクチャを利用してください。以下のようなワークフローエンジンでは、コンテナを利用することでコンピュートリソースを処理ごとに選択できる機構が存在します。 ツール 機構 Apache Airflow Kubernetes Executor / airflow-aws-executors Digdag ECS Command Executor / Kubernetes Command Executor Argo Workflows Core Concepts 参照 AZの障害発生時に別のAZにてバッチを実行出来る(MUST) AZの障害時に、別のAZにてバッチを実行出来るような構成にしてください。 ただし、バッチサーバーが多重起動し、重複してバッチが実行されないように注意してください(参照: 「バッチの2重起動を防ぐ」)。 例えば、ジョブキュー型のワークフローエンジンを利用すると、別AZにジョブワーカーを起動することでバッチ処理の途中から再開が可能となります(※キューもMultiAZ構成になっている必要あり)。 補足 ここでは例としてAZの単位での障害を想定していますが、要件によって「Region」「AZ」等の分離レベルを検討してください。 処理ワーカーが自動スケールされる(RECOMMENDED) バッチ処理を行うのに必要なコンピュートインスタンスが、必要に応じて自動スケールするようなアーキテクチャを利用してください。 ただし、過剰にワーカーがスケールされすぎていないか、ワーカー数に上限を設けるようにしてください。 永続化ファイルは外部ストレージに配置する(RECOMMENDED) ログなどの永続化するファイルはバッチサーバーではなく、外部ストレージに永続化してください。 ツールによってはデフォルトで外部ストレージを利用する設定があるのでそれを利用することをおすすめします。 以下がストレージの例です。 ストレージ 対応ツール S3 Apache Airflow / Digdag / Argo Workflows GCS Apache Airflow / Digdag / Argo Workflows 補足 本ガイドラインの意図としては、バッチアプリケーションが稼働するノードがリタイアしたとしてもログ自体は永続化したいということです。そのため、ストレージでなくともAWSの CloudWatch やGCPの CloudLogging などでのログの永続化も選択肢の1つとなります。 ただし、それらLoggingサービスはストレージサービスよりも比較的高価なため、Storageサービスにログを保存し、それらを容易に参照できる状態になっているのが良いと考えています。 アプリケーション設計 / SRE 自動リトライをする(MUST) 特定の処理がネットワークの一時的な問題などで失敗することがあるため、処理ごとに自動リトライを行ってください。 リトライはツール・ライブラリに任せる(RECOMMENDED) バッチごとにリトライを実装することはバグの原因になるため、ツールのリトライ機構に任せるようにしてください。 ツールのリトライ機構では足りない場合は、ライブラリを利用してください。 また前提として、バッチ処理で利用しているクライアントライブラリなどにリトライ機構が含まれている場合は適切に設定してください。 以下がツールやライブラリの例です。 ツール ツール 機構 Apache Airflow retry parameter Digdag _retry parameter Argo Workflows Retries Step Functions Retry GCP Workflows Retry steps ライブラリ 言語 ライブラリ Java failsafe など Python tenacity など Go retry-go など バッチの2重起動を防ぐ(MUST) 重複起動されてはいけないバッチ処理が多重起動されないようにしてください。 例えば、2重起動を防ぐ仕組みがあるワークフローエンジンを使う、またはバッチの先頭でLockを取ることなどで実現可能です。 処理を冪等にする(MUST) 処理はリトライを何度行っても問題ないようにしてください。 補足 冪等性を考慮する場面として、データの操作があげられます。以前のテックブログで「BigQueryでのデータ追記処理における冪等化の取り組み」を紹介しているので、そちらもご参照ください。 techblog.zozo.com 現在日時に依存する処理を入れない(NOT RECOMMENDED) 上記「処理を冪等にする」を達成するために、現在日時に依存する処理を避けてください。 代わりに処理開始時刻等で代替できないか検討してください。 またリトライ時にも冪等な処理となるように、リトライ時にリトライ前の処理開始時間を利用したり、特定の時間を外部から注入したり出来るようにしてください。 補足 実装の具体例として、ワークフローエンジンを利用している場合には、特定のワークフローの開始時間を取得できます。例えばDigdagでは以下のような「SessionTime」というものが利用できます。これを利用することで、リトライが発生した場合もリトライ前、リトライ後で同様の時間を利用した処理がされます。 docs.digdag.io ひとつひとつの処理を小さくする(RECOMMENDED) 処理が失敗した時のリトライ時の復旧時間を短くするため、ひとつひとつの処理を責任毎に分割してください。 また、各処理を責任毎に小さくすることで全体の見通しが良くなります。 補足 例えば以下のようなクエリでSELECT文に時間がかかる場合、INSERTの処理で失敗するとまた時間のかかるSELECT文からやり直す必要が出てきます。 INSERT INTO `project_id.dataset_id.destination_table` (count_result, timestamp ) SELECT COUNT (*) AS count_result, CURRENT_TIMESTAMP () AS timestamp FROM `project_id.dataset_id.source_table` WHERE last_purchase_date BETWEEN DATE_SUB( CURRENT_DATE (), INTERVAL 1 DAY) AND DATE_SUB( CURRENT_DATE (), INTERVAL 100 DAY); そこで処理を以下のように別々にすることで、2つ目の処理だけをリトライできます。ただし、この場合1つ目のSELECT結果を2つ目のクエリに渡してあげる必要があります。 SELECT COUNT (*) AS count_result, CURRENT_TIMESTAMP () AS timestamp FROM `project_id.dataset_id.source_table` WHERE last_purchase_date BETWEEN DATE_SUB( CURRENT_DATE (), INTERVAL 1 DAY) AND DATE_SUB( CURRENT_DATE (), INTERVAL 100 DAY); INSERT INTO `project_id.dataset_id.destination_table` VALUES (${count_result}, ${ timestamp }) ワーカーを増やせば処理性能が線形に伸びるように実装する(RECOMMENDED) 大量のデータ処理など、データ量が増えた場合でも処理ワーカーを増やすことで処理性能が線形に伸びるように実装してください。 例えばデータの処理を分割し、並列化することで実現ができます。その時のチャンクサイズは後から変えられるようにしてください。 補足 例えば、以下のような毎秒1000件で合計1000件を配信するようなバッチを考えます。 その後配信量が倍に増え、2000件の配信が必要になった場合以下のようにワーカーを倍にすることで処理スピードは落とさずに倍の配信ができます。ただし、ここではリクエストされる側の負荷は気にしないものとします。 早い段階でValidationを行う(RECOMMENDED) 早い段階でデータのValidationをおこない、不正なデータが確認された場合はバッチを落としてください。 長時間のデータ処理が完了した後に不正データにより処理が失敗することによる、処理遅延を防ぐことに繋がります。 処理失敗時に通知する(MUST) バッチ処理が失敗した場合SlackやPagerDutyで気付けるようにしてください。 補足 弊社ではSlackやPagerDutyを利用していますが、それぞれの利用ツールに合わせて通知先は変更してください。以下のようなツールでは通知の仕組みやプラグインが用意されています。 ツール 機構 Apache Airflow slack / pagerduty Digdag slack / その他参考 処理時間のSLAを設ける(MUST) バッチ処理時間にSLAを設け、SLAを超えた場合はSlackやPagerDutyなどで気付けるようにしてください。 SLAの設定は、「処理時間」または「特定の日時」のどちらでも問題ありません。 補足 以下のようなツールではSLAの仕組みが用意されています。 ツール 機構 Apache Airflow Timeouts Digdag sla Argo Workflows timeouts Step Functions TimeoutSeconds バッチ処理が適切に動作を開始しているかを監視する(MUST) 定期実行バッチが適切に開始されているかを監視し、開始されていない場合は気付けるようにしてください。 処理ワーカー数やワーカープロセスの監視を行ったり、プロセスやログが定期的に動いているかを監視したりすることで実現可能です。 例えば、正常にバッチ処理が開始しないケースとしては以下が考えられます。 アプリケーション自体の設定は正しいが、バッチ処理を実行するためのインスタンスが0台になっている アプリケーション自体の設定は正しく、バッチ処理を実行するためのインスタンスも起動しているが、スケジューラなどのプロセスが起動していない 依存関係を把握する(MUST) 各バッチが他の「どのような処理に依存しているのか」・「どのような処理に依存されているのか」を把握してください。 また、特定のバッチに異常があった場合に、依存関係のあるバッチを止めるなどの対応方針を事前に検討してください。 依存処理がある場合は待ち合わせ処理を実装する(MUST) バッチが他の処理に依存している場合は、時間で待ち合わせをするのではなく確実に依存処理が完了したことを確認したうえでバッチ処理を実行するようにしてください。 実行タイミングがいつでもいいものは平日の日中に実施する(RECOMMENDED) 実行タイミングがいつでもいい処理に関しては、対応者が対応しやすい時間帯である平日の日中に行ってください。 月1回など実行頻度の少ないバッチは極力避け、原則デイリー実行にする(RECOMMENDED) 月1回だけ実行されるような実行頻度の短いバッチは、デイリー実行でも問題無いようにして原則1日1回以上動かすようにしてください。 バッチを修正等してから初回起動までに時間が空かないため、問題の発見・修正を素早くできます。 ただし、毎日実行だと料金的なコストが大幅に上がるなどあればその限りではありません。 データの更新や配信等の副作用のあるバッチはDRY RUNを行う(RECOMMENDED) データの更新や配信等の副作用のあるバッチはDRY RUNを行い事前に動作が問題ないこと、処理内容が問題ないことを確認してください。 新規開発・修正をした直後のバッチ実行時には立ち会って実行ログや動作結果を確認する(MUST) バッチの修正を行った直後の初回起動時は、実行時間に立ち会ってログや動作結果を確認してください。 ガイドライン導入の効果と改善点 以上バッチ開発ガイドラインを紹介しました。今回バッチ開発ガイドラインを新規に作成したと紹介しましたが、実際にはガイドラインを作成してから2年ほどが経ちました。ガイドライン作成の結果MA部では、ガイドラインに書いてある項目について気を付けて実装ができるようになったと感じています。 特に冪等性の担保という部分においては、ガイドライン作成前に比べて注意して実装ができるようになったと感じています。そのお陰でシステムのリトライを気軽かつ安全にできるようになりました。 ただし、油断しているとガイドラインに沿っていない実装がまだまだ意図せずに入ってしまっていることがあります。ガイドラインの項目も多いので全PRですべてのチェックを1個1個行うのには時間がかかります。そのため、メンバーそれぞれがガイドラインの内容を当たり前にできるようになることが大事だなと感じています。 まとめ 本ブログでは、開発ガイドライン並びにバッチ開発ガイドラインを紹介しました。バッチ開発ガイドラインについては私達が利用しているガイドラインをほぼそのまま紹介しました。ぜひ利用して頂いて、バッチアプリケーションの品質向上に役立てていただければ幸いです。 終わりに ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに 技術評論社様より発刊されている Software Design の2024年5月号より「レガシーシステム攻略のプロセス」と題した全8回の連載が始まりました。 本連載では、ZOZOTOWNリプレイスプロジェクトについて紹介します。2017年に始まったリプレイスプロジェクトにおいて、ZOZO がどのような意図で、どのように取り組んできたのか、読者のみなさんに有益な情報をお伝えしていければと思いますので、ご期待ください。第1回目のテーマは、「ZOZOTOWNリプレイスプロジェクトの全体アーキテクチャと組織設計」です。 目次 はじめに 目次 ZOZOTOWNリプレイスの背景、目的 背景 目的 柔軟なシステム 開発生産性 技術のモダン化 採用強化 ZOZOTOWNリプレイスの歴史とアーキテクチャの変遷 アーキテクチャの変遷 2004年〜2017年:オンプレミス(リプレイス前) 2017年〜2020年:モノリスリプレイス 2020年〜:マイクロサービス化 技術スタックの選定 組織設計 プロジェクトチームの構成と変遷 マイクロサービス分割と組織 「開発」の側面 「運用」の側面 「組織」の側面 コミュニケーションと意思決定 理解を得る リリースフロー ADR(Architecture Decision Record) ロードマップ おわりに ZOZOTOWNリプレイスの背景、目的 背景 ZOZOTOWNは、2004年12月にサービスを開始して以降、基本的なアーキテクチャは変えずに成長してきました。数年に一度のペースでサイトリニューアルも行ってきましたが、UI/UXを中心としたリニューアルでした。 ZOZOTOWNのサイトデザイン変遷 右肩上がりの成長を続けていたものの、2017年当時、中長期目標として設定していた商品取扱高5,000億円にシステム全体が耐えられないことが予見され、リプレイスプロジェクトの検討が始まりました。 目的 リプレイスプロジェクトは、「ZOZOTOWNを使っていただいているお客様にいつでも快適に買い物をしていただけるサービスを提供するため」、そして、「ZOZOTOWNの成長のため」に行います。この目的を達成するために、リプレイスを通して、改善していきたいことを整理していき、目的を達成するためのポイントを大きく4つ定義しました。 柔軟なシステム リプレイス以前、オンプレミスのみで稼働していたZOZOTOWNは、新春セールなどの高負荷イベント時におけるサーバ増強には苦労してきました。イベントに合わせて事前にサーバの必要台数を見積もり、購入し、セットアップする必要がありました。また、処理コストが重いコンテンツをカットするなど、インフラだけではなくアプリケーションとしても作業が発生していました。 そこで、インフラ基盤をオンプレミスからクラウドへ移行することで、必要なときに柔軟に増強できるシステムにしたいと考えました。 開発生産性 事業展開における機能開発に加え、開発者視点でのUI/UX改善や、ソースコードのリファクタリングなどやりたいことがたくさんあります。しかし、モノリスアーキテクチャで開発されてきたZOZOTOWNでは、開発者数が増加するにつれてデプロイ作業が衝突して順番待ちが発生するなど、開発生産性に悪影響が発生し始めていました。 そこで、マイクロサービス化することで開発に関わるチームが並行で開発できるようにし、開発生産性を高めたいと考えました。併せて、IaC(Infrastructure as Code)や CI/CD(Continuous Integration / Continuous Delivery)を導入することで、デプロイ自動化による開発生産性の向上もねらいました。 技術のモダン化 ZOZOTOWNはVBScriptで動いています。開発元であるMicrosoft社もVBScriptの積極的な機能開発は現在行っておらず、このままでは稼働させる環境もなくなり、事業が停止するリスクがあります。経営リスクを回避するためにも、技術のモダン化は必須で、リプレイスプロジェクトは避けられないと考えました。 また、レガシー言語であるがゆえに、AWS、Google Cloud、DatadogといったクラウドベンダーがSDKを提供しておらず、利用を諦めるか、ライブラリを独自実装しなければならない状況でした。さらに、VBScript向けに協力会社内製のWebフレームワークを使用していて、そのフレームワークの動きを新メンバーは理解しなければならない状況でした。JavaやGo言語のようなクラウドベンダーが標準的にサポートしているプログラミング言語や、現代的なフレームワークに置き換えていくことで、そのような部分の実装コスト、学習コストを減らしたいと考えました。 採用強化 リプレイスプロジェクト、ひいては事業開発を加速するために採用強化が必要でした。リプレイスプロジェクトは、エンジニアとしての成長につながる経験ができるものだと思っています。知見をテックブログやイベント登壇で社外に向けて発信することにより、ZOZOに興味を持っていただき、採用を強化したいと考えました。 ZOZOTOWNリプレイスの歴史とアーキテクチャの変遷 アーキテクチャの変遷 2004年〜2017年:オンプレミス(リプレイス前) Webサーバ、DBサーバなどはすべてオンプレミスで運用していました。基本的な技術スタックはWindowsのIIS(Internet InformationServices)で、Classic ASP(VBScript)を動かしていました。データベースにはSQL Serverを利用していました。主なビジネスロジックはSQL Serverのストアドプロシージャで記述しており、WebサーバはHTML/JSONを生成するビューレイヤーとして利用していました。 リプレイス前のアーキテクチャ このアーキテクチャは2004年当時、Microsoft製品でシステムを構築する際のベストプラクティスとされていたものでした。ネットワークの細い当時はデータの近くで処理するストアドプロシージャのほうが高速で、ロジックを DB サーバに寄せることで DRY(Don't Repeat Yourself)に書ける利点がありました。このアーキテクチャでZOZOTOWNは売り上げを伸ばしてきたことからも、当時の技術選定として成功したと言えるでしょう。 一方で、さまざまな困りごとも出てきていました。主なビジネスロジックはストアドプロシージャとしてDBサーバで実行されるため、DBのCPUを消費してしまいます。CPUを増やすにはDBを増やす必要があり、負荷分散させづらい状況にありました。DBサーバのスケールアップでは限界が見え、DBを垂直分割し対処してきましたが、それでも限界が見えてきました。また、ストアドプロシージャのユニットテストフレームワークが なく、TDD(Test Driven Development)など昨今では当たり前とされている開発プラクティスを導入できないことも困りごとでした。 2017年〜2020年:モノリスリプレイス そこでストアドプロシージャからの脱却を目標の中心に据えて、モノリスtoモノリスアーキテクチャでのリプレイスプロジェクトが始動しました。フェーズを「参照系(商品、検索)」「準更新系(お気に入りログなど)」「更新系(カート、注文、会員)」の3つに分解し、フェーズ1として参照系機能のリプレイスプロジェクトが進行しました。 1 ビジネスロジックを処理するのに、DBサーバのCPUではなくWebサーバのCPUを使うアーキテクチャに変更できれば、Webサーバの台数を増やすことで負荷に対処できます。そこでSQL Serverの参照系ストアドを Java API化することにとにかく注力し、DBサーバの負荷を下げることが目標とされました。商品データは、オンプレSQL Serverからレプリケーションを貼って、クラウドSQL Serverに持ってきました。 リプレイスプロジェクト初期のアーキテクチャ この参照系リプレイスプロジェクトによって、ECサイトでトラフィックの大多数を占める参照系ワークロードのスケーラビリティを手に入れることができました。一方で、次のような課題も見えてきました。 モノリスアーキテクチャであることで生まれるデプロイの順番待ちが開発生産性に支障を与えていること クラウドSQL Serverのコスト(とくにライセンス費用)が高いこと 更新系ワークロードリプレイスの目処が立っていないこと ユーザートラフィックの入り口がオンプレIISのままであり、VBScript脱却の目処が立っていないこと 2020年〜:マイクロサービス化 リプレイス後の目指す姿(右)とストラングラーパターン(段階的な機能の置き換え)による移行 これを受けて新たなリーダーのもと、ZOZOTOWNリプレイスプロジェクトは再始動しました。マイクロサービス化しながらリプレイスプロジェクトを行う方針に大きく変更となり、メインで利用するDBもMySQLとするなど、技術スタックに関する変更も行われました。リプレイス後のアーキテクチャイメージと切り替え方法も示され、VBScriptからの脱却と、更新系ワークロードのリプレイス計画も立てられました。 技術スタックの選定 リプレイスプロジェクト再始動のタイミングで、技術スタックについてもあらためて選定し直しました。選定のポイントとしては「大規模開発に向いている」「人材を採用しやすい」「社内ノウハウがある」「廃れるリスクが少ない」技術を選定しました。とくに、最後の点については、リプレイスプロジェクトが長く続く可能性を考慮し、リプレイスのリプレイスが走ることを避けるために最重視したポイントでした。技術者個人としては新しいものを使いたくなりますが、邪念を排除し、組織にとって最も良い選択を行うことが肝要だと考えています。結果として、次のような技術選定になりました。 フロントエンド:JavaScript / TypeScript バックエンド:Java、Go、Python(ML向け) インフラ基盤:AWS、Google Cloud(ML向け)、Kubernetes RDBMS:MySQL(RDBMSが不得意な分野は別途検討、DynamoDB、Elasticsearchなど) バックエンドのプログラミング言語は、Javaをメインで利用しています。すでにJavaでリプレイスプロジェクトが始まっていたというのもありますが、大規模開発に向いていること、業務委託も含め人材を採用しやすいことを評価しました。Javaは一時期、機能開発が停滞していた印象を持っている方もいると思いますが、当時すでに機能開発も活発化しており、モダンな機能も取り込まれていることから、廃れるリスクも少ないと評価できたのが幸いでした。Go言語も使って良いと定めており、高速に動作し、アプリケーションの立ち上がりが早いことから、とくにトラフィック量の多いアプリケーションや、AWS Lambdaのようなサーバーレス環境で利用すると恩恵があります。機械学習の文脈では、ディープラーニングフレームワークなどライブラリの都合でPythonでAPIサーバを構築することもあります。 クラウドサービスは、AWSをメインで利用しています。人材採用しやすく、廃れるリスクが小さいのもそうですが、高いサービス品質を持ち、主要クラウドサービスの中で「最も痒かゆいところに手が届く」クラウドサービスであると評価しています。機械学習の文脈では、Google Cloudを使っても良いと定めています。データウェアハウス製品であるBigQueryや、Google独自の機械学習ワークロード向け集積回路TPU(Tensor Processing Unit)の提供を始めとして、データおよび機械学習の文脈で独自の強みを持つクラウドサービスであると評価しています。 コンテナ基盤は、Kubernetesを利用しています。2020年当時、Kubernetes以外にも活躍しているコンテナ基盤はありました。しかし、Google CloudやAzure、AWSがKubernetesのマネージドサービスを提供するようになり、Kubernetesが廃れるリスクはかなり小さいと判断できた時期でした。AWS EKS(Elastic Kubernetes Service)の提供は2018年であるため、2017年に技術選定をしていたら、違った判断になったかもしれません。 組織設計 プロジェクトチームの構成と変遷 2017年開始当初のリプレイスプロジェクトでは、フロントエンド/バックエンドはそれぞれでカートや商品詳細、検索などといった各機能を担当しやすいような複数チームで構成され、SREはすべてのインフラを管理する1つのチームで構成されていました。そして、各チームが事業案件(ZOZOTOWNの新機能追加や機能改修など)を担当しながら、リプレイスも進めるという二足のわらじ体制をとっていました。 ZOZOにはファッションやZOZOTOWNが好きなスタッフが多く、さまざまな部署からZOZOTOWNをさらに良くするためのアイデアが次々と寄せられます。それらに応えるため、どうしてもリプレイスの優先度を落とさざるを得ず、開発リソースの確保といった部分で、うまくリプレイスプロジェクトが進行していきませんでした。そこでバックエンドエンジニアの数名でリプレイス専属のチームを作り、リプレイスを進めていきました。 まずは、その専属チームが1つのリプレイス事例を作りました。その後、マイクロサービス化を拡大していく過程で、ID基盤や会員基盤・検索基盤・カート決済基盤など各マイクロサービスのチームを、構築するタイミングで作っていきました。そして、各マイクロサービスを担当するチームと、SREチームから構成される技術開発本部(現:技術本部)が設立されました。 リプレイスを行う初期段階では、事業開発を行ってきたチームが自分たちでリプレイスをやりたい、と思うのは当然だと思います。筆者たちも何度かトライしましたが、そのやり方でうまくいったことはありませんでした。リプレイス専属チームを作り、事例を作り、徐々に既存チームも巻き込み、最終的に融合するか、既存チームにシステムを移管する、という流れを取るのが有効です。 マイクロサービス分割と組織 マイクロサービスは組織論でもあります。マイクロサービス分割に関しては、ZOZOTOWNが持つ機能を大別し、それぞれの機能(マイクロサービス)を担当する専属のチームを作る想定で次のように分割設計を行いました。 認証・会員 商品(ブランド、ショップ)・お気に入り 検索 推薦 カート・決済 むやみやたらにマイクロサービスの数は増やさず、基本的には1チーム1マイクロサービスを担当するくらいが大まかな方針です。逆に言うと、マイクロサービスの規模感は1チームが専属で必要な作業量を見込めるくらいとなるように分割設計しました。将来的に1チームの担当サービスが増えて2、3個になってしまうことは想定していますが、1チームが数十個マイクロサービスを担当するような設計は避けています。 このようなマイクロサービスの分割設計は、「開発」「運用」「組織」の側面から検討を進めました。 「開発」の側面 開発の側面からは、各サービスに自律性を持たせ、開発生産性を高められる設計を意識しました。 まずは、ZOZOTOWNにおける機能とデータ、データとデータの関連性を把握することから始めました。これらの関連性を把握することで、たとえば、AとBの似たような機能をサービス分割するか一緒にするか、の意思決定がしやすくなります。ポイントとして、データの更新時に、複数のマイクロサービス間で分散トランザクションが必要にならないような境界線を見つけ、サービス分割を行うことを意識しました。うまくサービス分割できると、データに何かしらの仕様変更が入った場合でもサービス間の調整が不要になり、効率的な開発フローを回すことにつながります。マイクロサービスのメリットの1つである自律性を高めることにつながります。 「運用」の側面 システム運用の側面からは、リソース増強が必要な機能をきちんと把握し、効果的に実施できるようにする設計を意識しました。 モノリスの場合、特定のサブ機能のみをスケールさせたくても、モノリスサーバ全体をスケールさせなければなりません。マイクロサービスの場合、特定のサブ機能(≒マイクロサービス)のみをスケールさせることが可能なので、費用対効果を高めることができます。そのために、事業特性や過去実施イベント、障害記録を分析し、高負荷になる機能を整理しました。 ZOZOTOWNの場合、セールやZOZOWEEK、福袋といったイベント実施のタイミングで、とくにカート投入、購入、認証、商品検索などの機能が高負荷になる傾向があります。しかし、そういったイベント時でもそれほど負荷がかからないような機能もあります。そのような傾向を整理し、どういったマイクロサービス分割が、リソース増強などの運用全般で効率が良いかを考慮しました。 「組織」の側面 組織の側面からは、業務の専門性と、現在の開発体制からマイクロサービス化後の開発体制とのギャップを意識しました。 ZOZOTOWN の場合、カート投入・購入、商品検索、推薦といった機能が業務の専門性が高い機能に該当しますが、それらのチームで利用する技術スタック・業務知識は、ほかのチームに比べて特殊です。そのようなことを考慮し、組織分割の検討材料として取り入れました。 また、事業展開における業務量の把握も行いました。JIRAやGitHub、今後どんな開発案件があるかを示した開発ロードマップなどから、過去、現在、未来の開発案件がどのような機能の改修を必要とするものなのか、その傾向を分析しました。リプレイスが終わった後は事業開発をしていくことになります。各チームの業務量のバランスが良くなるように、ドメイン知識の関連性も意識したうえで担当マイクロサービスをアサインし、人員計画を考えました。 急にマイクロサービスと同数のチームを作るような大幅な組織変更をしてしまうと、新任リーダーの教育やチームビルディング、チーム間の連携に齟齬が生まれるなど、多くの懸念がありました。技術スタック・業務知識のキャッチアップにも時間がかかります。理想的なマイクロサービスの形、それに合わせた開発体制を目指して、技術的な練度を高めながら、現実的な組織変更を少しずつ重ねることで、組織づくりをしていきました。 コミュニケーションと意思決定 理解を得る リプレイスプロジェクトを進めるうえで、ビジネス部門、エンジニア部門(対社内/対社外)、経営層に対して、リプレイスの重要性を理解してもらうことは重要だと考えています。 ビジネス部門に対しては、社内向けの成果発表会などで、リプレイスによる事業効果(システム障害の減少、高速化、コスト削減など)を伝えることで、協力を得られやすい関係性を構築することを意識しました。 エンジニア部門に対しては、同じく社内向けの成果発表会・テックブログ・登壇での発信や、開発体験の向上、アラート件数の減少、などの成功体験を実際に感じてもらうことによって、リプレイスプロジェクトへの協力を得られやすくなりました。社外に対しては、テックブログや登壇を通じてZOZOの抱える課題や技術力を発信することで、採用強化につなげていきました。 そして経営層に対しては、リプレイスの目的、かかる費用、計画、考えられるリスクを整理して説明し、プロジェクトの承認を得ました。定期的なプロジェクト進捗報告を実施し、計画に変更があれば再度承認を得ながら進めています。進捗報告では、得られた事業効果も伝えることで、プロジェクトの優先度低下や廃止といった事態を避けることを意識していました。 リプレイスプロジェクトを計画どおり進めることは大事ですが、あくまでも事業が成功しているうえで成り立っているプロジェクトだと考えています。開発者としては、機能開発をフリーズして、リプレイスに専念するのが最もやりやすいでしょう。しかし、その間に競合他社に置いていかれ、リプレイスを実施するプロダクトとしての価値そのものを失ってしまっては元も子もありません。そのリスクを避けるために「事業を止めない」をリプレイスプロジェクトのポリシーとしました。サイト無停止でユーザーに気づかれないようにリプレイスしたり、リリース前に負荷試験を実施しボトルネックを事前に潰しておいたりと、事業に悪影響を出さないことを大前提として、技術的難易度が高くても工夫して成し遂げるように進めています。 リリースフロー ZOZOでは社内標準のリリースフローを定義しています。ZOZOにおけるリリースフローとは、案件の進捗工程(企画・設計・開発・テスト・リリース)ごとに必要な確認事項を定めたものです。リリースフローに従う必要があるかどうかはアーキテクチャの重要な変更、重要な情報の取り扱いなどの一定の条件が存在しますが、ZOZOTOWNリプレイスプロジェクトの各プロジェクトは必ずリリースフローに従うルールとしています。 リリースフローは、CTO、CISO部やテックリードを含めて企画、設計、テストの3段階でレビューMTGを行い、技術的、セキュリティ的に問題がないかを確認します。レビューMTG以外にも、全社で定められている開発ガイドラインに準拠しているかのチェックリスト確認もリリースフローには含まれています。 ADR(Architecture Decision Record) リプレイスプロジェクトでは、アーキテクチャや設計において、さまざまな意思決定が生まれます。それぞれがどのような検証、理由により方針を決定したのかADRとしてまとめています。ADRは、次の内容で記述しています。 タイトル(Title) コンテキスト(Context) 決定(Decision) ステータス(Status) 結果(Consequences) 最終的なアーキテクチャがまとめられているドキュメントはあるものの、その技術選定の理由や設計意図については残されていない、または議事録やコメントの一部として埋もれてしまっていることがよくあると思います。プロジェクトの初期から参画しているメンバーのみが理解している状態では、途中から参画するメンバーは新たな変更などの意思決定において、判断が難しくなってしまいます。また、時間経過とともに記憶もあいまいになり、初期から参画しているメンバーですらその意図を忘れてしまうこともあるでしょう。このようなことが積み重なってくると、変更に対して多くの時間がかかり、開発生産性の低下を招く可能性もあります。そのような状態を避けるためにADRを残すようにしています。 ロードマップ リプレイスプロジェクトにはいくつものサブプロジェクトがあり、各サブプロジェクトをまとめたロードマップを定義しています。しかし、当初からロードマップを定義できていたわけではありません。クラウドやマイクロサービスに対してノウハウや経験がない状態からの挑戦でしたので、まずは取りかかりやすい部分を対象にリプレイスしてみることにしました。しかし、障害を発生させてしまうなど、うまくいかないことも数多くありました。ですがそこで多くのことを学び、それらの経験をもとに次のように年度ごとにテーマを設け、少しずつ範囲を広げるようにロードマップを作成していきました。 2020年度:マイクロサービスプラットフォームの土台を作り、マイクロサービス化の事例を1、2個出していく(達成) 2021年度:さらにマイクロサービス化し、体制も強化し並列度を上げていく。うまくいくことを実証する(達成) 2022年度:終わりまでの全体設計・ロードマップをあらためて引き、さらに並列度を上げていく ロードマップを作成する際には、技術的な基盤を先に作りつつ、リプレイスの効果が大きいものを優先的に着手することを意識しました。また、事業効果(システム障害の減少、高速化、コスト削減など)も得られるように、サブプロジェクトのスコープを調整し、着実に事業成果を報告できる計画にしました。リプレイスプロジェクトは、過去に一度会社としての優先順位が下がったことがあります。それを避けるために、経営層にリプレイスプロジェクトの価値を定期的に理解してもらえるような計画を意識しました。 リプレイスロードマップは、進捗や状況次第で、四半期に一度のペースで適宜見直し、取り組んでいます。 おわりに 今回は、ZOZOTOWNリプレイスの全体感について、プロジェクト推進において意識していることやうまくいったこと、うまくいかずに軌道修正したことを盛り込んで紹介しました。今後、大規模システムのリプレイスプロジェクトに関わる方々の参考になれば幸いです。 第2回以降は、ZOZOTOWNリプレイスプロジェクトがこれまで取り組んできたサブプロジェクトを具体的にいくつかピックアップして、より詳細に紹介していく予定になっていますので、ご期待いただければと思います。 本記事は、技術本部 ECプラットフォーム部 ディレクターの高橋 智也と執行役員 兼 CTOの瀬尾 直利によって執筆されました。 本記事の初出は、 Software Design 2024年5月号 連載「レガシーシステム攻略のプロセス」の第1回「ZOZOTOWNリプレイスプロジェクトの全体アーキテクチャと組織設計」です。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com フェーズ1は2017年度中に終わらせるのが当初の目標でしたが、会社としての優先順位が下がるなどもあり、結果として3年ほど続くこととなってしまいました。 ↩
はじめに こんにちは、計測プラットフォーム開発本部iOSブロックの 中岡 です。普段はZOZOMAT/ZOZOGLASSの運用・保守や計測技術を使った新規事業の開発をしています。 目次 はじめに 目次 計測フレームワークとは Swift Package Managerへの移行の経緯 Swift Package Managerへの移行 移行作業でハマったこと まとめ 計測フレームワークとは 私たちのチームは、ZOZOMAT/ZOZOGLASSの機能を開発し、それらをライブラリとしてZOZOTOWN iOSチームに提供しています。このライブラリのことを私たちは計測フレームワークと呼んでいます。そしてこのライブラリの提供方法として今まではCocoaPodsを利用していました。元々はCarthageを利用していたのですが、Apple silicon導入に伴いCocoaPodsへ移行しています。そちらの経緯は以下の記事とMeetupのアーカイブをご参照ください。 techblog.zozo.com youtu.be speakerdeck.com Swift Package Managerへの移行の経緯 基本的にCocoaPodsに移行してからは開発体験などに問題はありませんでした。しかし、Appleから発表されたPrivacy Manifestに対応するため、依存ライブラリのいくつかを最新に更新する必要があり、そこでいくつかの問題に直面しました。以下はZOZOTOWN iOSの依存関係の一部です。 この中で以下のライブラリがPrivacy Manifestの対象 1 でした。 Lottie BoringSSL grpc-swift の0系の依存ライブラリ 1系にすることで swift-nio ベースとなり依存が消える nanopb ARCore の依存ライブラリ 2 Lottieに関しては特に問題なくCocoaPodsで最新版に更新できました。しかし、BoringSSLとnanopbはCocoaPodsで最新版にするには一筋縄ではいきませんでした。まずBoringSSLに関しては、依存しているgrpc-swiftの最新版がCocoaPodsでのサポートはされておらず、Swift Package Manager(以下SPM)のみとなっていました。 github.com nanopbに関しては、依存しているARCoreはCocoaPodsをサポートしています。しかし、CocoaPodsを使用してARCoreの最新版を導入すると、問題が発生しました。本来インストールしているOpenCVではなく、ARCoreが内部で使用しているOpenCVとZOZOGLASSがリンクし、クラッシュすることがわかりました。この問題は、SPMを利用してARCoreを導入することで解消されたことが確認されており、現在はその詳しい原因を調査中です。 これらの問題から、計測フレームワークをSPMへ移行することにしました。また合わせて、ZOZOTOWN iOSは共有の依存としてLottieやnanopb(Firebaseの依存)を持っているので、これらも同時にSPMへ移行しました。 Swift Package Managerへの移行 基本的には、podspecに記載されているdependencyやsource_fileをPackage.swiftに移行するだけです。以下の公式ドキュメントを参考に作業を進めました。 github.com developer.apple.com 移行前のpodspecの一部 Pod :: Spec .new do |spec| spec.name = " ZOZOMAT " # 省略 spec.source_files = " Sources/ZOZOMAT/**/*.swift " spec.resource_bundles = { " ZozomatResources " => [ " Sources/ZOZOMAT/Resources/**/*.xib " , # 省略 ] } spec.dependency " SwiftGRPC " , " 0.11.0 " spec.dependency " lottie-ios " , " 3.2.3 " # 省略 } end 移行後のPackage.swiftの一部 import PackageDescription let package = Package( name : "ZOZOMAT" , defaultLocalization : "ja" , platforms : [ .iOS ( "15.5.0" ) ] , products : [ .library ( name: "ZOZOMAT" , targets: [ "ZOZOMAT" ] ) , ] , dependencies : [ .package ( url: "https://github.com/grpc/grpc-swift.git" , .upToNextMajor ( from: "1.21.1" )) , .package ( url: "https://github.com/airbnb/lottie-spm" , .upToNextMajor ( from: "4.4.0" )) , // 省略 ] , targets : [ .target ( name: "ZOZOMAT" , dependencies: [ .product ( name: "Lottie" , package: "lottie-spm" ) , .product ( name: "GRPC" , package: "grpc-swift" ) , // 省略 ] , path: "Sources/ZOZOMAT" , // source_fileに対応するディレクトリを指定 resources: [ .process ( "Resources" ) ] , ) ] ) 移行作業でハマったこと そして作業を進めていく中で以下の2つのハマりポイントがありました。 Objective-Cが含まれているSwift Packageでのバンドルリソースへのアクセス方法 同じソースコードを複数のターゲットで使用できない まず1に関して、ZOZOMATの3D表示の機能はC++とObjective-Cで書かれており 3 、それらは別のライブラリとして管理されています。そしてこのライブラリはシェーダーコードなどのリソースを持っています。Objective-CからSwift Packageのバンドルにアクセスするには SWIFTPM_MODULE_BUNDLE というマクロを使用します 4 。基本的にはSwiftの Bundle.module と同じように動作します。以下のPackage.swiftの場合、リソースバンドルは SampleObjc-Package_SampleObjc-Target.bundle という名前になります。 import PackageDescription let package = Package( name : "SampleObjc-Package" , products : [ .library ( name: "SampleObjc-Target" , targets: [ "SampleObjc-Target" ]) , ] , targets : [ .target ( name: "SampleObjc-Target" , path: "Sources/SampleObjc" , resources: [ .process ( "Resources" ) ] ) , ] ) しかし、Objective-Cから SWIFTPM_MODULE_BUNDLE を使いアクセスしようとすると、以下のエラーのようなエラーが出て、クラッシュします。これは実際のバンドル名が SampleObjc-Package_SampleObjc-Target.bundle なのに、 SampleObjc_Package_SampleObjc_Target.bundle にアクセスしているために発生します。これを避けるためにObjective-CのSwift Packageでは名前に - を含めないようにする必要があります。 *** Terminating app due to uncaught exception 'SwiftPMResourcesAccessor', reason: 'unable to find bundle named SampleObjc_Package_SampleObjc_Target' 次に2の「同じソースコードを複数のターゲットで使用できない」に関してです。ZOZOMATは一部の機能をZOZOTOWN iOSとZOZOMATの計測機能のみを持つデモアプリで処理を分岐させるためにActive Compilation Conditionsを使っています。podspecに以下のようにフラグを設定しZOZOTOWNに提供しており、デモアプリの場合は pod install 時に post_install でそのフラグを書き換えていました。 ZOZOMATのpodspec # ZOZOTOWN用フラグを設定 spec.pod_target_xcconfig = { " SWIFT_ACTIVE_COMPILATION_CONDITIONS " => " ZOZOMAT_RELEASE " , } ZOZOMATのローカルにあるデモアプリ用のPodfile # デモアプリ用のフラグを設定 def set_demo_mode (pi) pi.pods_project.targets.each do |t| t.build_configurations.each do |bc| if t.name == " ZOZOMAT " bc.build_settings[ " SWIFT_ACTIVE_COMPILATION_CONDITIONS " ] = " ZOZOMAT_DEMO " end end end end post_install do |pi| set_demo_mode(pi) end これは元々Carthageを使っていた時に、デモアプリ用とZOZOTOWN用の2つのXcodeターゲットを用意し、フラグで処理を分岐させるという方法をとっていたためです。これと同じことを実現するために以下のように共通のソースコードを持つ2つのターゲットを定義し、それぞれに別のフラグを設定しようと考えましたが、SPMでは同じソースコードを複数のターゲットに含めることができませんでした。以下のようにPackage.swiftを定義しそれぞれのターゲットで swiftSetting を設定しようとするとエラーが出ます。 そのため、現在は一時的な対応としてデモアプリを動かす際は swiftSetting を書き換えて作業を行うという方法をとっています。将来的にはライブラリ内にこのような分岐を持つことは本来望ましくないので、リファクタリングをする予定です。 target 'ZOZOMATDemo' has overlapping sources: /Users/xxxxxx/SamplePackage/Sources/ZOZOMAT/Sample.swift import PackageDescription let package = Package( name : "ZOZOMAT" , products : [ .library ( name: "ZOZOMAT" , targets: [ "ZOZOMAT" ]) , .library ( name: "ZOZOMATDemo" , targets: [ "ZOZOMATDemo" ]) , ] , targets : [ .target ( name: "ZOZOMAT" , path: "Sources/ZOZOMAT" , swiftSettings: [ .define ( "ZOZOMAT_RELEASE" ) ] ) , // 以下のようにすることはできない .target( name : "ZOZOMATDemo" , path : "Sources/ZOZOMAT" , swiftSettings : [ .define ( "ZOZOMAT_DEMO" ) ] ), ] ) まとめ 本記事では、計測フレームワークをSPMへ移行する際の経緯と移行作業でハマったポイントについて紹介しました。SPMへの移行により、依存ライブラリの更新ができPrivacyManifestの対応ができましたが、移行によって新たに発生した問題もあります。 ブランチ切り替えの際に毎回Package resolveが走り時間がかかる ZOZOTOWN iOSはCocoaPodsとSPMの混在状態となっている ZOZOMATのデモアプリを動かす際に手動で swiftSetting を書き換える必要がある 今後はこれらの問題を解決し、より良い開発体験になるように改善していきたいと考えています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com https://developer.apple.com/support/third-party-SDK-requirements/ ↩ 5/1現在、最新のARCoreが依存しているnanopbはPrivacy Manifest対応されていないが将来的に対応される予定( issue ) ↩ https://techblog.zozo.com/entry/zozomat-cross-platform-3d ↩ https://developer.apple.com/videos/play/wwdc2020/10169/ ↩
こんにちは、DevRelブロックの ikkou です。2024年5月15日から17日の3日間にわたり沖縄県は那覇市で「RubyKaigi 2024」が開催されました。ZOZOは例年同様プラチナスポンサーとして協賛し、スポンサーブースを出展しました。 technote.zozo.com ZOZOとWEARとRubyKaigi エンジニアによるセッション紹介 Generating a custom SDK for your web service or Rails API Namespace, What and Why YJIT Makes Rails 1.7x Faster Using Ruby in the browser is wonderful. An adventure of Happy Eyeballs Embedding it into Ruby code Unlocking Potential of Property Based Testing with Ractor Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜 The depths of profiling Ruby rb_profile_thread_frames() TracePoint API Thread Events API It’s about time to pack Ruby and Ruby scripts in one binary スポンサーブースの紹介 RubyKaigi出張版「みんなの失敗」 失敗を “水に流せる” トイレットペーパー リニューアルしたWEARの紹介 箱猫マックスとチーコのステッカー RubyKaigi公式イベントのスタンプラリー After RubyKaigi 2024〜メドピア、ZOZO、Findy〜を開催します! おわりに ZOZOとWEARとRubyKaigi カラッと晴れた沖縄の空と会場の「那覇文化芸術劇場 なはーと」 ZOZOとRubyKaigiの関係は前身にあたるVASILY時代の RubyKaigi 2017 から始まっています。翌年の RubyKaigi 2018 では スタートトゥデイテクノロジーズとして初めて協賛 、その翌年の RubyKaigi 2019 では ZOZOテクノロジーズとしてRubyスポンサーに協賛 、 現CTOの瀬尾がスピーカーとして登壇 しています。このときは ファッションチェックアプリとRuby on Lambdaで開発したランキングサイトを展示していました 。 その後、コロナ禍を経て再開した RubyKaigi 2022 からはファッションコーディネートアプリ「WEAR」のバックエンド開発を担うチームが中心となって協賛とスポンサーブースの出展を続けています。 RubyKaigi2017参加レポート(全日分)とスライドまとめ RubyKaigi2018参加レポート RubyKaigi 2019参加レポート〜sonots登壇セッション & エンジニア8名による厳選セッション RubyKaigi 2022参加レポート 〜エンジニアによるセッション紹介〜 RubyKaigi 2023参加レポート 〜エンジニアによるセッション紹介〜 私たちが運営しているファッションコーディネートアプリ「WEAR」のバックエンドはRuby on Railsで開発しています。2013年にVBScriptで作られたシステムですが、2020年頃からVBScriptのシステムをコードフリーズし、Rubyへのリプレイスをはじめました。現在もリプレイスを進めながら、新規の機能もRubyで開発しています。 ZOZOのスポンサーブースに遊びにきてくれたMatzさん また、かねてよりMatzさんを技術顧問としてお迎えし、月次でMatz MTGと題したオンラインミーティングを実施しています。Rubyど真ん中の話から広く技術的に興味がそそられる話まで色々話せる時間として人気です。 昨年10月には10周年を迎えたWEAR ですが、RubyKaigi 2024の会期直前となる 5月9日に「WEAR by ZOZO」としてリニューアルしました 。リニューアル直後となるスポンサーブースの出展だったこともあり、会期中に不測の事態が発生しないか心配していましたが、何事もなく、ブースを訪れた方に実機で新機能を試してもらえました。 そんなZOZOとWEARとRubyKaigiの関係をお伝えしたところで、RubyKaigi 2024に参加したエンジニアがピックアップしたセッションの紹介と、スポンサーブース紹介の2軸でお送りします。ボリュームたっぷりの記事となっています! エンジニアによるセッション紹介 Generating a custom SDK for your web service or Rails API @tsuwatch です! @mullermp さんの「 Generating a custom SDK for your web service or Rails API 」が個人的にかなり良かったのでご紹介します。 本セッションはAWSで実際に使用している Smithy というIDLと実際に smithy-ruby を使いながら何ができるのかを紹介するものでした。 aws-sdk-ruby に実際に使われているそうです。 Smithyとは、プロトコルに依存しないIDLで、クライアント、サーバ、ドキュメントなどを生成するためのツールセットです。AWSが提供している数万のサービスに対してSDKを生成してきた叡智が詰まっています。 一部抜粋ですが、以下のようにサービスや、リソース、リソースに対する操作についての定義を記述すると、それを実行できるSDKが自動生成されるというものです。 $version : " 1.0 " namespace example.railsjson use smithy.ruby.protocols #railsJson use smithy.ruby.protocols #UnprocessableEntityError // / Rails High Score example from their generator docs @railsJson @title ( " High Score Sample Rails Service " ) service HighScoreService { version : " 2021-02-15 " , resources : [ HighScore ], } // / Rails default scaffold operations resource HighScore { identifiers : { id : String }, read : GetHighScore } // / Modeled attributes for a High Score structure HighScoreAttributes { // / The high score id id : String , // / The game for the high score game : String , // / The high score for the game score : Integer , // The time the high score was created at createdAt : Timestamp , // The time the high score was updated at updatedAt : Timestamp } // / Permitted params for a High Score structure HighScoreParams { // / The game for the high score @length ( min : 2 ) game : String , // / The high score for the game score : Integer } // / Get a high score @http ( method : " GET " , uri : " /high_scores/{id} " ) @readonly operation GetHighScore { input : GetHighScoreInput , output : GetHighScoreOutput } // / Input structure for GetHighScore structure GetHighScoreInput { // / The high score id @required @httpLabel id : String } // / Output structure for GetHighScore structure GetHighScoreOutput { // / The high score attributes @httpPayload highScore : HighScoreAttributes } そして以下のような呼び出しが可能になります。 require ' high_score_service ' client = HighScoreService :: Client .new( endpoint : ' http://127.0.0.1:3000 ' ) c.get_high_score( id : ‘ 1 ’) OpenAPIではダメなのかという意見もあるかと思いますが、Smithyはプロトコルに依存しないというところが明確に差分としてあります。そのため、RESTful APIはもちろん、RPC、pub/subなどを定義できます。しかも、なんとSmithyはOpenAPI、JSON Schema、Protobufに変換できます。 https://github.com/disneystreaming/smithy-translate その他にもかなりの拡張性があり、 Paginators , Waiters , Interceptors などさまざまな拡張性があります。 AWSほど巨大なサービスを社内もしくは社外に提供している事例はそれほどないでしょうが、OpenAPIよりもプログラマブルで拡張性があり、柔軟に使用できるので検討の余地が大いにあるのではないでしょうか。場合によっては自分たち開発者側だけの利用だけでも役に立つかもしれません。 セッションを聞いていると、自動生成されるデモを実演するたびにcoolと気持ちよさそうにしていたので、その気持ちがすごくわかりますと思いながら聞いていました。 その他の参考資料 https://github.com/smithy-lang/awesome-smithy https://aws.amazon.com/jp/blogs/news/developer-preview-smithy-code-generated-ruby-sdk/ Namespace, What and Why 伊藤です。RubyKaigiには今年で2回目の参加でしたが、今年も内容が幅広く、興味深いセッションばかりでした! 私からは @tagomoris さんの「 Namespace, What and Why 」をご紹介します。 speakerdeck.com このセッションでは、Namespaceの概要や目的、デモ、そして将来の展望までをお話しされており、非常に興味深い内容でした。 Rubyにはグローバルな名前空間しか存在しません。Namespaceは、Rubyに仮想的なトップレベル名前空間を導入し、ライブラリなどをグローバル名前空間から独立した形でrequire/loadできるようにする仕組みです。 Namespaceの導入の目的は、ライブラリなどの名前、定義、バージョンの衝突を回避することです。 名前の衝突 通常、Rubyでは階層化された名前を使う( Foo::Bar::Baz ) 既に他の誰かにFooを使われていたら、新たにFooを複製できない 定義の衝突 Module/Classの定義を変えると、全アプリケーションに影響が出る(定数やグローバル変数などをアプリケーション毎に変えられない) バージョンの衝突 アプリケーションによってライブラリなどの依存バージョンが異なる(ライブラリをアップデートすると、意図しない箇所に影響を及ぼす可能性がある) Namespace on readでは、Namespace毎にrequire/loadするライブラリなどを明示的に指定し、それぞれ独立した形で読み込むことを実現しています。裏側でNamespace毎にライブラリなどを複製し、トップレベル名前空間を仮想的に分けることで実現しているようです。 Rubyの Playground でNamespace on readを試せたので、デモを紹介します! 例えば、以下の2つのモジュールがあったとします。 #--- ./module1.rb module Hoge def self . foo puts ' module1 ' end end #--- ./module2.rb module Hoge def self . foo puts ' module2 ' end end 上記2つのモジュールは、モジュール名が衝突しているため、両方のモジュールを使おうとすると、以下のようにどちらか一方に上書きされてしまい、同時には使用できません。 require ( ' ./module1.rb ' ) require ( ' ./module2.rb ' ) Hoge .foo #=> module2 ところが2つのNamespaceを用意し、それぞれにどのモジュールを読み込むかを明示的に指定することにより、トップレベル名前空間を仮想的に分けられ、両方のモジュールを使用できるようになります。 namespace1 = Namespace .new namespace1.require( ' ./module1.rb ' ) namespace2 = Namespace .new namespace2.require( ' ./module2.rb ' ) namespace1:: Hoge .foo #=> module1 namespace2:: Hoge .foo #=> module2 Namespaceを用いると、namespace1とnamespace2の2つにトップレベル名前空間を分離させ、見事に名前空間の衝突を回避できていますね! 将来的に、例えばBundlerにNamespaceが組み込まれたら、gemのバージョンの衝突が自動的に回避されるようになるかもしれません。つまり、ライブラリの依存関係の地獄から抜け出せるということです! Namespaceはまだ絶賛開発中とのことなので、リリースを心待ちにしております! YJIT Makes Rails 1.7x Faster 小山 です。私からは @k0kubun さんの「 YJIT Makes Rails 1.7x faster 」をご紹介します。 speakerdeck.com Ruby 3.3 YJITの高速化に寄与した対応の詳細とRuby 3.4 YJITによってRailsアプリケーションが1.8倍高速化されたということが主題のセッションでした。タイトルは1.7xとなっていますが、発表当日までの間に1.8倍に高速化されたとのことで、冒頭で訂正されていました。 YJITはCRubyのJITコンパイラで、30回以上呼び出されるメソッドの高速化が行われます。 Shopifyで最もトラフィックが多いShopify StoreFrontで、Ruby 3.3 YJITとインタプリタを比較した場合、Ruby 3.3では平均17%高速化されたとのことです。 YJITはデフォルトではオフになっているので有効化するには以下のいずれかの設定が必要です。 コマンドライン引数 --yjit で有効化 環境変数 RUBY_YJIT_ENABLE=1 で有効化 Rubyコード内の RubyVM::YJIT.enable で有効化 Rails 7.2でRuby 3.3以降を使用している場合、デフォルトのイニシャライザ内で RubyVM::YJIT.enable が呼び出され有効化されるようになるとのことで、YJITが標準となります。 YJITのメモリ使用率を最小化するには以下の方法があります。 --yjit-exec-mem-size で生成するコード量をコントロールする Ruby 3.31でのデフォルトは48MiB --yjit-exec-mem-size の3〜4倍のメモリを使用する 2〜3倍分はメタデータに対して使われる Ruby 3.3のYJIT最適化で最もインパクトのあった以下の対応によって、Railsbenchが7%高速化されたとのことです。 サポートされていない呼び出し方や分岐の数の多い呼出しでのインタプリタへのフォールバックが行なわれなくした 例外ハンドラのコンパイルを行った スタック操作でレジスタが使われるようにした Ruby 3.4ではスタック値のようなローカル変数が最適化されて、 Kernel#binding が呼び出されないと推測している 数値, true, false, nil., :symbolなど単純な値を返すメソッドのインライン化 Ruby 3.4ではself, local variablesを返すメソッドにも対応 多くのCメソッドのインライン化 Ruby 3.4では jit_prepare_lazy_frame_call によって遅延フレーム呼び出しが可能になる YJITではプロダクションのアプリケーションで10〜20%ほど高速化されるためYJITを有効化しましょう。より多くのコードがJITコンパイルされ、レジスタ割り当てがされ、メソッドがインライン化されるためRuby 3.3にアップグレードしましょう。と締められていました。 RubyバージョンのアップグレードおよびYJIT有効化による恩恵がとても大きいことを話されており、私たちのアプリケーションにも早く導入したいと思いました! Using Ruby in the browser is wonderful. chika です。私からは @ledsun さんの「 Using Ruby in the browser is wonderful. 」をご紹介します。 speakerdeck.com このセッションは、 ruby.wasm を使用する上で不足している機能を実装し、 ruby.wasm に追加したということと、 ruby.wasm を使用したRuby製のフロントエンドフレームワークの紹介でした。 JavaScriptではできるがRuby( ruby.wasm )ではできないこととして、以下があります。 new演算子 new演算子は、JavaScriptでは new.Foo() と記述する演算子ですが、Rubyでは Foo.new というメソッド呼び出しとなる プロパティ呼び出し JavaScriptにおけるプロパティ呼び出しも同様に、Rubyではメソッド呼び出しとなる 外部リソースにアクセスする JavaScriptではローカルファイルへのアクセスや、OSの機能を呼び出せるが、Rubyではできない ruby.wasm がCRubyとJavaScriptの架け橋となり、 JS.global や JS.eval 、 JS::Object によってJavaScriptのAPIを呼び出すことで可能となっているが、JSとRubyのコードが入り混じってしまい、分かりにくくなってしまう 現状のRubyや ruby.wasm できない部分や分かりにくいところを改善するために、以下2つの改善を ruby.wasm 本体に向けてPRを作成したとのことでした。 1つ目は、JavaScriptのオブジェクトを、newメソッドから作れる修正になります。 元の ruby.wasm ではnew演算子が存在しないため、 JS.eval によってJavaScriptのnew演算子を呼び出す必要がありました。 JS .eval ' return new URLSearchParams(location.search) ' それが、今回のPRによって、以下のようにnewメソッドにて記述することが可能となります。 JS .global[ :URLSearchParams ].new( JS .global[ :location ][ :search ]) 実際のPRはこちら: https://github.com/ruby/ruby.wasm/pull/246 2つ目は、JavaScriptの JS::RequireRemote を require_relative#load に互換する修正です。 機能としては、Rubyの require_relative と互換性があり、 拡張子なしの機能名で指定可能 相対パスでの解決 二重ローディングの防止 があります。 言語本体のファイルに何も手を入れる必要がなく、ブラウザとターミナルでcompartibleになっているそうです。 この修正によって、JavaScriptでの以下の構文が、 < script type = "text/ruby" src = "lib_a" ></ script > < script type = "text/ruby" src = "lib_b" ></ script > < script type = "text/ruby" src = "main" ></ script > ruby.wasm にて以下のように記述することが可能となります。 require_relative ' lib_a ' require_relative ' lib_b ' 実際のPRはこちら: https://github.com/ruby/ruby.wasm/pull/292 次に作成しているものとして、Ruby製のフロントエンドフレームワークを紹介していました。 Ruby製のフロントエンドフレームワークはおそらく現在1つもなく、史上初のフレームワークかもしれないとのことで、とても面白い取り組みでした。 現在、 ruby.wasm を使ったRuby製のフロントエンドフレームワーク「Orbital Ring」というものを作成していて、現在の機能は以下になります。 AutoLoader Railsのclassic autoloaderを参考に、require_relativeが必要なくなるよう実装 Rendering ブラウザで動作するので、HTMLをレンダリングして書き換えたいため、画面を描写するメソッドを追加し、erbに渡すよう実装 Event Binding Railsのroutingのようなものを実装 現在120行ほどのコード量とのことでした(少ない!) また、実際にフレームワークを使用して作成したアプリケーションを公開していて、実際に以下のリンクからアクセスして動かすことが可能となっていました。 https://ledsun.github.io/kakikata/ セッションの紹介は以上になります。 ruby.wasm が登場してから2年ほど経ちましたが、毎年 ruby.wasm に関するセッションがいくつもあり、どんどん進化していっているなという印象で、追うのが楽しくなっています! 来年もまたどう進化するのか楽しみです。 An adventure of Happy Eyeballs 春日 です。私からは @coe401_ さんの「 An adventure of Happy Eyeballs 」をご紹介します。 speakerdeck.com このセッションでは、Happy Eyeballs Version 2という RFC 8305 で定義された接続アルゴリズムを用いてsocketライブラリを改修した際の考慮事項や実装方法の説明、実際にデモを行い従来の課題を解決できたことをお話しされていました。 従来のRubyのsocketライブラリの実装としては、次のようになっています。 DNSサーバに対し、接続したいドメイン名を問い合わせ、対象のIPアドレスを取得 解決されたIPアドレスに対し、接続を試行 接続が確立したらsocketオブジェクトを返す しかし、従来の実装方法では次のような課題がありました。 DNSサーバへの対象サーバのIPv4とIPv6の問い合わせは順番に行われるため、先に問い合わせた方の名前解決に時間がかかる場合、後に問い合わせる方がすぐにIPアドレスを返せるとしても、先に問い合わせた方の回答を待機する必要がある IPv4, IPv6の名前解決が両方とも成功した際、それぞれへの接続も順番に行われるため、先に接続試行した方が長時間接続の確立ができない場合、次に試行するアドレスがすぐに接続可能だとしても、先に接続試行した方が失敗するまで次の接続を待機する必要がある このような課題に対処するため、RFC 8305でHappy Eyeballs Version 2(HEv2)というアルゴリズムが定義されています。 HEv2のアルゴリズムは次の通りです。 DNSサーバへのクエリを非同期で行う 解決されたIPアドレスをソートする 接続試行を250msごとに非同期で行う どれか1つの接続が確立したら他の接続はキャンセルする 解決されたIPアドレスリストやソケットの状態によって次にすべき処理が異なることから、これらの処理をステートマシンで表せることに着目しました。この状態遷移図は2020年のRubyアソシエーション開発助成金ですでに作成されていたようです。( https://www.ruby.or.jp/grant/2020/matsushita_mentor_report.pdf ) この状態遷移図を参考に、コードを実装します。この実装では、 Kernel#loop とcase文が用いられています。今の状態をstateオブジェクトにもち、その値によって異なる処理を行います。 接続中のソケットは書き込み待ちのIOオブジェクトとして IO.select の引数に渡されます。そのいずれかで接続が確立されるか失敗すると IO.select は待機を終了し、そのソケットの配列を書き込み可能なIOオブジェクトとして返却します。 また、名前解決の待機のためには IO.pipe が用いられています。スレッド内で名前解決が完了すると書き込み用のIOオブジェクトに書き込まれ、 IO.select の引数に渡されている読み込み用のIOオブジェクトで読み込み可能になります。その結果、 IO.select は待機を終了し、読み込み用IOオブジェクトを返します。 IO.select の引数にこれらのオブジェクトを同時に渡すことで、1つの式で接続の完了と名前解決の両方を待機可能にしています。 各状態の詳細な処理内容は、セッションスライドをご覧ください。ここでは紹介しなかった、実装中に発生したRFC 8305では定義されていない部分の解決方法なども説明されています。 デモでは、IPv6の接続に長時間かかる場合、従来の接続方法だと接続ができていないところを、HEv2を用いた Socket.tcp で接続した場合は即座に接続ができたことを見せていただきました。 HEv2を用いた Socket.tcp はRuby 3.4に含まれるようです。また、Cで実装された TCPSocket.new のHEv2対応も現在取り組み中とのことです。 セッションの紹介は以上になります。 RFCで定義された接続方法を実装に落とし込むところはかなり興味深く、非常に勉強になりました。このアップデートにより、接続時のタイムアウトがかなり減るのではないかと期待できますね。リリースが楽しみです。 Embedding it into Ruby code 高久です。私からは @soutaro さんの「 Embedding it into Ruby code 」をご紹介します! speakerdeck.com 自社プロダクトへのRBS導入が現実的に考えられそうな内容で印象に残っているため選びました。 このセッションではRBSの型宣言を直接Rubyファイル書けるようにする rbs-inline を紹介されていました。 従来のRBSは型宣言したいRubyコードとは別に、RBSファイルに型情報を定義する必要があります。しかし1つの処理に対して2つのファイルに記述することは、開発やメンテ、プルリクレビューのしづらさが課題としてありました。 そこでRubyのコードに直接型情報を書ける rbs-inline の開発を現在進めているとのことでした。 rbs-inlineでは具体的に以下のようにコメント形式で型情報を記述します。 # rbs_inline: enabled class Person attr_reader :name #:: String attr_reader :addresses #:: Array[String] # @rbs name: String # @rbs addresses: Array[String] # @rbs returns void def initialize ( name :, addresses :) @name = name @addresses = addresses end def to_s #:: String " Person(name = #{ name } , addresses = #{ addresses.join( " , " ) } ) " end end YARDに少し似ていますね。YARDの構造に似せて作っているとのことです。しかしYARDタグは構文や型の構文がRBSとは異なるため、別タグとして記載する必要があります。 rbs-inlineはまだ検証段階で最適な構文等を模索中とのことでした。 私自身もWEARにRBS導入を検討したことがあるのですが、まさに@soutaroさんがおっしゃっていたような課題感からまだ導入に踏み切れていない状態でした。特にRubyコード修正した時に、RBSコードも一緒にメンテする必要があるのは気持ち的に少し大変で、メンテが漏れ始めてくると段々誰もメンテしないファイルになる可能性が高いと思っていました。レビューで毎回RBSファイルの修正有無をレビュアーが確認するのも大変です。 それが現状YARDのメンテやレビューが個人的にはなんの苦でもないことを考えると、同じコードに直接書くことで正しくメンテされた状態を維持できる可能性がかなり高くなると感じました。 YARDとどのように互換性を持たせるのかも気になるところで、今後の開発進捗が楽しみです! Unlocking Potential of Property Based Testing with Ractor 三浦 です。私からは @ohbarye さんの「 Unlocking Potential of Property Based Testing with Ractor 」をご紹介します! speakerdeck.com このセッションはohbaryeさん自身が作成したProperty Based TestingをRubyで実施するためのGemの話と、Property Based Testingのデメリットである実行時間を短縮するために更にRactorを掛け合わせて実行を試みた話の2段階構成となっていました。 Property Based Testingは名前だけは聞いたことがあり、実際に使ったことはなかったので、勉強したいと思いこのセッションに参加しました。 私たちがよく利用しているユニットテストは、開発者が入力値と期待する出力を明示し、コードがその通りに動作することを確認するテストです。この手法は「事例ベーステスト(Example Based Testing)」と呼ばれています。 メリット 入力値と期待値が決まっているのでどのようなテストをしたいのかが理解しやすい Property Based Testingに比べると実行時間が早い デメリット コードが冗長になる テストケースの粒度が開発者依存となるため、想定していない入力値によるバグが起こり得る 一方、今回のセッションで取り上げられている「プロパティベーステスト(Property Based Testing)」は、開発者はコードが満たすべき特性や条件を明示した「プロパティ」と呼ばれるものを定義し、ランダムに生成された入力値に対してそれが一貫して成り立つかを確認するテスト手法です。 メリット コード量が少なくて済む 開発者が想定していない入力値によるバグもカバーできる デメリット パターン数やShrinkの実行回数によっては処理時間が長くなる 上記のメリットとデメリットを踏まえて、対象コードの品質をどこまで担保したいかを考え、それに応じて使い分けるのが良さそうだと思いました。 Property Based Testingは、 PBT というGemで実装が可能です。 下記はセッションでも紹介されていた具体例となります。 context ' verify biggest method using property based testing ' do it ' return biggest number in array ' do # Runnerメソッド Pbt .assert do # テストに使用したい入力値の条件を記載(ex.ランダムな整数が入った配列) Pbt .property( Pbt .array( Pbt .integer)) do |numbers| # テスト対象のメソッドを実行 result = biggest(numbers) # 実行結果が仕様通りになっているか検証( ex.配列で一番大きな数値が返ってくる) expect(result).to eq numbers.sort.last end end end end 具体的な入力値は明示せず、 Pbt.property の引数にテストで利用したい値の条件を指定します。実行すると、条件に合わせた入力値がランダムに生成され、テストが実行されます。 デフォルトでは100パターンのテストが実行されます。失敗するケースが検出されると、同じ現象が再現する最小の入力値を探すためのshrinkが行われます。shrinkの実行履歴は Pbt.assert の引数に verbose: true を設定することで確認できます。 ケース漏れの防止や原因の特定は容易になるものの、テストの総実行回数が多いため、時間がかかってしまいます。 その対策として、Ractorでテストを並列実行させ処理実行が早くなるかを検証されていました。いくつかのテストに対してRactor、Process、Thread、シーケンシャルそれぞれ実行した際のパフォーマンスを比較するというものでした。CPUバウンドなテストに対してRactorは有効でしたが、基本的にはシーケンシャルに実行する方が早いという結果でした。 Property Based Testingとは何かから丁寧に説明されており非常に分かりやすいセッションでした。WEARでも入力パターンの多いコードがいくつか存在するので試してみたいと思いました。 Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜 小島です。私からは @kokuyouwind さんの「 Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜 」をご紹介します! speakerdeck.com このセッションでは、プロジェクト全体のRBSをLLMによって生成しようという試みが話されていました。RubyコードからRBSを自動生成する手法は既存でも存在するのですが、それぞれ一長一短があるため完璧なRBSを生成するのは現状では難しく、RBSを生成してから手動で修正することが必要になることが多くありました。そのため、既存手法とLLMを合わせて利用することで手動での修正を必要としないで理想的なRBSを出力させようという取り組みです。既存の手法では型を定義しきれず手動で修正しなくてはならないところ、LLMを利用して修正しているのですね! 今回の発表ではこれらを実現するRBS Gooseというツールを開発したと発表されていました。このRBS Gooseはまだ開発段階であり、実用できるレベルには至っていないとおっしゃっていました。 現段階でのRBS GooseからのRBS生成精度も発表で話されており、RubyコードからRBSを生成する既存の手法に関してはどの手法を使用してもRBS Gooseの出力精度に大差はないが、LLMのモデルの違いによっては大きく精度が変わるようでした。一番理想的なRBSを生成したモデルはGPT-4 Omniだったそうです。そのため、 rbs prototype rb + GPT-4 Omniの組み合わせが良さそうと発表では話されていました。RBS生成の精度は、今回検証に利用したコードであればGPT-4 Omniを利用した場合でPerfect(意図した型定義がされている)とのことでした。すごいですね! また、RBS Gooseから生成されたRBSでuntypedのままになっているところに関して、LLMがなぜuntypedのまま残したかをコメントしているものがあったそうです。これはとても興味深い結果だと感じました。 しかし不十分な点もあり、特殊ケースのRBSを扱えなかったり rbs_rails や typeprof などはトップレベルにRBSを生成するので対応が取れないといった課題点を話されていました。 LLMは現在とても注目されていると共に急速に進歩している分野です。RBS GooseはLLMが進歩すればするほど精度が良くなっていくと思われますのでこれからが楽しみですね! The depths of profiling Ruby 笹沢( @sasamuku )です。私からはスマートバンク社の @osyoyu さんの「 The depthes of profiling Ruby 」をご紹介します。 speakerdeck.com 本セッションでは、osyoyuさんが開発されたプロファイラ「 Pf2 」の解説がなされました。 Pf2の特徴として強調されていた点は下記になります。 マルチスレッドプログラムの解析 C拡張の呼び出しもトレース可能 複数の可視化モードが利用可能 個人的に興味深かったのはプロファイリングを実現する機能群です。それぞれ掘り下げて見てみましょう。 rb_profile_thread_frames() Pf2でスタックトレースを取得する際に使用されるAPIです。このAPIはosyoyuさん自身によって実装され、Ruby 3.3でリリースされました。 https://github.com/ruby/ruby/pull/7784 https://product.st.inc/entry/2023/12/25/160504 従来から実装されている rb_profile_frames() はカレントスレッド(GVLをロックしているスレッド)の情報しか取得できないという課題がありました。このAPIの追加により、マルチスレッドプログラムで任意のスレッドの情報を取得できるようになりました。これによりWebアプリケーションのようなIOの多いソフトウェアにおいても正しく全体をプロファイルできます。 TracePoint API Pf2でGCイベントの受信に使用されるAPIです。実行されるイベント種別を指定し、イベント発生時に任意のコードを実行できます。イベント種別には、クラス定義、メソッド呼び出し、C拡張のメソッド呼び出しなどがあります。 https://docs.ruby-lang.org/ja/latest/class/TracePoint.html TracePoint APIは任意のイベントを取得できるため、コードリーディングやバグを調査する際に使えそうです。簡単な例を下記に示します。発表でも登場した Hash#[] はC拡張として実装されていますが c_call イベントを指定することで呼び出しを検知できます。 trace = TracePoint .new( :c_call ) do |tp| p tp end trace.enable hoge = { a : 1 , b : 2 } hoge[ :a ] #=> #<TracePoint:c_call `[]' sample.rb:8> Thread Events API Pf2でGVLの状態取得に使用されるAPIです。同じ名前のAPIのドキュメントは見つけられませんでしたが、調べてみると rb_internal_thread_add_event_hook というAPIが近い機能を提供していました。 プロと読み解く Ruby 3.2 NEWS で次のように紹介されています。 スレッドが停止したり実行可能になったり実際に実行再開したりするときに内部的なイベントを発行して、それをトラップすることでスレッドの挙動を計測できるようになりました。 RubyKaigi 2023の「 Understanding the ruby global vm lock by observing it 」ではこのAPIを使ったツールの発表がDatadog社のivo anjoさんからありました。 本セッションに参加したことでPf2の特徴を知るだけでなくプロファイラで使用されているAPIに関心を持つことができました。まだ概要レベルしか理解できていないので、今後は実装を読んだり(Cワカラナイ)、プロファイラの動向にも目を光らせたりしていこうと思います! お昼ご飯にosyoyuさん激推しの「ポーたまおにぎり」を食べました! osyoyuさんが激推ししていたポーたまおにぎりも美味しかったです! 海ぶどうを挟むとさらに最高でした。 It’s about time to pack Ruby and Ruby scripts in one binary 山岡( @ymktmk )です。私からはSTORES社の @ahogappa さんの「It’s about time to pack Ruby and Ruby scripts in one binary」をご紹介します! speakerdeck.com 本セッションでは、ahogappaさんが開発されたRubyスクリプトとGemからシングルバイナリにコンパイルできる「 Kompo 」というツールが紹介されました。 開発者のahogappaさんは、Rubyでゲームエンジンを開発しているため、ゲームを配布する際にはバイナリの生成が必要でした。インタプリタ言語であるRubyは、Rubyがインストールされていない環境においても、バイナリを配布することで各環境に左右されることなくスクリプトを実行できるようになります。 これまでにも、Rubyをバイナリにコンパイルできるツールは存在していましたが、様々な問題がありました。 Ruby本体にパッチを当てている Ruby 3.0以上をサポートしていない Windows OS上でしか動作しない 同等の既存ツールには、これらの問題があり、それらを解決するために Kompo というツールが作成されました。ちなみにcompose、component、compositeなどの単語から由来しているそうです。 kompoの特徴としては、以下の通りです。 モンキーパッチのみで、Ruby本体にパッチを当てない 一時ファイルへの書き込みがない Gemfileをサポートしている 個人的に、 Kernel#require 、 require_relative 、 load などの内部実装にモンキーパッチを当て、Rubyスクリプトの解釈やC拡張の読み込みこんで、シングルバイナリを生成する手法は非常に興味深いと感じました。将来的にはクロスコンパイルやバイナリファイルの圧縮などにも対応していく予定だそうです。Rubyistにとって、ランタイムの依存なしにスクリプトを実行できることは、CLIなどのワンライナーなプログラムを運用する際に非常に有用です。さまざまな課題があるかもしれませんが、今後の発展に期待です! スポンサーブースの紹介 RubyKaigi 2024の会場となった「那覇文化芸術劇場 なはーと」のエントランスを進むと「ハイサイ」と迎えてもらえました! 前述の通りRubyKaigiの会期直前にWEARのリニューアルを控えていたため、スポンサーブースはDevRelブロックが中心となって準備しました。今回は陸路での移動ではなく空路での移動だったため、スポンサーブースの設営に必要な物品のヌケモレがないよう、いつも以上に慎重に準備を進めました。 また、事前に荷物の遅延が発表されていたため、印刷所から会場直送のパネルや事前に送った備品が届かなかった場合に備えて、会場近隣の印刷所と100円ショップを調べておきました。幸いなことに遅延はありませんでしたが、結果的に100円ショップを調べておいたことは役立ちました。 ZOZOのスポンサーブース全景 ZOZOのスポンサーブースでは社内企画「みんなの失敗」のRubyKaigi出張版として、日替わりでRubyKaigi参加者の “失敗” を集め、それを集合知の教訓として、ノベルティのトイレットペーパーと一緒に “水に流して” もらいました。また、リニューアルしたばかりのWEARを紹介し、iPhone実機で「ファッションジャンル診断」や「WEARお試しメイク」を触ってもらいました。 RubyKaigi出張版「みんなの失敗」 RubyKaigi出張版「みんなの失敗」は「付箋に自身の失敗を手書きしてもらう」というやや参加コストの高い企画でした。しかし、実際にはとても多くの「失敗」が集まり「あるある」や「わかる」といった声をたくさん聞きました。 Day 1では、水に流したい “開発の失敗” を挙げてもらいました。 Day 1で集めていた水に流したい “開発の失敗” の結果 RubyKaigiは国際会議という位置づけのため、日本語話者だけでなく英語話者もたくさん参加しています。掲示していたパネル類はあらかじめ日本語と英語を併記していましたが、その場で書いてもらう付箋はそういうわけにはいきません。そこで都度ChatGPTで翻訳し、ピンクの付箋にtranslateとして英語訳した “失敗” を記載しました。途中から追いつかなくなってしまいましたが、翻訳の効果もあってか英語話者からの “失敗” を集められたのは目論見通りでした。 Day 2では、水に流したい “技術的負債” を挙げてもらいました。 Day 2で集めていた水に流したい “技術的負債” の結果 このDay 2はスポンサーブースを巡るスタンプラリーが始まったこともあってか、非常に多くの “技術的負債” が集まりました。そのため、急きょ近隣の100円ショップに走り、部材を購入してパネルを拡張しました。 “技術的負債” として「スロークエリ」を挙げた方が一定数いたのは印象的でした。その特性上、“技術的負債” はなかなか “水に流しにくい” かもしれませんが、いつかは解消したいですね、といった会話もありました。 Day 3では、国際会議らしく水に流したい “i18n対応” を挙げてもらいました。 Day 3で集めていた水に流したい “i18n対応” の結果 Day 1、Day 2に比べると挙げにくいテーマかと思いましたが、それでも多くの “i18n対応” が集まりました。 失敗を “水に流せる” トイレットペーパー “ZOZO” の文字列とZOZOのコーポレートロゴが隠されている「失敗を “水に流せる” トイレットペーパー」 “失敗” を投稿していただいた方には、その失敗を “水に流せるように” トイレットペーパーをお配りしました。これは社内企画版の「みんなの失敗」と同様ですが、社内企画版は包み紙がありません。ノベルティとしてお渡ししているトイレットペーパーはZOZOスタッフも持っていない特別版です。その場でご説明した方もいますが、実は “ZOZO” の文字列とZOZOのコーポレートロゴが隠されています。 RubyKaigi 2022の “最後まで身につけているファッションアイテム” としての温泉タオル、RubyKaigi 2023の “一合一会” 米に続き、遊び心を忘れないデザイナーチームによる特別なアイテムでした。 リニューアルしたWEARの紹介 デスク右側に設置したモニターでリニューアルしたWEARを紹介 RubyKaigi出張版「みんなの失敗」のパネルがテーブルの2/3ほどを専有してしまいましたが、残ったスペースにモニターを設置し、リニューアルしたWEARの紹介をループ再生していました。 LEDテープでモニターを “デコってある” 姿は技術カンファレンスに似つかわしくないかもしれません。しかし、これは “映え” 目的のものではなく、想定よりも割り当てられたスポンサーブースが暗かったため、少しでも明るくするため準備日のDay 0に急きょ購入して設置したものです。 AIを活用してファッションの「好みのジャンル傾向」がわかるファッションジャンル診断 ユーザーが投稿したフルメイクデータをARで試せる「WEARお試しメイク」 また、自由に触れるiPhoneを設置し、AIを活用してファッションの「好みのジャンル傾向」がわかるファッションジャンル診断や、ユーザーが投稿したフルメイクデータをARで試せる「WEARお試しメイク」などを体験してもらいました。 ZOZOTOWNのZOZOCOSMEに導入されているARメイク機能は、リップやアイブロウなど、部位ごとのパーツを試せますが「WEARお試しメイク」はフルメイクを試せるのが大きな特長です。メイク関連アイテムとして、手鏡も配っていました。 箱猫マックスとチーコのステッカー 箱猫マックスとチーコのステッカー Take Freeのアイテムとして箱猫マックス(Box-Cat Max)とチーコのステッカーを配布していました。箱猫マックスはZOZOTOWNのキャラクターで、チーコはWEARのキャラクターです。 チーコのステッカーは「かわいい」と言ってくれる方が多かったのはとても嬉しいことです。そしてLINEスタンプとして販売している、エンジニアの生態をリアルに再現した「 箱猫マックス Vol.6 」の中から選出したステッカーはLGTMやDONEが人気でした! RubyKaigi公式イベントのスタンプラリー RubyKaigi公式イベントのスタンプラリー RubyKaigiでは例年、公式イベントとしてスポンサーブースを巡るスタンプラリーが開催されています。このスタンプラリーは参加者とスポンサーブースのZOZOスタッフが会話する良いきっかけにもなっています。参加した皆さんは最後まで集まりましたか? After RubyKaigi 2024〜メドピア、ZOZO、Findy〜を開催します! 5月28日にAfter RubyKaigi 2024〜メドピア、ZOZO、Findy〜を開催します! RubyKaigi 2024の興奮さめやらぬ5月28日に、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社で非公式アフターイベントのAfter RubyKaigi 2024を開催します! RubyKaigi 2024に参加した方も、参加できなかった方も、ぜひお気軽にご参加ください! findy.connpass.com おわりに 技術カンファレンスでは恒例のスポンサーパネルへのサイン! ZOZOでは、各種エンジニアを採用中です。ご興味のある方は以下のリンクからご応募ください。 corp.zozo.com 恒例となっている次回の開催日と開催地の発表 改めてRubyKaigi運営の皆さん、参加者の皆さん、おつかれさまでした。また来年は松山でお会いしましょう! 現場からは以上です!
はじめに こんにちは、ZOZOTOWN開発本部アプリバックエンドブロックの髙井です。 私達のチームでは、レガシーとなっているZOZOTOWNアプリ用API(以下、レガシーAPIと呼ぶ)のリプレイスに2023年から着手しています。リプレイス対象となるレガシーAPIは規模が大きいので、フェーズで区切り、段階的にリプレイスを進めています。区切られた各フェーズは、フェーズ1、フェーズ2といった形で呼び分けており、フェーズごとにリプレイス対象とするエンドポイントを設定しています。一方で、事業案件や他マイクロサービスのリプレイスが並行して行われるため、フェーズごとにリプレイス計画を柔軟に調整してきました。 本記事ではレガシーAPIのリプレイスについて、フェーズ3までを担当者が背景と課題を踏まえつつ紹介していきます。 目次 はじめに 目次 背景 フェーズ1 課題 1. リプレイス先APIの開発が初めて 2. リプレイスの土台が整っていない 課題1への取り組み 1. 毎日集まって開発状況を確認 2. 事前に既存実装のシーケンス図を作成 3. 積極的に有識者とのモブプロを行う 課題2への取り組み 1. 開発チームメンバー全員での既存コードの読み合わせ 2. クライアントに影響がない実装方法を採用 3. 本番環境へのリリース手法としてカナリアリリースを採用 結果 課題が解決できたか? 新たな課題 フェーズ2 課題 フェーズ2で取り組んだこと 結果 課題が解決できたか? 新たな課題 フェーズ3 課題 フェーズ3で取り組んだこと 結果 課題が解決できたか? 新たな課題 まとめ 背景 こんにちは。湯川と申します。まず私からは、本リプレイスプロジェクトの始まった背景と導入部分を説明します。 レガシーAPIは、十数年前にZOZOTOWNアプリがローンチされた際から存在しており、当初からVBScriptで実装されていました。しかし、年月が経ち、コードは複雑化し、メンテナンスコストが増大していました。さらに、VBScriptは歴史のある言語であり、新しいエンジニアの採用が難しい状況でした。 そこで、マイクロサービス化を進め、Webサイトをモダン化するリプレイスプロジェクトが社内で発足し、レガシーAPIもこのプロジェクトの一環としてリプレイスすることとなりました。リプレイスの目的は、VBScriptからの脱却を図り、現代的な言語とフレームワークを使用することです。 既にZOZOTOWNアプリの一部の画面で本番稼働しているBFF API(JavaのSpringで実装)をリプレイス先APIとして、レガシーAPIをリプレイスしていくこととなりました。 BFF APIについてはこちらの記事で紹介しているので、合わせてご覧ください。 techblog.zozo.com まず、私たちが取り組んだのは、計画を立てるために各APIが直接DBへ問い合わせしている箇所やマイクロサービスを参照している箇所など、外部との接続がどこでどのくらいあるかを調査することでした。 この調査により、以下のポイントが明らかになりました。 ロジックの複雑度の算出:どのくらいの接続があるかを把握することで、ロジックの複雑度を算出し、APIの移行難易度を把握することに役立ちました。 接続経路の特定:各APIがどこと接続しているかを把握することで、既存で存在しない接続経路があればシステムアーキテクチャに関わる新規構築が必要だと判断できました。 このポイントを押さえながら段階的なアプローチを取り、チーム内で完結する小規模なリプレイスから進めていく計画を立てました。 フェーズ1 こんにちは、アプリバックエンドブロックの伊藤です。ZOZOには2022年1月から中途入社し、2023年2月から5月にかけて実施されたレガシーAPIのリプレイスフェーズ1のプロジェクトに、開発者の1人としてアサインされていました。 課題 フェーズ1のプロジェクトには以下のような課題がありました。 1. リプレイス先APIの開発が初めて リーダーはリプレイス先のAPI開発経験がありましたが、開発者としてアサインされたメンバーはJavaの開発経験が浅く、リプレイス先APIの開発が初めてでした。また、社歴も比較的浅いため、現状のキャッチアップが必要な状態からのスタートでした。 2. リプレイスの土台が整っていない 今後長期にわたるリプレイスの初フェーズということで、そもそも案件全体でリプレイスについて十分な知見がない状態でした。 そのため、以下のような観点から対象のエンドポイントを選定し、リプレイスを開始しました。 今後のフェーズの土台を築く上で、まずはリプレイスの実績を作る 未経験者でも置き換えしやすい、スコープが小さく複雑度の低いシンプルなものから取り組む 課題1への取り組み 課題1つ目の「リプレイス先APIの開発が初めて」に対しては以下のようなことに取り組み、課題の解決に役立ちました。 1. 毎日集まって開発状況を確認 Javaの開発経験の浅いメンバーにとって、困っていることや詳細なタスク状況を共有しやすい環境を作ることは、開発をスムーズに進める上でとても重要でした。 2. 事前に既存実装のシーケンス図を作成 処理の流れやエラーパターンが事前に視覚化されていたことで、実装面で役に立ったことはもちろん、他チームとのコミュニケーションもスムーズに進みました。 3. 積極的に有識者とのモブプロを行う チームメンバーがどのようにコーディングやデバッグをしているかを学び、経験不足を補うことができました。 課題2への取り組み 課題2つ目の「リプレイスの土台が整っていない」に対しての取り組みは以下の通りです。 1. 開発チームメンバー全員での既存コードの読み合わせ 実装前に作成したシーケンス図へは起こしきれないような細かいロジックがありましたが、メンバー間で理解度を合わせることで、コードレビューの段階でバグを見つけることができました。 2. クライアントに影響がない実装方法を採用 レガシーAPIのインタフェースとURLは、あえて既存の形式を維持する方針でリプレイスを行いました。理由はクライアントであるアプリ側への影響なく、バックエンドとインフラのみの修正でリプレイスを進められるようにしたかったからです。そうすることで関わるチームは少なくなり、結果的にスピーディーな開発につながりました。 具体的に行った対応は以下の通りです。 リプレイス先APIでは、レガシーAPIのインタフェース設計をそのまま引き継いだ アプリからのリクエストを受け付けるAPI Gatewayに対象エンドポイントのルーティング設定を追加した 3. 本番環境へのリリース手法としてカナリアリリースを採用 既存実装がかなり複雑な部分でバグを生んでしまい、リリース後に意図しないエラーが発生しましたが、段階的にN%リリースをしたため影響を最小限に抑えることができました。 カナリアリリースの詳細については以下のテックブログで紹介しておりますので、気になった方はご参照ください。 techblog.zozo.com 結果 課題が解決できたか? 対象エンドポイントのスコープは小さかったものの、上記のような取り組みのおかげで経験不足を補いながら無事にリリースができました。これらの取り組みは後に紹介されるフェーズ2以降でも活かされています。 また事前にわかっていた課題以外にも、以下のような実装以外のタスクが発生しましたが、プロジェクト内で都度対応しながら解決していきました。 今後続くリプレイス案件の土台を整えるために、最適なリプレイス方法を議論する必要があった リプレイス先APIのコーディング規約やレビュー規約が整っていない状態であった メンバー間の役割分担が上手く行かず、1人が案件リーダーと開発リーダーの2つの役割を担ったため作業過多になった 新しくリプレイスプロジェクトを始める際には、このような点を事前に考慮できると良さそうです。他にも上記の例に限らず、想定外のタスクが発生するかもしれないため、スケジュールにバッファを設けておくと良いかもしれません。 新たな課題 フェーズ1の中でわかったことは、レガシーAPIの実装は想像よりも難解であったということです。コードレビュー時にはチームメンバー全員でコードの読み合わせをしたものの、実装時には担当者が個人でレガシーAPIの処理を調査したために、もっと深いところで理解度に個人差が生まれていました。その結果リリース後のバグ発生につながってしまいましたが、バグを正しく修正するために再度チームメンバー全員でコードを読み合わせたことが役に立ちました。 フェーズ2 ZOZOTOWNアプリバックエンドのエレーナです。レガシーAPIリプレイスフェーズ2プロジェクトの開発者の1人として参加したので、フェーズ2について説明いたします。 課題 リプレイス先APIでは、データベース(以降DB)へ直接接続しない設計となっています。原則的に該当するマイクロサービスからしかDBにアクセスしません。一方でレガシーAPIは直接DBにリクエストしているところが多い状態で、置き換え方法が決まっていないことがリプレイスのブロッカーとなっていました。そのため、そのブロッカーを解除することがフェーズ2のメインの目的となりました。 その上で、同時並行で開発していた新規機能においても、リプレイス先APIからの直接DBリクエストが必要だと判明しました。新規機能のリリーススケジュールに影響を与えないように、DBアクセスの課題の優先度がさらに高まったため、早急に解決する必要がありました。 フェーズ2で取り組んだこと モノリスなレガシーAPIをマイクロサービスで設計されたシステムへリプレイスする場合、理想的な置き換え先は以下になります。 BFF層で管理すべきコードをリプレイス先APIへ置き換え マイクロサービスで管理すべきコードをマイクロサービスへ置き換え しかし、一部のマイクロサービスがまだ設置できていない現状で、リプレイスを必ず新規サービスの設置から始めると、多大な工数がかかります。リソースが限られている状態でさらに柔軟な方法が必要なので、マイクロサービスができていない場合は該当するロジックを過渡期APIへ置き換えるようにしました。 過渡期APIとは、レガシーAPIのシステムを再利用した名前の通り仮のAPIとなっています。レガシーのインフラストラクチャー上にできているので、DBへ接続経路が存在しています。 過渡期APIに含む処理は重要なポイントが2つあります。 その処理はリプレイス先APIでできないこと(例:DB直アクセス) 将来的にはマイクロサービスへ置き換える前提 サービス自体がまだ存在しなくても今のタイミングでBFF層とマイクロサービス層のロジックが切り離し可能で、過渡期APIへ置き換える余分なステップが無駄になりません。 そして最後に、事業案件の事情でフェーズ2をなるべく早めにリリースできるように、対象エンドポイントは1本で複雑度が低いものを選定しました。 結果 課題が解決できたか? 小さくてシンプルなエンドポイントを選んだおかげで不具合なしに短い期間で実装が完了して無事にリリースできました。リリーススコープが小さかったですが大きなリプレイスブロッカーを解除できました。 新たな課題 実は、リプレイスの大きなブロッカーがもう1つ残っています。レガシーAPIは端末やユーザー情報をセッションに保存および取得しているところがあるので、該当処理の置き換え方法を検討する必要があります。最初はその課題を含めてフェーズ2で全リプレイスブロッカーを解除する予定でしたが、フェーズ2のリリースを早める方が優先だったのでセッション課題の解決をフェーズ3へ移動しました。 フェーズ3 アプリバックエンドブロックのカイルです。2023年10月からレガシーAPIのリプレイスプロジェクトに参加しています。 課題 マイクロサービスを経由していないDBアクセスの経路ができたので、次は セッションに入っている情報へのアクセス経路を作る という課題の解決に取り組みました。レガシーAPIでは、ユーザーがログイン中でも未ログインでもシステム全体でユーザーの情報にアクセスできるようにセッションに保存しています。 セッションと言っても、過去のマイグレーションプロジェクトでサーバー上に保存していたデータをRedisに移行することがすでに完了していました。ただ、アクセスする方法はレガシーAPIのフレームワークで使っているプラグインのみで、違う言語で実装されているリプレイス先APIからアクセスする経路は存在しませんでした。 セッションデータが保存されているRedisに直接アクセスしてもよかったのですが、特定のデータ形式や保存先に依存したくないと考えていました。また、セッション情報は複数のシステムからアクセスする必要があるのでアクセス方法を統一する要望もありました。 レガシーAPIではセッションから取得した情報を使っているエンドポイントが多く、アクセス方法の課題が今後のリプレイスのブロッカーとなっていました。 フェーズ3で取り組んだこと セッション情報のアクセスを統一するため、マイグレーションプロジェクトを担当しているチームでまずセッション基盤を開発することにしました。フェーズ3ではリプレイス先APIからセッション基盤への経路を追加してセッション情報を取得できるようにしました。 また、フェーズ3へ入る前にレガシーAPIが動いていたオンプレサーバーの縮退スケジュールが決まりました。縮退スケジュールに間に合うようにサーバー負荷が高いエンドポイントからリプレイスを行うことを決めました。 そこで、まずはレガシーAPIの全エンドポイントで、レガシーAPI全体のリクエスト数に対する各エンドポイントのリクエスト数の割合を算出しました。そして、この割合が高いものをサーバー負荷が高い(リプレイスの優先度が高い)エンドポイントとして優先順位を付けました。 そのため、セッションの取得が必要となり、かつサーバー負荷が高いエンドポイントとして、商品詳細画面の下部に表示されるレコメンド商品を返すAPIをフェーズ3の対象としました。 フェーズ3の進め方についても新しく実施してみたことがあります。フェーズ1の課題だったレガシーAPIの理解を改善するために、案件のキックオフ後に開発メンバー全員でコードを読みながら既存のレガシーAPIの仕様を理解したうえで開発に入りました。 結果 課題が解決できたか? 対象エンドポイントで必要となるセッション情報を新たにセッション基盤経由で取得できるようになり、今までなかった経路をひとつ実現できました。そのおかげで今後リプレイスするエンドポイントでもセッション情報の参照が必要な時にこの経路を使って実装できるようになりました。 そして事前に既存処理をすり合わせて設計を決めることによって、よりスムーズに開発を進めることができました。 新たな課題 今回できたのはセッション情報の参照のみですが、他のエンドポイントでセッション生成や更新、セッションクリアなどの操作が必要になってきます。セッション基盤ではそのようなケースをどう扱うかはまだ検討中の段階で、これから確定して実装する必要があります。 そして開発プロセスにおいても、全体の流れが安定してきた一方で新たな課題も見えてきました。特にリリースサイクルをもっと柔軟にしないといけないと感じました。今まではひとつのフェーズの対象となっているエンドポイントを全て開発して、テストとリリースをまとめて行っていましたが、フェーズが大きくなるとコードの量が膨大になって開発しづらくなります。エンドポイントごとのリリースサイクルを導入できたらコード管理やリリーススケジュールが調整しやすくなる想定です。 フェーズ3でも実際に開発の途中でスコープが変更されて、同じブランチで管理していた複数エンドポイントのコードを分ける作業が必要となりました。今後のフェーズではリリースサイクルをもっと細かくすることで開発を楽にしたいと考えています。 まとめ 本記事では、これまでのレガシーAPIのリプレイスで取り組んだ課題とその解決方法をフェーズごとに紹介しました。これまでのリプレイスによって、チーム内のJava開発経験を蓄え、各マイクロサービスへの接続経路を作る事ができました。まだリプレイスは道半ばです。今後はこれまでのリプレイスで蓄えた開発経験や整えた接続経路を基に、残りのエンドポイントのリプレイスも進めていきます。本リプレイスでのリプレイス計画の立て方、進め方が、今後APIのリプレイスを検討している方の参考になれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは、DevRelブロックの ikkou です。5月13日に「Google Cloud Next ‘24 Recap in ZOZO」と題した、Google Cloud Next ‘24の振り返りイベントをオンラインで開催しました。 zozotech-inc.connpass.com 本振り返りイベントの前提となる、Google Cloud Next ‘24に参加したメンバーによるレポート記事もあわせてご覧ください。 techblog.zozo.com 目次 はじめに 目次 当日の登壇内容 Google Cloud Next ‘24 Recap AIにより変わる開発・運用について AIに対応したBigQueryと今後のデータ分析について Datastreamを使用したリアルタイムデータストリーミングの紹介 最後に 当日の登壇内容 4月のGoogle Cloud Next ‘24に参加したMA部のエンジニア3名に加え、特別ゲストとしてグーグル・クラウド・ジャパン合同会社の小野様にご登壇いただきました。 タイトル 登壇者 Google Cloud Next ‘24 Recap グーグル・クラウド・ジャパン合同会社 小野 友也 様 AIにより変わる開発・運用について 平宮 瑛宜 AIに対応したBigQueryと今後のデータ分析について 杉田 駿介 Datastreamを使用したリアルタイムデータストリーミングの紹介 佐久間 貴人 今回のイベントではオフライン会場は設けず、YouTubeのライブ配信のみで実施しました。当日の発表はYouTubeのアーカイブ動画をご覧ください。 なお、グーグル・クラウド・ジャパン合同会社の小野様による発表は都合によりアーカイブに含まれておりません。あらかじめご了承ください。 www.youtube.com Google Cloud Next ‘24 Recap グーグル・クラウド・ジャパン合同会社の小野様には、Google Cloud Next ‘24で発表された各機能をご紹介いただきました。20分という短い時間で、新たに発表された機能をギュッと濃縮した、振り返りイベントに相応しい発表でした。ありがとうございました! AIにより変わる開発・運用について speakerdeck.com 平宮は、前提事項としてZOZOでMA部が何をやっているのか紹介した後、Gemini in ◯◯シリーズとして、Google CloudにおいてGeminiをどのように活用できるかを紹介しました。 AIに対応したBigQueryと今後のデータ分析について speakerdeck.com 杉田はZOZOの業務とも関わりの深いBigQueryに関するAI系の新機能について、具体的な例を挙げながら紹介しました。できることが多くなる分、より緻密な権限管理が必要となることについても触れました。 Datastreamを使用したリアルタイムデータストリーミングの紹介 speakerdeck.com 佐久間はDatastreamのユースケースとZOZOでの利用例にあわせて、今後試したいことを紹介しました。 最後に イベント当日にリアルタイムでご視聴いただいた皆様ご視聴ありがとうございました。見逃した方はぜひアーカイブ動画をご覧ください。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。計測システム部、研究開発ブロックの皆川です。普段はコンピュータービジョンに関わる研究開発を担当しています。 2024年の3月に3次元コンピュータービジョンの国際学会である 3DV 2024 がスイスのダボスで開催され、幸運にも参加できたので、発表の内容や参加した感想をご紹介いたします。 目次 はじめに 目次 3DV 2024とは なぜ参加したのか 開催地のダボスと、会場のダボスコングレスセンターについて 学会のスケジュール 印象に残った発表 全体的な感想 3D Computer Vision for Dynamic Scene Understanding by Daniel Cremers ドライバーアシスト ドローンを使った研究 バンドル調整 初期のSLAM 直接的なSLAM ニューラルネットワークとSLAM さいごに おまけ 3DV 2024とは 先述の通り、3DVは3次元のコンピュータービジョンに関する国際学会です。3DVは、他のメジャーなコンピュータービジョンの学会であるICCVやCVPR、SIGGRAPH等の学会と比べて、かなり小規模な学会と言えます。実際、近年のそういった巨大学会の参加者は5000人程度のものから多いものだと1万人以上に上ります。一方で3DV 2024の参加者数は、著者が実際に関係者から聞いたところによると350人と、巨大学会と比べて10分の1以下のスケールでした。 なぜ参加したのか 近年、学問分野として大きな盛り上がりを見せているコンピュータービジョンやAIですが、その研究環境に関する特徴として以下のようなことが言えると思います。 arxiv というpeer reviewを必要としない高速かつオープンアクセスな論文プラットフォームがある。 実際の論文だけでなく、論文のアイデアを視覚的に説明する動画だったり、実際に自分で試せるコードを内包したプロジェクトページがある( その一例 )。 特定の技術領域を整理して、理解を助けるようなサーベイ論文( その一例 )やGitHubリポジトリ( その一例 )がある。 X(旧Twitter)で、スペシャリスト達が論文の間を埋めるようなアウトプットをしてくれている。 これはつまり研究者にとって自走しやすい環境が整っているということです。実際、今回発表された研究は、ほぼ例外なくarxivに既に掲載されており、プロジェクトページで動きを確認できるものが多かったです。さらに事前に該当するサーベイを読み込んでいたら、学会会場で得られる情報はあまり多くないと思います。それでは一体、飛行機で14時間もかかる物価の高い国に苦労して行くことの対価はどこにあるのでしょうか。それは以下にあると思います。 社外の研究者の持っている経験則や常識、ナラティブを吸収できる 参加者から刺激をもらえる 結果から言うと、どちらも大きな収穫がありました。具体的に言うと、カメラ位置の推定技術など、雑多な技術が混在していてなんとなく俯瞰できてないような分野をよりクリアな目で見ることができるようになりました。またNeural Implicit RepresentationやDiffusion Modelといった、流行の技術と人体計測の関係もよりクリアになりました。 また他社の研究者達との雑談からたくさん刺激を受けました。ドイツ語圏(スイス、ドイツオーストリア)の研究機関の多くは普段から英語で研究活動をやっていること。研究者同士の機関を超えたコラボレーションが多いこと。そしてGAFAMの研究開発のスケールの大きさなど、驚かされることが多かったです。 開催地のダボスと、会場のダボスコングレスセンターについて 開催地となったダボスは、スイスの玄関口チューリッヒ空港から電車で3時間くらいにある、人口1万人ほどの小さな観光地です。 Davos Dorf駅を中心とした居住エリアが、ダボスの90%以上を占める山岳エリアに囲まれており、居住エリアの端から端まで約1時間ほどで歩ける小さな都市です。 筆者が行ったのは3月の下旬でしたが、気温はおよそ摂氏0度で、雪がまだかなり残っており、スキー客の姿もちらほらと見られました。 現地の人の話では毎年クリスマスシーズンに行われるアイスホッケーのトーナメントの時期と、1月に開かれる世界経済フォーラム(通称ダボス会議)の時期は、町に溢れかえるほどの人が来るそうです。 学会会場の ダボスコングレスセンター は、先述の世界経済フォーラムの会場として有名です。画像は正面の入口なのですが、地図を頼りに行くと裏口に辿り着いてしまい、日本だとよくみられるような立て看板などなかったため、参加者と思われる人達が迷っていました。2日目以降も裏口で迷う参加者がおり、他の参加者がこっちだよ、と道案内をしてあげるような場面も見かけました。 下記画像は宿泊先のホテルから見た景色です。正面に見える茶色の建物がダボスコングレスセンターの裏口に当たる部分です(画像に写っているのは建物全体の5分の1程度)。実際はこの裏口の脇の坂を下って、5分ほど歩き、この大きな建物の正面に回る必要がありました。 坂を下るとコングレスセンターの正面玄関に着きます。 学会のスケジュール 学会のスケジュールは以下のような構成でした。 初日 チュートリアル 2日目から4日目 オーラル発表 ポスター発表 キーノート チュートリアルは カメラ幾何学 と、 3D Gaussian Splatting に関するものでした。執筆時点(2024/05/11)では、チュートリアルと キーノート は一般公開されています。 ポスター発表は全発表者に義務付けられていました(チュートリアルとキーノートを除く)。ポスター発表とオーラル発表は交互にあるので、オーラル発表で気になったことは後のポスター発表で直接発表者に質問できる仕組みでした。オーラル発表はすべてメイン会場(下記の画像参照)で行われるので、学会に特有の「どの発表を見るかの下調べにとても時間がかかる」という現象から完全に自由でした。 また、参加者同士のネットワークがすでにある程度出来上がっており、学会全体を通してかなりアットホームな雰囲気があったように感じます。逆にいうと、ポスターセッションや自由時間(下記の画像参照)のときに、自分のような新規参加者は少し居心地の悪さを感じるかも、と思いました。 印象に残った発表 全体的な感想 全体としては、カメラの位置推定や3D再構成に関する基礎研究が多かったという印象でした(合わせて体感で4割くらい)。対照的にVedaldi氏の キーノート やNeRFの主著者であるMildenhall氏の キーノート では、3Dのパラメトリックモデルや生成モデルの応用など、新規性の高いトピックが触れられていました。 また同じトピックでも、理論的な発表と実践的な発表のバランスが取れているように感じました。例えばカメラの位置推定で言うと、初日に 理論的なチュートリアル 、2日目以降には実際にドローンや自動運転の会社の創業者でもあるプレゼンターのキーノート( ドローン 、 自動運転 )がありました。 発表の内容としては、著者と同一の課題(画像を元にした身体計測)に取り組んでいる発表がなかったのは残念でした(過去の3DVにはありました)。ただし既存のパイプラインに組み込めそうな技術はいくつか見つかったので、収穫はあったと言えます。 3D Computer Vision for Dynamic Scene Understanding by Daniel Cremers このキーノートは、Cremers氏の研究グループの約20年間の自動運転に関する研究を総括するような発表でした。 www.youtube.com ドライバーアシスト 約20年前のドライバーアシストの研究成果について(動画の 7:53 頃)。Cremers氏の研究グループは、画像から深度と物体の動きを色付きで可視化するような仕組みについて研究をしていたそうです。現在は自動運転の研究が盛んですが、当時はドライバーをアシストするような方向の研究分野も盛んだったとのこと(現在もこの分野はあるそうです)。 ドローンを使った研究 ドローンを使った研究も長年続けてきた分野とのこと( Engel et al., IROS 2012 。動画の 10:28 頃)。PTAMという方法でSLAMを行い、一応の自律飛行はできるようになったが、求めていた精度までは達しなかったそうです。例えば屋外に出てから屋内に戻るといったような飛行は実現できなかったとのことでした。ただし2017年に提唱したLSD SLAMという方式ではそれが実現できたとのこと( Von Stumberg et al., ECMR 2017 )。 バンドル調整 オーストリアの数学者Kruppaが1913年にした証明が今日バンドル調整(Bundle Adjustment)と呼ばれる技術の先駆けになったとのことでした。バンドル調整とは複数のカメラ画像から対象の物体の再構成とカメラ位置の推定精度を上げるような技法のことで、とても歴史が長いことから解かれた問題と理解している人が多いそうですが、実際は違うとのことです。実際、最近の研究( Demmel et al., CVPR 2021 , Weber et al., CVPR 2023 )では演算スピードやメモリ効率の大幅な向上が達成されているとのこと。 初期のSLAM 上記のようにリアルタイムでない、3D再構成の精度を最重要視したバンドル調整についての研究のほか、SLAM(Simultaneous location and mapping)の研究も多いとのこと。また、SLAMが初めてリアルタイムで実現できたのは2002年頃(動画の 20:50 頃)だそうです。ただし当時のSLAMの方式はKruppaの流れを汲むもので、画像から特徴点を抽出、マッチングする方法でした。 「3DV 2024 Keynote - Daniel Cremers - 20.03.2024」の20:37よりスライド部分を強調して引用 直接的なSLAM その方法的な限界を突破するため、直接的な方法であるLSD SLAM( Engel et al., ECCV 2014 )を提唱したのが2014年だそうです。特徴点の抽出に頼らず、画像1から画像2へ再投影した際の色の差が最小になるようなカメラの移動と3Dモデルを見つける、という問題設定です。当時世界初の大規模SLAMシステムであるにもかかわらず、単眼カメラと市販のラップトップのCPUでリアルタイム処理ができるとのこと(動画の 22:48 頃)。後継のDirect Sparse Odometry( Engel et al., PAMI 2018 )やDMVIO( Stumberg et al., ICRA 2022 )で性能は更に上がったとの事です。 ニューラルネットワークとSLAM ニューラルネットワークがSLAMの分野で応用され出したのは意外に遅く、2017年頃だそう( Zhou et al., CVPR 2017 等)。ただし、当時それらはまだSOTA(state-of-the-art、特定タスクで最高スコアの方法のこと)ではなかったとのことです。D3VO( Yang et al., CVPR 2020 )では、連続する2画像を用いカメラ位置や深度などをニューラルネットワークに学習させることで複眼のVIOと同等の精度を達成できたとのこと。つまりこの方法は深度センサーや感性センサーを代替する方法として有効であることが示唆されるそうです。 以上はいずれも静的なシーンの理解に分類されるタスクとのことです。例えば、連続する二画像間で動いているものは、普通フィルタリングやマスキングで推論や演算に影響のないような処理がされるとのこと。その他にもダイナミックなシーンの理解というテーマで最近の研究結果が紹介されていましたが、時間の都合上割愛いたします。Cremers氏は発表全体を通して、厳密さとわかりやすさ、そしてユーモアに注力されているのが感じられました。また自動運転やSLAMの技術にあまり詳しくない著者でも、発表を何度も見返すことで分野への理解がどんどん深まるように感じました。 さいごに コンピュータービジョンの小規模な国際学会である3DV 2024に参加した感想や内容の一片をお伝えしました。生身の人間が集まる学会に物理的に参加することの利点として、そこでしか得られない情報を得られたり、他の研究者達から刺激を貰えることがあると感じました。また小規模学会の良さとして、聞く発表を選ぶ必要がない良さは感じましたが、分野に明るい人は見る発表の選択肢が少ないと感じるかもしれないとも思いました。 また個人的な感想ですが、毎日朝から晩まで屋内にこもってひたすら新しい技術を勉強し、夜はホテルに帰ってひたすら寝るという生活は、受験生の夏休みのような少し懐かしい感じもしました。今回こういう特殊な経験ができたことに対して、とても感謝しています。 おまけ 会場ではBoston Dynamicsの Spot が歩き回ったり、タンスの中のものを探したりするデモが見られました。スイスの ETH Zurich ではこのSpotを使って研究ができる学部生向けの授業があるそうです。 次の指示を待っているSpotの様子 以上になります。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com 最後までご覧いただきありがとうございました!
はじめに こんにちは、 ZOZOMO店舗在庫取り置き というサービスの開発を担当している、ZOZOMO部OMOブロックの木目沢です。 2024年4月17日から19日の3日間にかけて東京ビッグサイトで 「ファッション ワールド 東京 2024 春」 が開催され、このイベントにZOZOが出展しました。 ZOZOの出展ブースでは、私が開発を担当している 店舗在庫取り置き や FAANS 、 Fulfillment by ZOZO の3サービスが展示されました。 展示にあたっては各サービスのビジネスチームのメンバーが中心となり、準備から出展までを行いました。私たち開発チームからも数名が参加し、ビジネスチームと共に出展ブースにて多くの来場者の方々とお話しさせていただきました。 この記事では、前半で 「ファッションワールド東京 2024春」 の概要とZOZOの展示ブースの様子を紹介し、後半では開発チームがビジネスチームとともに活動する理由について述べます。 目次 はじめに 目次 ファッションワールド東京とは ZOZOが出展しました 店舗在庫取り置き Fulfillment by ZOZO FAANS 開発メンバーも参加しました 開発チームがビジネスチームと共に自事(※)をする意味 おわりに ファッションワールド東京とは ファッションワールド東京 は、毎年春と秋にRX Japanが主催するアパレル向けの展示会です。最新のサステナブルファッション、アパレル、生地、素材、ファッションDXを扱う企業が出展しています。今年の春は2024年4月17日から19日の3日間で、世界25カ国、800社が出展し、22910名が来場しました。当日の様子は 公式サイト でも公開されていましたので、ご参考までに紹介しておきます。 ファッションワールド東京 2024春 ZOZOが出展しました ZOZOの出展ブースでは、 店舗在庫取り置き や FAANS 、 Fulfillment by ZOZO のZOZOMO関連のサービスが展示されました。展示ブースには各サービスをご利用いただいているブランド様、サービスに興味を持たれているブランド様にもご来場いただき、サービスについて案内いたしました。 ZOZOブースの様子 ブース内では定期的に各サービスをより良くご利用いただくためのミニセミナーを開催しました。各回とも大変好評で、多くの方々にご来場いただき、サービスのアピールをさせていただく機会となりました。 FAANS/店舗在庫取り置き セミナーの様子 以下、ブース内に設置されていた各サービスの概要を説明するパネルと、各サービスに関連するテックブログの参考記事をご案内します。 店舗在庫取り置き AWSで実践するカオスエンジニアリング 〜ZOZOMOでの取り組み〜 ZOZOMO開発チームのユニットテスト戦略とテスト駆動開発 DynamoDBによるOutboxパターンとCDCを用いたCQRSアーキテクチャの実装〜ZOZOMOでの取り組み Chatworkさんと合同でCQRS Meetup【Chatwork × ZOZO】を開催しました #cqrsmeetup Fulfillment by ZOZO AWS CDKで構築するイベント駆動型アーキテクチャの実装戦略 FBZにおけるサーバーレス監視で実施したアラート通知の最適化 マルチAZ化から学んだ無停止でインフラを変更するために考慮すべき3点 物流支援サービスを支えるAWSサーバーレスアーキテクチャ戦略 FAANS 新規サービス「FAANS」における、立ち上げからReact+TypeScriptのSPA開発を2年間運用した際に取り組んだ組織的・技術的な課題 Kubernetesネイティブなワークフローエンジンとは!FAANSでArgo Workflowsを導入した話 FAANSにおけるCloud RunからGKE Autopilotへのリプレイス事例 Storybook × MSW × Chromaticを使ったUIの影響範囲を自動検知するための取り組み 開発メンバーも参加しました 今回の展示はビジネスチームが中心となり、出展準備を進めていきました。開発チームも運営に参加し、ご来場いただいた方々にサービスの内容や必要に応じて技術的な背景なども説明いたしました。 ファッションワールド東京のような展示会に出展し、多くの企業の方々にサービスをアピールすることは良いビジネス機会となります。また、既にサービスをご利用いただいているブランド様やサービスに興味のある企業の方々に直接お話しする機会の少ない開発チームのメンバーにとっては、ご意見やご感想を直接お伺いできるチャンスでもあります。 また、今回のようにビジネスチームと開発チームと共に活動することは、プロダクトにとっても必要なことです。以下はこの点についてご紹介したいと思います。 開発チームがビジネスチームと共に自事(※)をする意味 私たち開発チームはただサービスを作って終わりではなく、常に価値のあるソフトウェアを継続的に提供し、事業を成長させていくために活動しています。そのためには、ビジネスチームと常に一緒に自事(※)をし、状況の変化に合わせて開発を進めていく必要があります。 私たちは状況の変化に合わせて開発を続けていくために「アジャイル」であることを重要視しています。この考え方をまとめた アジャイルソフトウェア開発宣言 では、以下のように紹介されています。 私たちは、ソフトウェア開発の実践 あるいは実践を手助けをする活動を通じて、 よりよい開発方法を見つけだそうとしている。 この活動を通して、私たちは以下の価値に至った。 プロセスやツールよりも個人と対話を、 包括的なドキュメントよりも動くソフトウェアを、 契約交渉よりも顧客との協調を、 計画に従うことよりも変化への対応を、 価値とする。すなわち、左記のことがらに価値があることを 認めながらも、私たちは右記のことがらにより価値をおく。 (この宣言は、この注意書きも含めた形で全文を含めることを条件に自由にコピーしてよい。) まさに今回の展示会への参加は「顧客との協調」「個人と対話」を重視した結果とも言えます。さらに、このソフトウェア開発宣言には「 アジャイルソフトウェアの12の原則 」というページがあることはご存知でしょうか。そこには、アジャイルソフトウェア開発宣言にある「価値」に対応する「12の原則」が解説されています。 そのうちの1つに以下のような原則が示されています。 ビジネス側の人と開発者は、プロジェクトを通して 日々一緒に働かなければなりません。 顧客との協調していくことや状況によって変化していくことに対応するため、常にビジネス側と共に仕事をしていく必要があるということです。私たちもこの点について日々意識して活動しており、今回の展示への参加もその一環となります。 開発メンバーも参加しました ※ ZOZOでは仕事のことを自事と呼んでいます。「スタッフ一人ひとりが他人のことも自分のこととして考える」という意味と、「仕事=仕えること」ではなく「自事=自然なこと」だという意味が込められています。 おわりに 本記事では、 「ファッションワールド東京 2024春」 への出展の紹介と、開発チームがビジネスチームと共に自事をする意味を紹介しました。 今回の展示では私自身も開発メンバーとして3日間参加し、ご来場いただいた多くの方々にサービスを案内いたしました。また、サービスをご利用いただいているブランド様ともお話させていただく機会もありました。サービスについて高い評価をいただくこともあり、立ちっぱなしの3日間は大変疲れましたが、このようなお話をいただき疲れも吹き飛びました。 今後もビジネスチームと共に価値を提供し続けていきたいと考えています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com 最後までご覧いただきありがとうございました!
はじめに こんにちは、計測プラットフォーム開発本部SREブロックの近藤です。普段はZOZOMATやZOZOGLASS、ZOZOFITなどの計測技術に関わるシステムの開発、運用に携わっています。 計測プラットフォーム開発本部では、複数のプロダクトを運用していますが並行して新しいプロダクトも開発しています。SREチームでは増え続けるプロダクトの運用負荷に対して改善は行っていますが、さらなるプロダクトの拡張に備えてZOZOFITの開発運用を別チームへ移管することになりました。移管作業の中でAWSリソースを別チームが管理するAWSアカウントへ移行する作業が発生することになりました。本記事では移行時に遭遇した課題と、その課題の解決に至るまでの取り組みをご紹介します。 目次 はじめに 目次 背景・課題 調査 ユーザ移行Lambdaの作成 簡易ダイアグラム フローチャート ユーザ移行Lambdaの処理 IAMの設定 移行元で用意するRole 移行先のユーザ移行Lambdaの実行Role 移行後 移行後に顕在化した問題 ユーザ移行Lambdaを経由した場合、ユーザ認証が大文字と小文字を区別するcase-sensitiveな判定になってしまう ユーザ移行Lambdaを経由したサインイン時にSMSが2通届いてしまう まとめ 背景・課題 まず、ZOZOFITを移管する上でAWSのリソースを別アカウントへ移行する事を検討しました。別チームの管理となるため管理上は別アカウントへ移行するのが適切な形です。ただし、クロスアカウントでのリソース移行は制約も多いため、慎重に検討する必要があります。このためまずはクロスアカウントでのデータ移行方法と影響範囲について調査しました。 ZOZOFITのシステム構成は以下の記事で詳細を記載していますが、データの移行対象となるのは、S3、RDS、Cognitoのユーザープール、の3つでした。前提として今回の移行ではサービス停止(ダウンタイムが発生する)が許容されていました。この中で、S3はレプリケーションを事前に設定し、RDSはスナップショットを利用してそれぞれデータを移行するため、クロスアカウント固有の事情で影響が大きくなることはないと判断しました。一方で、Cognitoのユーザプール移行に関しては未知の部分だったので調査から始まりました。 調査 Cognitoのユーザープールの移行方法について調査した結果、 AWS公式ドキュメント から、以下の2つの方法があるとわかりました。どちらの方法もクロスアカウント固有の制約はなく、この2つの移行方法についてそれぞれのPros/Consを整理して比較しました。なお、どちらの方法でもセッション情報が引き継がれず、ユーザーがサインアウト状態になる影響も判明しました。この点に関しては共通事項のため比較要素としては記載していません。 1 CSVファイルからユーザプールへのインポート 2 ユーザ移行Lambdaを利用した、イベントドリブンのユーザ移行 移行方法 Pros Cons CSVファイルからユーザプールへのインポート すべてのユーザの移行が一括で行える パスワードリセットが全ユーザに強制される ユーザ移行Lambdaを利用した、イベントドリブンのユーザ移行 サインインまたはパスワードをリセットすることで、データの移行が完了する ユーザがサインインまたはパスワードリセットを行った際にしか移行が行われないため、移行に長期間を要する Pros/Consを比較した結果、ユーザ影響の少ないユーザ移行Lambdaによるデータ移行を移行方法として選択する方針となりました。しかし、ここで1つ課題が見つかりました。移行対象のユーザープールではMFAの設定を必須にしており、移行時にMFAの有効化ができるか懸念があったためです。他社の事例を見ると回避手段がないように見えましたがAWSのテクニカルアカウントマネージャーに相談したところ、ユーザ移行Lambdaを利用してMFAの設定を有効化する方法を教えていただきました。懸念事項の回避手段が見つかり、実際にユーザ移行Lambdaを作成することになりました。 ユーザ移行Lambdaの作成 まずはユーザ移行Lambdaの挙動を整理するために簡易的なダイアグラムとフローチャートを用意しました。 簡易ダイアグラム フローチャート サインイン パスワードリセット 移行先アカウントのユーザープールに紐づけられたユーザ移行Lambdaが移行元のユーザープールのデータを取得する形となっています。ユーザ移行Lambdaは取得したユーザのデータの取得をレスポンスとして返すだけで、実際の登録処理は行いません。実際の移行先のユーザープールへのデータ登録はAWS側が行います。ここまででユーザ移行Lambdaの動きを簡単に説明しましたが、ユーザ移行Lambdaの実処理に関して実装時に注意したポイントを記載します。 ユーザ移行Lambdaの処理 ここからユーザ移行Lambdaのコードと実装時に注意したポイントを解説します。AWSで提供されているドキュメントを参考にしながらPythonで実装しました。処理の流れとしては、最初にユーザの存在を確認し、次にイベント情報をみて処理を分岐させます。サインインの場合は認証を処理した上でレスポンスを返し、パスワードリセットの場合は何もせずにレスポンスを返す形になっています。ここで注意したポイントは以下の2つです。 データ移行の観点から、レスポンスに含めるユーザ情報は移行元ユーザープールの情報を利用する レスポンスを受け取るのはAWS側であるため、 AWS公式ドキュメント に記載されているコードに沿って実装することを優先し、例外などもそのまま返す import boto3 from boto3.session import Session import json import os def lambda_handler (event, context): # setting src resource info SRC_ROLE_ARN = os.environ[ 'SRC_ROLE_ARN' ] SRC_USER_POOL_ID = os.environ[ 'SRC_USER_POOL_ID' ] SRC_USER_POOL_CLIENT_ID = os.environ[ 'SRC_USER_POOL_CLIENT_ID' ] SRC_AWS_REGION = os.environ[ 'SRC_AWS_REGION' ] # switch to src aws account sts_cli = boto3.client( 'sts' ) response = sts_cli.assume_role( RoleArn=SRC_ROLE_ARN, RoleSessionName= "switch_role_session" ) session = Session( aws_access_key_id=response[ 'Credentials' ][ 'AccessKeyId' ], aws_secret_access_key=response[ 'Credentials' ][ 'SecretAccessKey' ], aws_session_token=response[ 'Credentials' ][ 'SessionToken' ], region_name=SRC_AWS_REGION ) src_client = session.client( 'cognito-idp' ) # get user info from src cognito by input user info from event username = event[ 'userName' ] try : user = src_client.admin_get_user(UserPoolId=SRC_USER_POOL_ID, Username=username) except Exception as e: print (f "Unexpected {e=}, {type(e)=}" ) raise e # initiate auth from src cognito, if admin_initiate_auth is failed then is raised error if event[ 'triggerSource' ] == 'UserMigration_Authentication' : print ( 'UserMigration_Authentication' ) password = event[ 'request' ][ 'password' ] try : response = src_client.admin_initiate_auth( UserPoolId=SRC_USER_POOL_ID, ClientId=SRC_USER_POOL_CLIENT_ID, AuthFlow= "ADMIN_NO_SRP_AUTH" , AuthParameters={ 'USERNAME' : username, 'PASSWORD' : password } ) except Exception as e: print (f "Unexpected {e=}, {type(e)=}" ) raise e event[ 'response' ][ 'finalUserStatus' ] = 'CONFIRMED' event[ 'response' ][ 'enableSMSMFA' ] = True elif event[ 'triggerSource' ] == 'UserMigration_ForgotPassword' : print ( 'UserMigration_ForgotPassword' ) # common response sign-in/password reset # create new user on dst cognito by response data # just now, new user is active and verified for userattribute in user[ 'UserAttributes' ]: if userattribute[ 'Name' ] == 'phone_number' : phone_number = userattribute[ 'Value' ] if userattribute[ 'Name' ] == 'email' : email = userattribute[ 'Value' ] if userattribute[ 'Name' ] == 'email_verified' : email_verified = userattribute[ 'Value' ] if userattribute[ 'Name' ] == 'phone_number_verified' : phone_number_verified = userattribute[ 'Value' ] if userattribute[ 'Name' ] == 'custom:user_id' : user_id = userattribute[ 'Value' ] event[ 'response' ][ 'userAttributes' ] = { 'username' : username, 'email' : email, 'custom:user_id' : user_id, 'email_verified' : email_verified, 'phone_number' : phone_number, 'phone_number_verified' : phone_number_verified } event[ 'response' ][ 'messageAction' ] = 'SUPPRESS' # output migration user_id print (f "Migration: {user_id=}" ) return event IAMの設定 今回はクロスアカウントでの移行となるため、移行元と移行先でそれぞれRoleを作成しました。移行元では移行先のユーザ移行Lambdaが実行するRoleに権限を委譲する目的でRoleを用意しています。 移行元で用意するRole IAMRoleLambdaFunctionImportCognitoUserPool : Type : 'AWS::IAM::Role' Properties : RoleName : 'import-cognito-user-pool-role' AssumeRolePolicyDocument : Version : '2008-10-17' Statement : - Effect : 'Allow' Principal : AWS : !Sub 'arn:aws:iam::${DestinationAWSAccountID}:role/${DestinationAWSIamRole}' Action : 'sts:AssumeRole' Policies : - PolicyDocument : Version : 2012-10-17 Statement : - Effect : 'Allow' Action : - 'cognito-idp:AdminGetUser' - 'cognito-idp:AdminInitiateAuth' Resource : - !Ref CognitoUserPoolSrcArn PolicyName : 'import-cognito-user-pool-policy' 移行先のユーザ移行Lambdaの実行Role IAMRoleLambdaFunctionImportCognitoUserPool : Type : 'AWS::IAM::Role' Properties : RoleName : 'import-cognito-user-pool-lambda-function-role' AssumeRolePolicyDocument : Version : '2012-10-17' Statement : - Effect : 'Allow' Principal : Service : - 'lambda.amazonaws.com' Action : 'sts:AssumeRole' Policies : - PolicyDocument : Statement : - Effect : 'Allow' Action : - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:DescribeLogStreams' - 'logs:PutLogEvents' Resource : !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*' PolicyName : 'import-cognito-user-pool-lambda-function-policy' - PolicyDocument : Version : 2012-10-17 Statement : - Effect : 'Allow' Action : - 'sts:AssumeRole' Resource : !Sub 'arn:aws:iam::${SourceAWSAccountID}:role/${SourceAWSIamRole}' PolicyName : 'import-cognito-user-pool-assume-role-policy' 移行後 ユーザ移行Lambdaで発生した例外は準正常系のみであり、想定内の挙動に収まりました。また、移行後に全ユーザがサインアウト状態となるため、想定以上のサインインが行われた場合にCognitoのAPIのRateLimitに抵触することを懸念しましたが、無事想定内に収まりました。結果、大きな問題は起きませんでしたが、いくつか想定外の問題が発生しました。 移行後に顕在化した問題 ユーザ移行Lambdaを経由した場合、ユーザ認証が大文字と小文字を区別するcase-sensitiveな判定になってしまう 今回は移行処理のため、移行元のユーザのemailをレスポンスとして返却していましたが、この点で問題が発生しました。具体的にはLambdaが受け取ったusernameとLambdaから返されるレスポンスのemailが完全に一致していない場合はデータ移行がされないとわかりました。通常の処理では大文字と小文字を区別しないcase-insensitiveな判定をしているため、挙動が違う形となってしまいました。通常ケースとユーザ移行Lambdaを経由した場合の挙動の違いは以下の通りです。 ケース ユーザの入力値 DBに保存されている値 結果 通常のサインイン USER@example.com user@example.com 成功 ユーザ移行Lambda経由のサインイン USER@example.com user@example.com 失敗 通常のパスワードリセット USER@example.com user@example.com 成功 ユーザ移行Lambda経由のパスワードリセット USER@example.com user@example.com 失敗 テスト時に検証できていなかったケースで、移行後にAPIサーバ側でDBに保存されているユーザのemailを取得し、CognitoにはDBから取得したemailを受け渡す形に修正し、問題を解消しました。 ユーザ移行Lambdaを経由したサインイン時にSMSが2通届いてしまう サインイン時にユーザ移行Lambdaを経由した場合、移行先と移行元のCognitoからそれぞれSMSが届いてしまう問題が発生しました。こちらはテスト時にも発生していた問題でしたが、見落としてしまいました。切り替え後に移行元のCognitoでMFAを無効化することで対応しました。単純なテスト時の見落としですが、切り替え後に移行元のCognitoでMFAを無効化するべきだったこと、一時的にであれこの事象を許容できない場合はダウンタイムが不可避なこともわかりました。 まとめ いくつか移行後に問題が見つかりましたが、大きなトラブルなくユーザ負担も最小限に抑えて移行が完了しました。また、今回の移行作業は経験したことのない作業だったので多くの知見が得られました。 特に認証機構として利用しているCognitoについてデータ移行を想定していなかったこともあり、実際に調査してみないとわからない部分が多くありました。いざ移行するとなった時に初めて方法を検討する形となったのも反省点です。どのようなサービスであれ移行を考慮した上での技術選定は大切だなと改めて学びました。 計測プラットフォーム開発本部では、今回紹介したように、新規サービスの開発を活発に行いながら運用負荷を削減することでバランスを取って働いています。このような環境を楽しみ、サービスを一緒に盛り上げていける方を募集しています。少しでもご興味のある方は以下のリンクからぜひご応募ください。 corp.zozo.com CSVファイルのインポートについては、 AWS公式ドキュメント に詳細な仕様が記載されています。 ↩ ユーザ移行Lambdaについては、 AWS公式ドキュメント に詳細な仕様が記載されています。 ↩
こんにちは、MA部MA開発ブロックの @gachi-muchi-engineer です。 4/9-4/11に開催された Google Cloud Next '24 へ参加してきました。去年に続きオフライン開催で、今年はアメリカ・ラスベガスで開催されました。弊社からはMA部の @gachi-muchi-engineer ・ @da-sugi ・佐久間の3名が参加しました。去年参加した際の様子は以下のテックブログで紹介しています。 techblog.zozo.com 今年はどのようにAIを利用しているのか、利用していくのかを紹介したセッションが多かったように感じられました。本記事では、現地での様子と特に興味深かったセッションをピックアップして紹介します。 また、今回のテックブログで紹介できなかった内容などを含めてRecapのオンラインイベントを2024/5/13に開催予定です。このイベントでは、Google Cloud Japan合同会社の方にも登壇していただき今回のGoogle Cloud Next '24について詳しくお話いただきます。ぜひご参加ください。 zozotech-inc.connpass.com 現地での様子 Google Cloud Next '24の会場であるマンダレイ・ベイのコンベンションセンター 今回のイベントは、ラスベガスのマンダレイ・ベイで開催されました。会場はホテルに併設されているコンベンションセンターで、東京ドーム4個分ほどの広さだそうです。フルリモートで運動不足だった私達の足腰は、会場内を歩き回るだけでかなり鍛えられたと思います。 セッション会場の様子 各セッションは、センター内の大きな会議室で行われました。たくさんの会議室があり、それぞれ広々とした会場でした。 エキスポ会場内の地図 企業ブースは、とても大きな展示スペースで大変な数が出展されていました。お話した企業ブースの方が「この雰囲気はクレイジーだぜ」と言っていたのがとても印象深かったです。 それ以外にも企業ブースでは、Passport Program(いわゆるスタンプラリー)が行われていました。特定のブースを回ってQRコードを読み込むとCloud Nextのグッズがもらえるというものもありました。 エキスポ会場内の様子 以降で現地参加したメンバーが気になったセッションについて紹介します。 セッション紹介 @gachi-muchi-engineer です。 私は主にバックエンドの開発・運用に携わっています。今回参加したGoogle Cloud Next' 24の発表の中から今後の開発・運用が大きく変わっていくと感じた内容を紹介します。 Gemini for Google Cloud 今回のGoogle Cloud Next '24では、Google CloudのサービスにGeminiを導入した機能の紹介が多かったです。 まずGeminiについて簡単に紹介します。GeminiはGoogleが提供する最新のAIモデルです。テキストや画像、動画、音声などの複数の異なるデータを一度に理解できるマルチモーダルモデルの生成AIです。 詳しくは、以下のGoogleブログの記事をご覧ください。 blog.google GeminiをGoogle Cloudのサービスに導入することで、エンジニアをサポートする機能が追加されました。私のパートでは、Google CloudのサービスにGeminiが導入された機能を中心に紹介していきます。 What's next for Google Cloud databases in the gen AI era データベースに関して、"What's next for Google Cloud databases in the gen AI era"のセッションからいくつか紹介します。 Gemini in Databases データベースの開発・管理・運用を支援する3つの機能の紹介がありました。 1.Database Studio 「 What's next for Google Cloud databases in the gen AI era 」の27:32より引用 Cloud SQLにDatabase Studioが追加されました。BigQueryのWebコンソールと同じような操作性で、Webコンソールからクエリを実行し結果を確認できるようになります。 それに加えて、Geminiのサポートによりコンソールで自然言語を利用してクエリを生成できるようになります。他にも既存クエリを最適化してもらえるのはとても便利だと感じました。また、 Query Insights と合わせて利用することによりクエリの実行計画の可視化やクエリの実行時間の比較なども行えるので、開発者にとって非常に使いやすいと感じました。Database Studioを実際に利用してみましたが、とても操作性がよく利用しやすかったです。 今までは運用時に、 Cloud SQL Auth Proxy を利用したり、Cloud Shellからデータベースに接続したりしていました。ただ以下の課題を感じていました。 Cloud SQL Auth Proxyでは意図しないデータベースに接続してしまうなどのリスクがある Cloud Shellから接続する場合は、接続するのに時間がかかってしまう Database Studioを利用することで、これらの課題を解消し、より簡単に運用ができると感じました。 これだけでもとても便利ですが、これに加えてGeminiによるクエリのサポートを得られるようになります。Geminiに「こういうデータがほしい」と問いかけるだけでクエリを生成してくれるので、調査などの運用コストが下がると感じました。 2.Database Center 「 What's next for Google Cloud databases in the gen AI era 」の27:56より引用 Google Cloudのプロジェクトごとに運用しているデータベースを一元管理できるサービスです。自然言語で「セキュリティリスクを含んでいるデータベース」や「特定の状態になっているデータベース」などの検索が可能です。もちろん検索しただけではなく、そのデータベースに対してセキュリティアップデートの適応やスケールアップなどの操作も可能です。 3.Database Migration Service 「 What's next for Google Cloud databases in the gen AI era 」の35:22より引用 データベース移行を支援するサービスです。データベース移行は、データの整合性や移行後のパフォーマンスなど様々な課題がありますが、プロシージャーなどの移行や最適化もサポートされているので、簡単に行えると感じました。 紹介したサービスのAIによる支援は開発に関して大幅に生産性を向上させるだけでなく、セキュリティの課題やパフォーマンス向上に関するレコメンドなどは運用コストの大幅な削減に繋がると感じました。MA部では、月1回程度の頻度でアップデートの確認や対応をしています。地味に運用コストが結構掛かってしまったり対応漏れがあったりするので、Database Centerの活用でこういった課題の解決ができそうだと感じました。Database Migration Serviceは、データベース移行における課題やタスクをほとんど解消してくれるサービスなので、もし機会があればぜひ利用したいと思いました。 ベクトル検索に対応したデータベースプロダクト 「 What's next for Google Cloud databases in the gen AI era 」の5:04より引用 まずベクトル検索に関して簡単に紹介します。一般的なキーワード検索は、ある単語が文字列に含まれているものを検索する方法です。文章内に検索対象のキーワードが含まれていないと検索されません。一方でベクトル検索は、キーワードをベクトル(数値)に変換して、そのベクトルの類似性を利用した検索する方法になります。検索対象のキーワードが含まれていなくても検索されます。 これまでAlloyDB, Cloud SQL for PostgreSQLがベクトル検索に対応していましたが、これに加えて下記のデータベースプロダクトでもベクトル検索に対応予定です。 Cloud SQL for MySQL Spanner Firestore Bigtable Memorystore for Redis これにより、Google Cloudが提供するあらゆるデータベースプロダクトで最新データを利用してベクトル検索が可能になります。これによりリアルタイム性が求められるプロダクトでデータベースの選択肢が増えたと感じました。 MA部ではリアルタイムマーケティングシステムを運用しています。詳しくは以下のテックブログで紹介しています。 techblog.zozo.com 今回のベクトル検索の対応により、上記のテックブログで紹介したイベント検知とユーザー抽出の領域や最適化の領域において、将来的にベクトル検索を利用するアプローチが考えられ非常に興味深かったです。 このセッションでは、デモやユースケースの紹介もあり、どのように開発や運用に活用されるかを具体的な例を用いて非常にわかりやすく紹介されていました。運用に関しては、よくある社内でのケースを元にDatabase Centerがどのように活用できるのかが紹介されていました。特に自然言語からSQLを生成し実行するアプリケーションのデモは非常に興味深かったです。ぜひセッションのアーカイブ動画をご覧ください。 cloud.withgoogle.com Cloud Run: What's new "Cloud Run: What's new"のセッションからいくつか紹介します。 Volume Mounts 公開資料「 Cloud Run: What's new 」のP.9より引用 Cloud RunにおいてNFSとCloud StorageのVolume Mountsがサポートされます。これにより、Cloud Run上でのファイルの読み書きが可能になります。 これまでのCloud Runではファイルの読み書きが インメモリのファイルシステム だけでしたが、この機能が追加されることでメモリを気にせずファイルの読み書きが可能になります。この機能により、Cloud Run上で構築できるアプリケーションの幅が広がると考えられます。 Automatic Security Updates 公開資料「 Cloud Run: What's new 」のP.10より引用 デプロイされたimageのbase imageのセキュリティアップデートが自動で適用されるようになりました。 ダウンタイムはなく、リビルドの必要もありません。セキュリティインシデントに対して48時間以内にアップデートが自動で適用されるので、セキュリティのリスクを最小限に抑えられます。App EngineとCloud Functionsで運用されてきた機能のようで、Cloud Runでも利用できるようになります。 Gemini in Cloud Run Recommendations 公開資料「 Cloud Run: What's new 」のP.12より引用 Cloud Runのサービス一覧のページでGeminiがサポートされます。今までもレコメンドは表示されていましたがGeminiを利用することで、チャットを通してより積極的に利用してほしいと考えているようです。例えば、コストについての推奨事項があった場合に「より効果的なコストパフォーマンスを提供するための構成」などGeminiに質問ができるようになります。 Application canvas 公開資料「 Cloud Run: What's new 」のP.19より引用 Application canvasを使うことで、システムアーキテクチャをWebコンソール上で簡単に設計できるようになります。設計された各サービスの起動や接続に必要なロールの設定などを画面上で全て設定できるようになるサービスです。特にシステム構築の初期段階でアーキテクチャ図を作成する際にこちらを利用することで、そのままプロトタイプも開発できるようになるので非常に便利なサービスだと感じました。 これだけでも非常に便利なのですが、Geminiに自然言語で「こんなことができるアプリ」のように問いかけると適切なアーキテクチャを構築してくれます。セッションのデモで実際に自然言語で問いかけを行って、アーキテクチャの生成からアプリケーションのデプロイやアーキテクチャを修正する一連の流れが紹介されていました。 MA部では、現在運用しているマーケティングに関するシステムのリプレイスを行うZMPというプロジェクトを進めています。詳しくは以下のテックブログで詳しく紹介しています。 techblog.zozo.com その中で、Cloud RunはAPIや管理画面を構成する際に利用しています。Gemini in Cloud Run Recommendationsが利用できるようになると運用や改善点を発見するのに非常に役立ちそうだと感じました。私たちのチームでは、プロジェクトが進行していく中でリプレイスするシステムが増えていきます。Application canvasは、そこでシステム構築を検討する初期段階でアーキテクチャ図を作成する際に非常に便利だと感じました。自然言語で問いかけるだけでよいのも非常に便利です。 What's new with BigQuery 続いて"What's new with BigQuery"のセッションからいくつか紹介します。まず、このセッションでは様々な機能がBigQueryに統合され「AIに対応した単一のデータ分析プラットフォーム」であることが強調されていました。統合(unified)というキーワードがとても多く使われていたのが印象的で、新機能も様々なサービスとの連携強化やプラットフォームとしてより使いやすくなるような機能が多く紹介されていました。 公開資料「 What's new with BigQuery 」のP.12より引用 以下では、BigQueryの新機能の中から特に興味深かったものを紹介します。 Continuous real-time analytics in SQL 公開資料「 What's new with BigQuery 」のP.19より引用 この機能は、ストリーミングデータに対して継続的にSQLを実行できるようになるといったものです。 今まではDataflowを利用することが多かったですが、この機能によって選択肢が増えると思いました。BigQuery Studioなどから簡単に利用できるのであれば、Dataflowを構築する手間が省けるので非常に便利だと感じました。 BigQuery data canvas 公開資料「 What's new with BigQuery 」のP.27より引用 BigQuery data canvasは、自然言語を利用してデータ分析とビジュアライゼーションの作成が可能になります。 特に非エンジニアのデータ分析のハードルが大きく下がると感じました。 BigQuery data preparation 公開資料「 What's new with BigQuery 」のP.28より引用 BigQuery data preparationは、AIの支援を受けながらデータのクレンジングや変換が行えます。 AIがデータ変換に対してレコメンドしてくれる点が非常に便利そうだと感じました。大量にデータがあった場合に、意図しないデータや想定外のデータが含まれているときの警告を出したり変換を提案したりしてくれるようになります。以前データ分析をした際に、意図しないデータが入っていることで予想と違う結果になってしまい分析結果を検証しなければならないケースがありました。この機能を利用することで、そういった課題を解消できると考えられました。 BigQuery Workflows 公開資料「 What's new with BigQuery 」のP.25より引用 BigQuery Workflowsは、BigQuery Studioから簡単にワークフローを作成できる機能です。 Webコンソールからノーコーディングでワークフローを構築できます。構築されたワークフローはスケジュール実行ができるだけでなく、Cloud DataformやCloud Composer用にエクスポートできるようです。これはデータマートの作成や集計などで利用できそうなイメージが湧きました。BigQuery data preparationやBigQuery data canvasと組み合わせることで、データの前処理から分析、ビジュアライゼーションまでを一貫して行うことができると感じました。 What's next data analytics in the AI era こんにちは、MA部MA施策推進ブロックの @da-sugi です。私のパートでは、BigQueryで新たに発表があった機能を活用したデータ分析の進化について紹介します。 こちらのセッションでは、他セッションでも紹介されたBigQueryの新機能を前半で紹介しつつ、後半では、Geminiを活用したデータ分析のデモが行われました。AIに対応したBigQueryによって、スピーディーな分析と意思決定がいかに実現可能か、デモを交えて発表していました。会場の雰囲気としては1つ1つのプレビューの発表でも拍手が出るくらい、盛り上がっていました。 この章では、主にデモについて紹介します。新機能はデモに関連するものについてだけ紹介します。 BigQuery Studio (GA) 「 What’s next for data analytics in the AI era 」の24:09より引用 BigQuery Studioはデータの探索、分析、可視化、および共同作業を行うためのツールで、SQL、Python、自然言語などを使用してデータにアクセスできるデータ分析プラットフォームです。 わずか7 ~ 8か月前のGoogle Cloud Next '23でプレビューとして発表されたばかりですが、今回のGoogle Cloud Next '24で一般提供となりました。 BigQuery integration with Vertex AI for multimodal AI (GA) 「 What’s next for data analytics in the AI era 」の29:17よりスライド部分を強調して引用 BigQueryとVertex AIの統合によってマルチモーダルAIを実現できることが発表されました。 この統合により、BigQueryに保存されているデータを使用して、マルチモーダルデータを処理し、Vertex AIで機械学習モデルをトレーニングすることが可能になります。 Vector search in BigQuery (preview) 「 What’s next for data analytics in the AI era 」の31:59より引用 BigQueryにおける「ベクトル検索(Vector Search)」のプレビューが発表されました。 特定のカラムにベクトルデータが保存されており、そのデータをクエリして類似性の高いベクトルを持つレコードを見つけ、これにより画像検索など様々な分野で類似性検索を効率的に行うことが可能となります。 Gemini in BigQuery(preview) 直感的なインタフェースを使用してデータを探索し、インサイトを抽出できるGemini in BigQueryのプレビューが発表されました。Geminiを使用すると、SQLクエリを記述することなく、データセットの傾向やパターンを視覚的に理解できるようになります。またBigQuery Data Canvasを使うことで、インタラクティブな操作や自然言語でのデータ検索を共同で作業でき、その結果をビジュアル化して分析・共有できます。 デモ GeminiとLookerを使用して、オンラインファッションECサイトのデータを分析するデモが行われました。画像は、カスタムLookerアプリケーションで作成したもので、このリッチなレポートはGeminiとLookerで、なんとわずか数分で構築されたものだと言っていたのには驚きました。 「 What’s next for data analytics in the AI era 」の36:08より引用 実際にデモを通して、ビジュアライズに必要なデータをどのように構築されたのかを見ることができました。 まずデータ分析までの重要な3つのステップについて説明がされました。 「 What’s next for data analytics in the AI era 」の37:12より引用 ソーシャルメディアデータなどの複数のソースから取り込まれたデータを、全てまとめて分析できるようにGeminiとBigQueryを使用して変換 BigQuery Data CanvasとBigQueryのツールを使用して、トップトレンドの商品を発見・分析し、チャートを作成 BigQuery Vector Searchを使用して、トップトレンドの商品に似ている商品を見つける 1. GeminiとBigQueryを使用したデータのクリーニング クリーニング前のデータ 「 What’s next for data analytics in the AI era 」の38:14より引用 クリーニング作業 「 What’s next for data analytics in the AI era 」の38:25と38:49よりスライド部分を強調して引用 右側に表示されたAI生成コードの適用をクリックするだけで、瞬時に投稿日のフォーマットを整え、商品名のみが抽出されました。 クリーニング後のデータ 「 What’s next for data analytics in the AI era 」の38:54より引用 2. BigQuery Data Canvasを使用したトップトレンドの可視化 自然言語でのデータ検索と分析 Data Canvasを使用することで、自然言語でデータの検索ができ、即座にテーブルを作成できていました。 「 What’s next for data analytics in the AI era 」の39:43より引用 そのデータを元に、GeminiとBigQueryが自然言語からSQLを作成してデータ分析を可能にし、またVISUALISEから、わずか数秒でチャートだけでなくテキストのインサイトを作成していました。 「 What’s next for data analytics in the AI era 」の40:03より引用 「 What’s next for data analytics in the AI era 」の40:31より引用 3. BigQuery Vector Searchを使用した商品の類似性検索 Vector SearchはBigQuery内で、テキストから画像、動画からテキストなど、様々なデータ形式の類似性検索を行えます。これを使用して、デモではトップトレンドの商品に似た商品を検索していました。 「 What’s next for data analytics in the AI era 」の41:30より引用 4. MLの活用 また、BigQueryの機械学習を使用することで、トップ5の商品の今後1年間の販売予測もすぐに可能でした。これもGeminiとの対話によって実現されています。 「 What’s next for data analytics in the AI era 」の42:12より引用 セッション内のほとんどの作業が、単一のアプリケーション上で自然言語での対話によって実現可能であるのは非常に便利だと感じました。AIを活用したデータ分析がどれほど進んでいるのかを再認識できました。 ZOZOでは、BigQueryの新機能(GeminiとVector Search、Vertex AI)を組み合わせて、デモにあったように簡単な手順で様々な指標でのデータ分析が可能になり、次にどんな施策・配信をするのかなどの意思決定をよりスピーディーに行えるようになると思いました。 またMA部としては、前章(What's new with BigQuery)でも紹介があったSQLの継続的な実行(Continuous real-time analytics in SQL)と組み合わせ、最新のトレンド・販売予想などのデータを利用した配信も可能になるので、開発・運用しているマーケティング関連のシステムの改善や新規機能にも活用していけそうだと考えています。 What's new with IAM こんにちは、MA部MA開発ブロックの佐久間です。私からは "What's new with IAM - from least privilege to organization policies and AI-powered assistance"のセッション内容について紹介します。 私は普段バックエンドエンジニアとして開発・運用業務を行っていますが、IAMというセキュリティに関係する部分において、AI/MLがどのように機能していくのか興味がありました。管理者だけではなく、私のようなアプリケーション開発者の目線においても理解が深まる内容でした。 以降、IAMについての最新情報とともに、気になったトピックを紹介していきます。 Identity Provider こちらはGoogle CloudのIAM全体像です。 公開資料「 What’s new with IAM 」のP.6より引用 Identity Platformはその根底に位置し、多様なIdentityで構成されます。Identity Providerにはユーザーのものを使用できますが、以下の3通りの使い方があります。 【Cloud Identity】IdPをGoogleに同期して使用 【Identity Federation】Workforce Identity Federationを使用してGoogleに同期せず使用 【Mixed Mode】上記2つを合わせ、従業員の拡大や買収などで同期しきれない分にIdentity Federationを使用 今回、120以上の製品がIdentity Federation対象としてGAとなりました。一例ですが、Microsoft Power BIのEntra IDでBigQueryを利用できるようになっています。 Access Boundary 多層防御のアクセス管理として、IAMのGrant、Denyに加え、Access Boundaryが紹介されました。 公開資料「 What’s new with IAM 」のP.14より引用 こちらはアクセス可能なリソースの範囲、境界を制限するポリシーです。この境界には組織、フォルダー、プロジェクトといったレベルで定義できます。これらのポリシーはGrantを意味するのではなく、アクセス可能な最大の範囲を定義します。こちらは間もなくプレビュー版が公開されるそうです。 たとえ誤操作や誤認識で許可されてしまってもセーフティネットとして機能してくれそうです。 Privileged Access Manager Privileged Access Managerでは権限を資格として定義し、申請や承認の仕組みが利用できます。更新や承認が監査ログとして残る他、任意のEメールアドレスへ経過を送信できます。この度プレビューになりました。 公開資料「 What’s new with IAM 」のP.20より引用 Compute Adminなどの特権をそのまま付与するのではなく、資格として付与する権限や期間を定義できるため、一時的なトラブルシューティングのために1時間だけ払い出す、などの使い方ができます。そのため、作業後には意図しない権限が残り続けることはなく、不要な棚卸し作業から解放されそうです。 CIEM (Cloud Infrastructure Entitlement management) Google Cloud-AWS間でCIEMがプレビューになりました。年内にはMicrosoft Azureとの連携が予定されています。 公開資料「 What’s new with IAM 」のP.25より引用 CIEMではマルチクラウド環境でのアクセス権限管理を可能にし、IAMロールの最適化についてもレコメンドを受けることができます。また、Chronicle SOARというセキュリティプラットフォームに統合することで、発覚した過剰な権限がどのような脅威になるのかやその修復方法がわかり、JIRAチケットの自動発行なども可能になります。Chronicle SOARの詳細については以下のドキュメントをご覧ください。 cloud.google.com セッションでは過剰な権限付与がどれほど一般的に行われているかが説明され、最小権限の原則の重要性を強調されているようでした。 Resource Configuration 既に110以上のビルトインポリシーが、ガードレールのようにデフォルトで有効になっていますが、さらに組織レベルでのカスタムポリシー作成がGAとなりました。 公開資料「 What’s new with IAM 」のP.29より引用 例えば、GKEクラスターの作成にはバイナリ認証を有効にする必要がある、などを組織のポリシーとして定義できるようになります。 バイナリ認証が有効になっていると、検証環境で合格したイメージのみが本番環境にデプロイされることを担保できるなど、開発者にとってもうれしいポイントです。 Gemini Cloud Assist 最後に、Geminiによるアシストについての紹介です。こちらは間もなくプレビューになるそうです。 公開資料「 What’s new with IAM 」のP.37より引用 IAMやRoleに10,000もの推奨事項があるとして、必ずしも全てに対応する必要はないはずです。そこでGeminiが何から取りかかれば良いか、優先すべき事項を提案してくれます。また、あるサービスアカウントが最後に使われたのはいつだったのか、複雑なアクセスポリシーからどんな権限で拒否されているのかなどについても、自然言語で質問できるようになります。 やはり自然言語でどんな相談にも乗ってくれるのは、初学者や管理者などあらゆる立場の人にとって頼もしい存在に感じられました。ただセキュリティに関する部分なので、AI/MLが修復を推奨しなかったものはどのようなものなのか、なぜ推奨に至らなかったのかなどにも個人的には注意しようと思います。 まとめ 今年のGoogle Cloud Next '24は、去年同様にAIに関するセッションがとても多かったです。 去年はAIの可能性や今後についての観点でのセッションがメインだったと感じましたが、今年は進化したAIと実際の利用事例の紹介や今後Google CloudにどのようにAIが組み込まれ、進化していくかという内容が多かったと思います。 セッションだけでなく、世界中の企業が集まる企業ブースでAIの活用事例や現場のエンジニアとコミュニケーションを通して、本当にいろいろなところでAIが活用されているとを知ることができました。 この一年でAIが進化する速度の凄まじさを感じるとともに、次の一年でどこまで進化するのかが楽しみになりました。 紹介したセッション以外にもたくさんの興味深い発表がありました。全てのセッションは参加登録すれば公式サイトの Session Library から視聴できます。ぜひご覧ください。 最後に カンファレンス参加に伴う渡航費や宿泊費は 福利厚生 のひとつであるセミナー・カンファレンス参加支援制度によって全て会社負担です。 ZOZOでは一緒にプロダクトを開発してくれるエンジニアを募集しています。ご興味のある方は下記リンクからぜひご応募ください! corp.zozo.com
はじめに こんにちは。検索基盤部 検索技術ブロックの今井です。 検索基盤部では検索機能や検索精度を改善する中で検索クエリの意図解釈にも取り組んでいます。ZOZOTOWNで検索窓にクエリを入力して検索ボタンを押すと、クエリに応じて検索の絞り込み条件に変換するクエリ解釈機能の処理が動作します。 例えば、「ワンピース 白色」と検索した時、「ワンピース」を洋服のカテゴリー、「白色」を色のカテゴリーと解釈し、「白色のワンピース」を検索する絞り込み条件に変換します。 2024年5月現在ではスマートフォン向けWebサイト( https://zozo.jp/sp/xxx )とアプリのみ、クエリ解釈機能の処理が適用されています。クエリ解釈機能では意図解釈や検索の絞り込み条件に変換しています。 現在はシンプルな辞書ベースの手法を用いていますが、カバーしきれない課題も出てきており、改善のモチベーションが少しずつ上がってきています。本記事ではこれまでのクエリ解釈の取り組みについてシステム面も含めて紹介します。 目次 はじめに 目次 クエリ解釈について 従来のZOZOTOWNのクエリ解釈機能 導入背景 アーキテクチャ クエリ解釈機能のロジック 課題 クエリ解釈APIへのリプレイス 辞書生成バッチについて おわりに クエリ解釈について クエリ解釈は、検索の精度改善を目的として、検索者が入力したクエリの意図を解釈し、検索条件に変換します。クエリ解釈機能は以下のような処理フローで実現され、検索者の意図に沿った検索結果を返すことを目指しています。 ZOZOTOWNでは一部のみを導入し、全ての処理の導入までは至っていませんが、いずれは全て導入してクエリ解釈を強化したいと考えています。 以前弊社のテックブログ「 ZOZOTOWN検索の精度改善の取り組み紹介 」でも紹介している下記の外部記事で詳しく解説されていますのであわせてご参照ください。 Daniel Tunkelang: Query Understanding 検索体験を向上するQuery Understandingとは ※クエリ解釈は英語で query understanding (wikipedia) と呼ばれています。 従来のZOZOTOWNのクエリ解釈機能 ZOZOTOWNには10年ほど前からクエリ解釈機能が導入されていました。 導入背景 導入に至った背景としては以下2つの観点がありました。 検索の精度改善の観点 検索エンジンに対してキーワードマッチで検索するよりも、特定カテゴリのID等で検索結果を絞った方が検索精度の向上が期待できるため SEO観点 キーワード検索の検索結果ページに遷移するよりも、ブランドページやカテゴリページに遷移した方がSEO観点で良いとされるビジネス的な要件のため これらの観点が元となって導入された機能は以下です。 クエリ文字列から特定の絞り込み条件に変換する仕組み 特定ページへのリダイレクトURL構築(Web用) アーキテクチャ クエリ解釈機能が導入されたシステムアーキテクチャは以下です。この機能はClassic ASPで実装されています。 クエリ解釈機能のロジック 先述の通り、クエリ解釈機能で用いる手法はシンプルな辞書ベースの変換手法を用いています。以下は辞書のイメージです。 ターム 意図エンティティタイプ 意図エンティティ名 意図エンティティID ジャケット category "jacket" 1 パンツ category "pants" 2 zozo brand brand "ZOZO BRAND" 11 zozo shop shop "ZOZO SHOP" 21 zozo brandshop brand "ZOZO BRANDSHOP" 12 shop "ZOZO BRANDSHOP" 22 ... 検索条件の構築までの流れは以下です。タームによっては複数意図を持つタームも存在します。(e.g. 上記の表の"zozo brandshop") クエリ ジャケット を入力する 辞書内のターム一覧から ジャケット に完全一致でマッチした辞書エントリーを取得する 取得した辞書エントリーから カテゴリエンティティ(カテゴリ意図) であること、そのidが 1 であることを認識する 辞書エントリーによっては複数エンティティに紐づいていることがある 検索エンジンにリクエストする絞り込み条件として category_id に 1 をセットする 複数エンティティが存在する場合、優先意図の考慮や絞り込み条件同士の整合性が取れているかなどをチェックして一意に絞り込む 導入しているクエリ解釈機能の中には、以下3つの処理が含まれています。 処理 説明 クエリ分割(Query Segmentation) スペース区切りの文字列を意味のあるまとまりごとに扱うようにするために判定・分割する。 e.g. ・クエリ「ジャケット」→「ジャケット」としてセグメント化 ・クエリ「zozo brandshop 春服」→「zozo brandshop / 春服」として分割しセグメント化 クエリの属性の引当(Entity Recognition) セグメント化された各文字列がどの属性のタームなのかをエンティティとして識別する。 e.g. ・「ジャケット」→「カテゴリ:jacket(ID:1)」を属性として識別 ・「zozo brandshop 春服」→「ブランド:ZOZO BRANDSHOP(ID:12)」「ショップ:ZOZO BRANDSHOP(ID:22)」「キーワード:春服」を属性として識別 絞り込み検索条件の構築(Query Scoping) 識別されたタームを検索条件に変換する。検索エンジンに応じてクエリ要素へのマッピング内容が変わる。 e.g. ・「カテゴリ:jacket(ID:1)」→「カテゴリID:1」の絞り込み条件に変換 ・「ブランド:ZOZO BRANDSHOP(ID:12)」「ショップ:ZOZO BRANDSHOP(ID:22)」「キーワード:春服」→「ブランドID:12」AND「キーワード:春服」の絞り込み条件に変換("zozo brandshop"をショップではなくブランド条件として検索する場合) 課題 現行の辞書ベースの手法には、クエリ解釈精度面の課題があることが分かりました。特に、辞書にマッチしたタームが以下のケースに該当する場合は、変換を控える必要があります。 誤変換されるケース 複数の変換候補があるにもかかわらず、特定の候補に変換することがある この誤変換によって、他の変換候補の検索結果が表示されなくなる問題も発生 同音異義語が存在するケース ワンピース(カテゴリ)、ワンピース(漫画)は、カテゴリに変換するとワンピース(漫画)関連の商品が表示されなくなる 文字列長が短いケース 1文字、2文字など短いタームを変換してしまうため、検索者が意識せずたまたま入力したタームに対して誤ったまま変換してしまう可能性がある これらの問題が発生した場合は1件ずつアドホックに対応してきました。ただし、このままだとアドホック対応をいつまでも続ける必要があるため、根本解決する必要があります。 根本解決に向けて進めるために、まずはレガシーな実装の問題を解決するところから進めようと考えました。現行のクエリ解釈機能を保守運用する中で以下のアーキテクチャ面の課題が出てきました。 APIの管轄部署が検索基盤チーム以外であったため、改修や新機能の検証を気軽に出来なかった 実装がレガシー Classic ASPで実装されていたため、新規参画者に開発・保守運用の経験が少なく、学習コストが高かった モノリシックなレガシーAPIの1機能となっているため、改修による他影響を考える必要があった テストコードがないため、リグレッションに対して注視する必要があった アドホック対応 現状上記のような問題が発生したときに都度対応している ビジネス要件での変換依頼に対応することが時々ある 改善施策を施しABテストを実施するサイクルが回せていない これらのアーキテクチャ面の課題を解決すべく、まずはクエリ解釈APIのリプレイスを実施し、変換の課題を解決するための環境を整えました。次項では、そのリプレイス対応について説明します。 クエリ解釈APIへのリプレイス これらの課題解決の第一歩として、ZOZOTOWNのモノリシックなレガシーAPIの中からクエリ解釈の主機能を切り出し、クエリ解釈APIとしてリプレイスしました。このリプレイスではまず既存仕様を踏襲することとし、今後改善しやすいアーキテクチャを構築することを最優先としました。 このAPIは弊社技術スタックの推奨言語の1つであるGo言語で開発しました。採用理由としては、高速に動作することやGo言語開発の経験者による開発スピードの向上が期待できることなどが挙げられます。 リプレイス前後のシステムイメージは以下です。 ※ 辞書ファイルのレコード数は大規模ではないためKubernetesのPodに内包する形式を採用。そのため外部通信が発生せず安定かつ高速に動作。 クエリ解釈APIとして切り出すことで以下の恩恵が得られました。 機能追加、保守運用のし易さ リプレイス前は必要最低限の変更しか行われていなかったが、リプレイス後は問題の特定や影響範囲が分かりやすくなったこともあり定期的に改善が行われるようになった これにより「実装がレガシー」と「APIの管轄部署が検索基盤チーム以外であったため、改修や新機能の検証を気軽に出来なかった」の課題が解消された パフォーマンス向上 リプレイス前のAPIが高速でなかったこともあったが、リプレイス前後で比較すると10倍以上高速に動作 辞書登録する内容を一部見直し、リプレイス前は検索時に逐次DB問い合わせして取得していた情報をリプレイス後では予め辞書登録しておくようにしたことも効果的であった 今回のリプレイスにより以下の課題を解消できました。 実装がレガシー APIの管轄部署が検索基盤チーム以外であったため、改修や新機能の検証を気軽に出来なかった ただし、「アドホック対応」と「改善施策を施しABテストを実施するサイクルが回せていない」の課題の解消にはまだ至っていないため、引き続き解消に向けて取り組んでいきたいと思います。 辞書生成バッチについて 辞書ファイルは辞書生成バッチで生成していましたが、このバッチもClassic ASPで実装されていました。クエリ解釈APIのリプレイスに伴いバッチ側も同様にリプレイスを行いました。 具体的には、ワークフローエンジンに Vertex AI Pipelines を採用しPythonでバッチ処理を実装しました。 Classic ASPからVertex AI Pipelinesに乗り換えたことでワークフローエンジンとしての基本的な機能を得ることができ、保守運用を行いやすくなりました。以下は基本的な機能の例です。 バッチ処理内のタスクの依存関係を定義できる 処理途中で失敗したタスクからリトライできる backfillが容易になる データソースにはGoogle BigQueryとカスタマイズCSVファイルを利用します。Google BigQueryからはブランドやショップ、カテゴリなどの情報を取得します。これらのデータを用いて辞書ファイルを作成しGoogle Cloud Storageにアップロードします。 Google Cloud Storage上の辞書ファイルは、クエリ解釈APIのDockerコンテナイメージを作成するタイミングでダウンロードし内包しています。このDockerコンテナイメージからKubernetesのPodを作成・起動することで、外部通信が発生することなく安定かつ高速に動作します。 おわりに 本記事では、ZOZOTOWNでのこれまでのクエリ解釈の取り組みについて紹介しました。 クエリ解釈機能を従来のClassic ASPの実装からAPIとして切り出してGo言語でリプレイスしました。リプレイスではこれまでの仕様を踏襲したため依然として残っている課題も多々ありますが、切り出したことで改善しやすい状態に整えられました。 現在、クエリ解釈の次ステップの試みとして誤変換に対する課題を解決する手段などを検討・分析しています。この取り組みについても紹介できるようになり次第共有したいと思います。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。SRE部フロントSREブロックの三品です。 3月19日から3月22日にかけてKubeCon + CloudNativeCon Europe 2024(以下、KubeCon EUと呼びます)が行われました。今回弊社からはZOZOTOWNのマイクロサービスや基盤に関わるエンジニア、推薦システムに関わるエンジニアの合わせて4人で参加しました。 本記事では現地の様子や弊社エンジニアが気になったセッションや現地の様子について紹介していきます。 目次 KubeConEU2024の概要 セッションの紹介 現地の様子 ブースについて 参加に向けてのTips 最後に KubeCon EU 2024の概要 昨年4月にオランダ アムステルダムで行われたKubeCon EUの様子については昨年の参加レポートをご覧ください。 techblog.zozo.com 今年のKubeCon EUはフランスのパリで開催されました。昨年まではコロナ禍の影響もありオフラインとオンラインのハイブリッドで開催されていました。 しかし今年はオフラインに統一され12,000人以上が現地で参加しており、今年もKubeCon EU史上最大の参加人数を更新しました。 キーノート会場の様子 KubeCon EUではキーノートやセッション、LTなどを通してKubernetesに関する最新のアップデートの紹介や実際にKubernetesを採用した企業の幅広い運用ノウハウを聞くことができます。 以降では参加してきた社員がそれぞれ気になったセッションや現地の様子について取り上げてご紹介します。 セッションの紹介 セッションタイトル Tutorial: Cloud Native WebAssembly and How to Use It - Brooks Townsend & Michael Yuan Cloud-Native LLM Deployments Made Easy Using LangChain Strategies for Efficient LLM Deployments in Any Cluster Why Is This so HARD? Conveying the Business Value of Open Source Kubernetes Maintainers Read Mean Comments To Infinity and Beyond: Seamless Autoscaling with in-Place Resource Resize for Kubernetes Pods Comparing Sidecar-Less Service Mesh from Cilium and Istio Future of Intelligent Cluster Ops: LLM-Azing Kubernetes Controllers Is Your Image Really Distroless? - Laurent Goderre, Docker Building Confidence in Kubernetes Controllers: Lessons Learned from Using E2e-Framework - Matteo Ruina, Datadog & Philippe Scorsolini, Upbound Tutorial: Cloud Native WebAssembly and How to Use It - Brooks Townsend & Michael Yuan ML・データ部MLOpsブロックの松岡です。 私の所属するMLOpsブロックではMLを使用した様々なサービスのプラットフォームを運用しています。MLにおいて近年話題になっているのがLLM(大規模言語モデル)とLLMを使った生成AIです。KubeCon EUでもLLMについてのセッションが多くありました。 その中でも特に興味深かったのがWasmを紹介した次のセッションです。 Wasmについての概要 LLMを実用化するにあたって重要な懸念点の1つがサービスがスケールする際のコストの問題です。 それを解決するためにユーザーのCPUやGPUで高速にプログラムを動かすことができる WebAssembly(Wasm) が注目されています。 このセッションでは、Wasmの概要から入り、実際にWasmを使ってLLMを動かしChatbotを構築する方法を紹介していました。説明が幅広く網羅的でかつ具体的でわかりやすかったです。 Wasmはブラウザ上で高速にプログラムを動かすために登場したバイナリコード形式のプログラミング言語です。 アセンブリと言う名前ですが、コーディングにあたってアセンブラ言語を記載する必要はなく、 C言語 や Rust など様々な高級言語からWasmバイナリにコンパイルできます。 コンパイル済みのコードは純粋な機械語ではなく抽象化された機械語となっており、Wasm Runtimeが各環境に応じた形へ変換し実行します。この仕組みによりJavaのように単一のコードで異なるOSやCPUアーキテクチャをサポートします。 当初この技術は3D処理などをブラウザ上で実行する用途として注目されていましたが、最近ではMLの推論をエッジで実行する用途にも注目されています。そしてWasmが小さなVMのようなものであることを応用しサーバーサイドにおけるDockerの置き換えとしても注目されているようです。 Wasm RuntimeによりCPUのアーキテクチャが抽象化される 「 Cloud Native Wasm And How To Use It 」より引用 WASIでWasmの利用用途が広がる このセッションではWASI(WebAssembly System Interface)0.2.0についても説明されていました。 WASIは、Wasmが直接サポートしていない標準入出力への読み書きや、ファイルシステム、ソケットへのアクセスなどアプリケーションに必要な機能をWasmから使用するのに使われています。これによりWasmはブラウザだけでなくサーバーサイドでの利用も可能になりました。 これらのインタフェースを標準化し、各Wasm Runtimeにてサポートすることで、実行環境が抽象化されコンテナ標準のように使用できるようになることがアナウンスされていました。 Wasmをコンポーネントとして扱い、コンポーネントを統合して新たなコンポーネントを作るアイデアが紹介されていました。それぞれのコンポーネントはGoやJavaなど様々な言語で記載可能です。コンパイラーがWasmのバイナリに変換します。コンポーネント間の入出力はWASIによるインタフェースで定義されます。 WASIは WIT(Wasm Interface Types) に基づく高度な型システムを利用できます。セッションではWITの型を一瞥できる wit-cheat-sheet が紹介されていました。 各コンポーネントはメモリー空間が独立しておりWASI以外には依存しないことで高い独立性を保ちます。そして、これらのコンポーネント間の呼び出しはナノ秒で行うことができるとのことです。WASIにより入出力が定義されているため、コンポーネント間は言語を揃える必要すらありません。Rustで書いたコンポーネントとGoで書いたコンポーネントを組み合わせて新しいコンポーネントを作るといったことが可能なようです。これはコードの再利用性を高め、システム構築の言語選定時に大きな自由度を与えてくれることになります。 異なる言語のコンポーネントがWASIでつながる 「 Cloud Native Wasm And How To Use It 」より引用 Wasm Edge Runtimeにより多くの環境でバイナリを高速に動かすことができる Wasm Edge Runtimeでの実行速度についての説明では、Wasmがネイティブの速度を超える事例について紹介していました。 sometimes faster than nativeについて 「 Cloud Native Wasm And How To Use It 」より引用 これは、特定の環境に最適化されていないネイティブバイナリーコードよりは、実行環境ごとに最適化されたWasmのほうが高速になりえるという話です。特定の環境に最適化されたネイティブバイナリーコードよりWasmのほうが速いというわけではないので注意してください。WasmはWasm Runtimeにより実行環境に応じて最適化されて実行できる利点がここでは主張されています。 また、プラグインにより機能を提供することで最小の機能を保ちながら必要な機能を追加していく事が可能であること、一例としてガベージコレクションをプラグインで提供したことが紹介されていました。 このガベージコレクションはKotlinからの強い要望があったそうですが、Kotlinの実行環境としてJVMだけでなくWasmという新しい選択肢が出てくることは大変興味深いです。 Cloud NativeにおけるWasiの有用性 WasmをKubernetes上で動かし、 Linuxコンテナーを置き換えるものとするアイディア も紹介されていました。 マイクロサービス化において、オーバーヘッドとなるLinux部分をWasmのモジュールへ置き換えることでコストの削減とスループットの向上につながる可能性があります。Wasmのモジュールはミリ秒で起動します。これはLinuxコンテナを起動するよりはるかに高速です。またWasmのモジュールが必要なメモリー量はLinuxコンテナよりも少ないです。そのため、Linuxコンテナの代わりにWasnのモジュールを用いることでコスト・運用ともに大幅な改善が期待できるとのことです。 また、Wasmはアーキテクチャに依存しないことも魅力的です。例えば私は普段Apple SiliconのMacBook Proで作業していますが、これにはArmをサポートするCPUとMetalをサポートするGPUが採用されています。ところがサーバーサイドでは一般的にx86をサポートするCPUとCUDAをサポートするGPUを使用することが多く、両環境でコンテナイメージを共有できませんでした。 Linuxコンテナイメージに変わって、Wasmコンポーネントを使用することでこの差は抽象化され両環境で一貫したバイナリを実行できるのはとても魅力的に感じます。フロントエンド寄りの技術だと思っていたWasmがCloud Nativeで大きな変革を起こす可能性があることは今回のセッションでの良い発見でした。 Wasmについて深く調べてみたくなりました。 LLMをエッジで動かすチュートリアル セッション後半では実際にWasmを使用して簡単な Hello World! を作成した後、より実践的なデモとして端末のGPUを使ってLLMモデルを動かしChatbotを実行するハンズオンが行われました。 LLMを使った生成AIは大量のコンピュータリソースを使用することがネックとなっています。WasmによりユーザーサイドのGPUを使用することでこの問題を解決できるかもしれません。このハンズオンのユースケースもまたWasmが今後大きく注目される部分だと思います。 Wasmは Cloud Native Wasm Day Hosted by CNCF においても詳しく紹介されているためそちらも合わせて見られることをおすすめします。 この他にもさまざまな魅力的なセッションがあり、全てを紹介できないので、いくつかをピックアップします。 Cloud-Native LLM Deployments Made Easy Using LangChain このセッションではLangChainを使用したクラウドネイティブなLLMのデプロイ方法について段階を追って説明されていました。モデルをデプロイするための手順として使用するモデルの定義、モデルの実行方法についての検討、LLMのパッキング、モデルのコンテナ化、複数モデルの統合の5段階で説明されていました。ML、特にLLMについてはモデルの作成だけでなく生成もコストが大きいこと、その結果に曖昧性が残ることなどから多くの項目を検討する必要があることを考えさせられました。 Strategies for Efficient LLM Deployments in Any Cluster 利用用途に応じたモデル選定について解説したセッションです。コストの大きなLLMsだけでなく必要に応じてSMLs(Small Language Models)=小規模言語モデルを選ぶメリットなどを紹介しています。 モデルの得手不得手を理解して、複数モデルの使い分けを意識したいと思います。 Why Is This so HARD? Conveying the Business Value of Open Source オープンソースプロジェクトの価値をいかに示すか解説したセッションです。 GitHub のIssueやPull Requestへラベルを付けて進捗を可視化する方法など、オープンソースに限らずプロジェクト管理で幅広く活用出来そうなアイデアが紹介されていました。 Kubernetes Maintainers Read Mean Comments KubernetesのIssueに投げられた意地悪なコメントについて紹介されていました。技術的な情報ではありませんが、コミュニティーがどのように機能しているかを理解し、円滑なコミュニケーションを推進する方法について示唆がありました。 To Infinity and Beyond: Seamless Autoscaling with in-Place Resource Resize for Kubernetes Pods KubernetesでPodsを運用するうえで悩みの種であるスケーリングについて、スケーリングがどのように機能するか、新しいin-Place Pod Resizingについて紹介されていました。 スケーリングにおける副作用についての学びが大きかったです。 Comparing Sidecar-Less Service Mesh from Cilium and Istio ML・データ部MLOpsブロックの 岡本 です。 Solo.ioのChristian Posta氏による、 Cilium と Istio Ambient Mesh を使ったサイドカーレスなサービスメッシュ構成の比較についてのセッションをご紹介します。本セッションのメインテーマでは、サイドカーレスなサービスメッシュについて、CiliumとIstio Ambient Meshを使ったそれぞれの構成を次の観点で比較していました。 コントロールプレーン データプレーン 相互認証 / mTLS 可観測性 トラフィック制御 サイドカーとは、Kubernetes Pod内のアプリケーションコンテナに対し補助的な役割を持つコンテナです。サービスメッシュを構成する場合に、従来はKubernetesの各Pod内でアプリケーションコンテナとEnvoyコンテナを稼働するサイドカーモデルが取られていました。 サイドカーモデルで構成するサービスメッシュには、サービスメッシュを透過的に構成できることや他のワークロードにリソースが占有されてしまう(ノイジーネイバー問題)を回避できるなどの利点がありました。 サイドカーコンテナの利点 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 一方でサイドカーモデルにはいくつかの課題もありました。例えば、アプリケーションコンテナとサイドカーコンテナの競合によるPodの起動・終了シーケンスの複雑化や、サイドカーのアップグレード時にアプリケーションの再起動が必要になることなどです。 サイドカーコンテナの課題 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 サイドカーレスなサービスメッシュはこのようなサイドカーモデルでの課題を解決するために提案されました。 次にセッションのメインテーマであるCiliumとIstio Ambient Meshを使ったサイドカーレスなサービスメッシュ構成の比較のうち、データプレーン部分の違いについて簡単にご紹介します。 サービスメッシュのアーキテクチャは大きくコントロールプレーンとデータプレーンに分かれています。データプレーンでは、主にPod間の通信を制御するプロキシの役割を担っており、コントロールプレーンではデータプレーンを管理しています。 データプレーンにおける構成の主な違いは、CiliumではOSI参照モデルのL4処理をeBPFで実装しているのに対し、Istio Ambient MeshではZtunnelで実装しているという点です。eBPFとはextended Berkeley Packet Filterの略であり、Linuxカーネルのコードを変更することなく、動的にカーネルの機能拡張を行う技術です。CiliumではeBPFを利用することでネットワーク処理を効率化しています。一方でZtunnelとはzero trust tunnelの略であり、Istio Ambient Meshのために実装されたNodeごとのプロキシです。Istio Ambient MeshではZtunnelを利用することでwaypoint proxy(L7処理を行うコンポーネント)へ効率的かつ安全にトラフィックを転送できます。 CiliumサービスメッシュのL4 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 Istio Ambient MeshサービスメッシュのL4 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 サービスメッシュの機能をどのネットワークレイヤで実現し、どこで実装しているのか、CiliumとIstio Ambient Meshでのそれぞれの構成の比較は次のスライドをご参照ください。 CiliumサービスメッシュでのL4・L7の分離 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 Istio Ambient MeshサービスメッシュでのL4・L7の分離 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 発表の最後の部分では、次の観点についてそれぞれのサービスメッシュ構成が適しているかそうでないかについて述べられていました。 リソースのオーバーヘッド 機能の分離 セキュリティの粒度 更新時の影響 これらの観点について各構成はそれぞれ次のスライドで評価されています。 Ciliumサービスメッシュの評価 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 Istio Ambient Meshサービスメッシュの評価 「 Comparing Sidecar-less Service Mesh from Cilium and Istio 」より引用 Ciliumでは特にリソースオーバーヘッドを小さくしたいケースに適しており、機能の分離という観点ではあまり適していないことがわかります。Istio Ambient Meshでは特に機能の分離という観点で適している一方で、リソースオーバーヘッドについてはCiliumでの構成にやや劣ることがわかります。このようにサイドカーレスなサービスメッシュにおいてはいくつかの構成が考えられます。またそれぞれの構成にはトレードオフがあるとわかりました。 サイドカーレスなサービスメッシュはサイドカーモデルでの課題解決のために提案されているとご紹介しましたが、サイドカーモデル自体についても日々改善されています。Kubernetesのv1.29では Sidecar Containers の機能がbetaで提供され、明示的にアプリケーションコンテナとサイドカーコンテナが区別できるようになります。これによりサイドカーモデルでのサービスメッシュでは、セッション内で挙げられていたいくつかの課題の改善が見込まれています。 今後のサービスメッシュにおいてはサイドカーモデルまたはサイドカーレス、CiliumまたはIstio Ambient Meshなどユースケースに応じてどの構成を取るべきか判断が必要だと感じました。 Future of Intelligent Cluster Ops: LLM-Azing Kubernetes Controllers SRE部ECプラットフォーム基盤SREブロックの織田です。 1日目のKyenoteは、すべてがMLやAI、LLMに関するものでした。今回紹介するセッションもLLMを利用してマニフェストに記述したコマンドや文章を理解し、効率的に実行する LLMNETES の紹介です。 LLMNETESは、OpenAIのGPT-3を含む、複数のLLMをサポートしておりローカルのモデルもデータセットとして使用できます。また、独自のインタフェースを実装することで独自のLLMも使用できます。 次にマニフェスト例とそれらをApplyすることでどのようなことができるのか4つほど紹介します。 1つ目は、ポート80を公開したnginx Podを3台作成する例です。 spec.inputに実行したいことを記述するとそれ通りにリソースの作成などを実施できます。複雑でマニフェストの作成に時間がかかりそうな場合やPodをすぐに用意したい場合などに役立ちそうに感じました。 apiVersion : llmnetes.dev/v1alpha1 kind : CommandExec metadata : name : command1 spec : input : Create 3 nginx pods that will serve traffic on port 80. 2つ目にデプロイされているPodとDeploymentのイメージをスキャンする例です。 spec.typeにScanImages、spec.resourcesにスキャンしたいリソースを記述すると対象のリソースのイメージをスキャンできます。 CIからツールを使って定期的にスキャンする場合は、CIでツールのセットアップなどを行う必要があるため、マニフェストをApplyするだけでスキャンできるのは便利そうに感じました。 apiVersion : llmnetes.dev/v1alpha1 kind : ClusterAudit metadata : name : cluster-audit-cves spec : type : ScanImages resources : - Pods - Deployments 3つ目にカオスエンジニアリングのシミュレーションをトリガーする例です。 spec.commandに実行したいことを記述します。以下は、default namespaceのPodを削除する例となります。 apiVersion : llmnetes.dev/v1alpha1 kind : ChaosSimulation metadata : name : chaos-simulation-cr spec : level : 10 command : break my cluster networking layer (or at least try to) 最後は、クラスタ内の非推奨APIを検出する例です。 spec.typeにClusterUpgradeCheckを指定します。現在、非推奨APIを検出する方法は多く存在しますが、マニフェストをApplyするだけで検出できるのはとても魅力的に感じました。 apiVersion : llmnetes.dev/v1alpha1 kind : ClusterUpgradeCheck metadata : name : cluster-upgrade-check spec : type : ClusterUpgradeCheck 1つのツールでイメージのスキャン、カオスエンジニアリング、非推奨APIの検出など様々なことができます。そのため、実施したいことごとにツールをインストールせずシンプルな構成になることで、アップグレードなどの運用コストが下がるというメリットがありそうに思いました。 LLMNETESは、開発中でありまだ本番で利用するまでには至っていませんが、検証などを行いながら導入を検討していきたいと考えています。 Is Your Image Really Distroless? - Laurent Goderre, Docker SRE部フロントSREブロックの三品です。 私が所属するSRE部フロントSREブロックでは、ZOZOTOWNが持つAPIの中でもクライアントに近い、frontendレイヤーのサービスを運用しており、言わばEmbedded SRE的な業務を行っています。 今回KubeCon EUに参加して私が業務の中で活かせそうだなと思ったセッションをいくつか紹介します。 このセッションでは、私たちが使っているDocker ImageはDistributionの影響を受けない状態にできていますか?と言うことを投げかけていました。 セッションの内容を説明すると、最初にDocker Imageを作成するのに必要なLinuxのDistributionやセキュリティとユーザビリティのジレンマについて説明していました。 what is a distro? 「 Is Your Image Really Distroless? - Laurent Goderre, Docker 」より引用 Usability-Security Dilemma 「 Is Your Image Really Distroless? - Laurent Goderre, Docker 」より引用 次にDistrolessなDocker Imageを生成するためDockerfileを作成しbuildするための方法やツールが説明されていました。 Multi-stage builds 「 Is Your Image Really Distroless? - Laurent Goderre, Docker 」より引用 buildKit 「 Is Your Image Really Distroless? - Laurent Goderre, Docker 」より引用 その後、bashなどがメインコンテナに含まれることによっての危険性を危惧し、initコンテナを利用し安全に依存関係のファイルをインストールする方法についてdemoを交えて説明していました。 init containers to the rescue! 「 Is Your Image Really Distroless? - Laurent Goderre, Docker 」より引用 私は今までDistrolessについてあまり意識を向けたことがありませんでした。 しかし、セッションを通してbashなどがメインコンテナにインストールされていることで実際にコンテナに侵入された場合の危険性について再考できました。 セッション内でinitコンテナの利便性について触れていましたが、initコンテナを利用してmainコンテナに何かを注入する処理を行うと、container起動までに追加の時間を必要とする可能性があります。セキュリティとのトレードオフになるため、initコンテナを利用する際にはこの点を考慮する必要があると感じました。 コンテナをセキュアに扱うと言う意味ですごく意味のあるものだと考えられますので、今回取り上げました。 Building Confidence in Kubernetes Controllers: Lessons Learned from Using E2e-Framework - Matteo Ruina, Datadog & Philippe Scorsolini, Upbound このセッションでは、Kubernetesコントローラーの信頼性を高める手法としてエンドツーエンドテスト(以降、E2Eテストと呼びます。)を取り上げ、E2Eテストの基礎理念から始まって特に他社2社の実践例について取り上げていました。 本記事ではその中でも自分が個人的に気になった部分を抜粋して紹介します。 本セッションではKubernetesのE2Eテスト( e2e-framework )をベースに説明されていました。 ※ e2e-frameworkとは、Kubernetesクラスター内で実行されるコンポーネントのエンドツーエンドテストを行うためのGo製フレームワークです。Kubernetesリポジトリ上にも多くのE2Eテストの例が公開されています。 その後E2Eテストのツールの特徴について述べられていました。 Goals 「 Building Confidence in Kubernetes Controllers: Lessons Learned from Using E2e-Framework 」より引用 日本語で翻訳すると以下のようになります。 採用に役立つ文書化されたフレームワーク 組み込みGoテストパッケージを使用する テストを構成するためのプログラム可能なAPIコンポーネントを提供する クライアントの機能を抽象化するヘルパー関数を提供する Kubernetesへの依存を避ける Go E2E Test Framework for Kubernetes でも同じようなことが書かれているのでこちらもご覧ください。 次にプログラムの動きについて説明をしていました。基本的なプログラムの動きについて説明します。 Example code 「 Building Confidence in Kubernetes Controllers: Lessons Learned from Using E2e-Framework 」より引用 環境ごとの設定情報を持つ 環境ごとの設定情報を元に実際のテストを紐づける テストの実行 DatadogやCrossplaneのE2Eテストの事例紹介ではE2Eテストフレームワークの比較や実装方法、問題点が紹介されていました。本記事ではその中でもフレームワークの比較について取り上げたいと思います。 Alternatives we considered 「 Building Confidence in Kubernetes Controllers: Lessons Learned from Using E2e-Framework 」より引用 セッションの中では、フレームワークがGoで書かれていること、そしてテストの時に環境のセットアップができると言う意味でe2e-frameworkが有効であると結論付けられていました。 セッション内では、実際にe2e-frameworkを実際に使ってみての課題感についても取り上げられていましたが、本記事ではその点については触れないので興味のある方は動画をご確認ください。 私は、Kubernetesのカスタムコントローラーを運用する業務は直接的には実施していませんが、Kubernetesの挙動を簡素化させたい時や機能をカスタムしたい時があります。そういった時にKubernetesのカスタムコントローラーは有効なので、引き続きKubernetesのE2Eテストの分野についてもウォッチしていきたいなと感じました。 現地の様子 今年のKubeCon EUはパリで開催され、日本とは全く違う景色を楽しむことができました。また、オリンピックを控えたこの時期には、オリンピックグッズを扱う店舗も見られ、オリンピックの雰囲気を肌で感じることができました。 パリの風景 会場のParis Expo Porte de Versaillesです。敷地内にはマクドナルドやコンビニも隣接されており、非常に行動しやすい会場でした。 会場入り口の様子 バッジピックアップの会場 会場の案内図 会場周囲にはトラムが走っており、交通の便がよく治安も良い場所のように感じました。地元のグループによる早朝パリ観光マラソンも開催されていました。 会場近くの風景 続いては、KubeCon EUのランチです。KubeCon EUのランチでは、肉、魚、ビーガン向けの食事などが提供され、参加者の多様な食事ニーズに対して細やかな配慮が施されていました。 会場で提供されたランチ(1日目) 会場で提供されたランチ(2日目) また、今回もCloud Native Community Japanさん主催のもと 日本人交流会 が実施され、各社の参加されているエンジニアの方々と意見交換ができとても楽しい時間を過ごすことができました。 日本人交流会の様子 ブースについて KubeCon EUはCloudNativeConと同時に開催されKubernetesをはじめとする様々なOSSプロジェクトのブースや企業ブースがあり、それらのプロジェクトに参加するメンテナーと身近に話せる機会でもあります。 実際に我々も社内で利用するOSSプロジェクトのブースに出向き機能要望や意見交換を実施できました。 KubeCon EUはOSSプロジェクトのメンテナーとの距離が近く議論できる機会になるなと感じました。 Fluxブースの様子 Istioブースの様子 参加に向けてのTips できるだけ早めに準備する 参加の可否をできるだけ早く決めて、一刻も早く動くことで自由度が広がります。 KubeCon EUのチケットは早い時期に買うほど大幅に割引が効くため、参加コストを大きく抑えられます。 例えば、2023年11月28日までにチケットを購入していた場合$1149で参加できました。しかし、2024年3月17日以降にチケットを購入すると$2229となっておりほぼ倍の金額となっていました。 とくに宿泊する宿については、時間が経つにつれて条件の良い宿が埋まっていくため早めに準備することで現地での移動を便利にできます。 時差ボケに備える フランスは日本と比べて8時間遅く時間が進みます。フランスの朝8時は日本では深夜のため、朝起きることは簡単でもセッションが進むにつれて眠たくなる状態でした。 そのため意識的に睡眠を取って体内時間をずらす必要がありました。 質問を用意しておく セッション終了後には質疑応答が用意されていることが多く、登壇者との交流の機会でもあるため、セッションの内容を予習しておき疑問点をまとめておくとより有意義な参加になります。 最後に 今年からKubeCon EUはオフライン形式で統一され、会場は活気にあふれていました。 今回はKubeCon経験者(NAも含む)からKubeCon初参加者まで、幅広いメンバーで参加しましたがオープンソースプロジェクトのメンテナーや発表者との距離が近かったため、Kubernetesおよび、その関連エコシステムについての深い学びを得ることができました。 ZOZOでは一緒に働くエンジニアを募集しています。また、KubernetesやCloudNativeが大好きなエンジニアも大歓迎です。ぜひご応募ください。 corp.zozo.com corp.zozo.com
こんにちは、カート決済SREブロックの飯島と、ECプラットフォーム基盤SREブロックの織田です。 本記事では複数チームで運用する共通のAWSアカウントとKubernetesにおけるコストの可視化についてご紹介します。 背景 コスト可視化に対する課題 課題解決へのアプローチ AWSリソースのコスト可視化 AWSコスト配分タグ タグの定義と運用ルール タグの付け方 AWS Cost Explorer AWSコスト配分タグの活用例 Kubernetesクラスタのコスト可視化 Kubecost 比較検討 カスタムバンドル採用の決め手 アーキテクチャ 可視化の仕組み ダッシュボード 効果 コスト可視化の活用事例 最後に 背景 現在、ZOZOTOWNはモノリスなサービスを機能ごとに分け、マイクロサービスに移行しながらモダンアーキテクチャへのリプレイスを実施しています。マイクロサービスの移行先としてクラウドプラットフォームはAWSを、アプリケーション実行基盤はKubernetesを選定しています。両者とも複数チームで共通アカウントを利用するマルチテナント構成となっています。 AWSアカウントやKubernetesをマルチテナント化することには、メリットとデメリットの両面があります。 マルチテナントのメリットとしては、1つのAWSアカウントや1つのKubernetesクラスタですべてのサービスを管理できるため、運用作業が簡素化されます。Kubernetesの場合、アップグレードの回数が減り、共通の設定を他のクラスタに展開する必要がないため、運用コストを削減できます。 しかしその一方で、マルチテナントにすることで課題も生じます。特に、コスト可視化の面での影響が大きく、1つのアカウントやクラスタを複数のチームやサービスが利用しているため、チームやサービス単位でのコスト分離が困難になり、コストの可視化が難しくなります。 このように、マルチテナントにすることで運用が簡素化される半面、コストの可視化や分離が難しくなるというトレードオフが発生しておりました。 コスト可視化に対する課題 上記で述べた通り、マルチテナントでは共有されたリソースを複数のチームやサービスが利用するため、個別のコスト把握が困難になる傾向があります。1つのAWSアカウントやKubernetesクラスタで請求やリソース使用量が集約されてしまうため、チームやサービス単位でコストを明確に分離・可視化できていませんでした。 そのため、各プロジェクトやチームのコスト負担を正確に見積もることが難しくなり、予算計画の精度が下がります。マルチテナントの利点を生かしつつ、コスト可視化を実現するためには、仕組みを構築する必要がありました。 次に紹介するアプローチを講じることで、マルチテナントでもチームやサービス単位でのコスト可視化を実現し、より適切な予算管理を行えるような取り組みを実施しました。 課題解決へのアプローチ 課題解決のアプローチとして、AWSコスト配分タグの活用と、KubernetesクラスタへのKubecostの導入を実施しました。次のセクションからはそれぞれの詳細について説明します。 AWSリソースのコスト可視化 AWSコスト配分タグ AWSコスト配分タグ とは、AWSリソースに割り当てることができるラベルです。このタグにより、リソースのコストをより詳細に追跡し、分析できます。 タグの定義と運用ルール AWSコスト配分タグとして以下の表に示すタグを新たに作成しました。 Key Value CostEnv AWS環境を表す文字列。「dev」や「prd」など。 CostService どのサービスで使用しているかを表す文字列。Namespace名に統一。 CostTeam どのチームが管理しているかを表す文字列。 各タグのValueの値には、命名規則を設けてフォーマットを統一しています。原則として、AWSリソースには「CostEnv」と「CostService」を付けることを必須としています。CostServiceが付いていれば管理しているチームは把握できるため、「CostTeam」は任意としています。 タグの付け方 基本的にAWSリソースはAWS CloudFormationで管理されています。そのため、スタックテンプレートを使用してタグを付けます。以下にサンプルのコードを示しますが、タグの書き方はリソースによって異なりますので、詳細は公式ドキュメントをご参照ください。 Resources : IAMUserSample : Type : "AWS::IAM::User" Properties : UserName : "IAMUserSample" Tags : - Key : CostEnv Value : "dev" - Key : CostService Value : "zozo-techblog-sample" 上記のサンプルのコードを実行すると、AWSコンソールから以下のようにタグ付けされていることが確認できます。 注意すべきポイントはタグ付けができないAWSリソースも存在することです。また、AWS CLIではタグ付けができてもAWS CloudFormationではできない場合もあります。以下の公式ドキュメントに情報がまとまっているので、合わせてご参照ください。 Resource types you can use with AWS Resource Groups and Tag Editor AWS Resource Groups Tagging API Reference AWS Cost Explorer AWS Cost Explorerとは、AWSのコストおよび利用状況を可視化し、分析するためのツールです。事前にAWS Billing and Cost Managementコンソールから タグの有効化 を実施することで、AWSコスト配分タグを利用した検索やフィルタリングが可能です。 AWSコスト配分タグの活用例 AWS Cost Explorerの操作方法について説明します。以下の図は、検索とフィルターを使用していない状態で、AWSアカウント全体のコストの合計が表示されています。 例えば、ディメンション機能を使用してTag:CostServiceを選択すると、サービスごとのコストの合計が表示されます。 また、ディメンション機能で「サービス」を選択し、フィルター機能を使用してTag:CostServiceから特定のサービスを選択すると、そのサービスのAWSリソースごとのコストが表示されます。 Kubernetesクラスタのコスト可視化 Kubecost Kubecost とは、Kubernetesクラスタ内のリソース利用とコストを追跡・管理するためのツールです。無料版とエンタープライズ版が提供されています。また、AWSが提供している、Amazon EKS上でKubecostを利用するのに最適化された カスタムバンドル もあります。弊社ではAmazon EKSでKubernetes環境を構築しているため、このカスタムバンドルを採用しました。 比較検討 どの種類を採用するか決めるにあたり、主に「データの閲覧可能な期間」と「利用料金」の2つの観点で検討しました。以下の表は各種類の特徴をまとめたものです。 Kubecostの種類 データの閲覧可能な期間 利用料金 カスタムバンドル 15日前までの制限あり 無料 無料版 15日前までの制限あり 無料 エンタープライズ版 制限なし 有料 カスタムバンドルと無料版では、データの閲覧可能な期間が15日前までに制限されており、それ以前のデータは閲覧できません。コストを見積もる際には、月や年単位の過去のデータが必要となるため、コスト可視化の用途には適していませんでした。利用料金についてはどちらも無料ですが、カスタムバンドル以上のメリットがない無料版は選択肢から外しました。 一方、エンタープライズ版ではデータの閲覧に制限がなく、無期限に閲覧できます。ただし有料であり、Amazon EKSクラスタで稼働するNode数に応じて料金が決まります。利用料金を算出した結果、エンタープライズ版の採用が即決できるものではありませんでした。 カスタムバンドル採用の決め手 Kubecostにはデータをエクスポートできる API が提供されています。詳細については後述しますが、このAPIを使用してデータをAmazon S3に保存することで、閲覧可能な期間の制限に縛られなくなります。このようにして、カスタムバンドルのネックが解消できたため、採用することにしました。 余談ですが、カスタムバンドルのメリットの1つに、AWSサポートでの問い合わせが可能ということが挙げられます。問題発生時の問い合わせ先がAWSに統一できることはユーザーとしてもメリットとなります。 アーキテクチャ 弊社で採用しているKubecostのアーキテクチャです。 公式のアーキテクチャ も合わせてご確認いただくと理解しやすいかと思います。 Kubecost内のコンポーネントごとの役割としては以下のようになっています。 Kubecost Cost-Analyzer Frontend: nginxを実行し、Prometheusへのルーティングを行う Cost model: コストの計算とメトリクスを提供する AWS Sig V4 proxy: Amazon Managed service for Prometheusからクエリするためのプロキシ。パスワードレス認証が可能になり、AWS認証情報が公開されるリスクを軽減できる Prometheus Prometheus Pod: Kubecost Cost-Analyzerが生成したデータをAmazon Managed service for Prometheusにリライトする Amazon Managed service for Prometheus: コストとメトリクスを格納する時系列データストアでKubecostが生成したデータを格納する Kubecostは、Kubernetes APIサーバーからNode、Pod、コンテナ、ボリュームなどのメトリクスを収集します。主要なクラウドプロバイダー(AWS、GCP、Azure、etc.)の価格データが組み込まれており、そのデータを使用して、リソース使用量に基づきコストを算出します。 また、収集したメトリクスと価格データを組み合わせて、リソースごとのコストをモデリングします。CPUとメモリの使用量、永続ボリュームの種類とサイズ、ネットワークの送受信データ量などから、それぞれのコストを計算します。 Namespace、Deployment、Service、Podなどのレベルでコストを集計しているため、特定のワークロードのコストを確認できます。Kubecostはリザーブドインスタンスやスポットインスタンスの利用、適切なインスタンスタイプ選択に関する最適化の提案をします。また継続的にメトリクスを収集することで、長期的なコストトレンドの分析や予測も可能になります。 Web UIやGrafanaを使用することで、メトリクスの可視化やアラートを設定できます。しかし弊社では、先述したようにカスタムバンドルでのデータ永続化に課題があったため、独自に可視化の仕組みを構築しています。詳細に関しては、次のセクションで説明します。 可視化の仕組み 先述したように、KubecostのAPIを使用してデータをエクスポートし、Amazon S3に保存しています。その処理と、Amazon S3のデータを可視化する仕組みについて説明します。アーキテクチャは以下の通りです。 まず、毎日定時に起動するGitHub ActionsのWorkflowからAPIを実行し、前日のNamespaceごとのコストをCSVファイルで取得します。その後、取得したCSVファイルをAmazon S3にアップロードします。APIは以下のcurlコマンドで実行しています。 curl " http://kubecost-cost-analyzer.kube-support.svc.cluster.local:9090/model/allocation " \ -d window =yesterday \ -d aggregate =namespace \ -d accumulate =true \ -d format =csv \ -G 次に、Amazon S3のイベント通知機能を使用し、アップロードしたCSVファイルをAmazon SQSのキューに通知します。最後に、それをSplunkの Add-on で取り込み、ダッシュボードで可視化しています。弊社でのSplunkの活用に関する情報はテックブログで公開されていますので、そちらもぜひご覧ください。 techblog.zozo.com techblog.zozo.com ダッシュボード ダッシュボードではデータを表示したい日付の範囲と、対象のAmazon EKSクラスタの環境が選択できます。選択した日付の範囲内の、Amazon EKSクラスタのコストの合計が表示されます。 また、日ごとのAmazon EKSクラスタ全体のコストとNamespaceごとのコストが表示され、選択した日付の範囲内で推移を確認できます。 先述したようにKubecostではNamespaceだけでなくDeploymentやServiceなどのコストも集計しています。またコストの分析や予測、監視といった機能も提供していますが、弊社ではチームやサービス単位でのコストの可視化を目的としたため、上記の仕組みを採用しました。 効果 これまでの課題解決へのアプローチにより、マルチテナントのAWSアカウントとKubernetesのコスト可視化が実現できました。その結果、コストを正確に見積もることができるようになり、適切な予算管理が可能になりました。またチームやサービス単位でコストが明確に分離・可視化できるようになりました。 次に、コスト可視化の活用事例をご紹介します。 コスト可視化の活用事例 ZOZOTOWNのカート投入処理では、Webサーバとデータベースの間にキューイングシステムを挟むことでリクエストのキャパシティコントロールを実現しています。このシステムで使われているAmazon Kinesis Data Streamsのシャード数を変更しました。 techblog.zozo.com 以下の図は、左側がシャード数変更前のコストを示し、右側がシャード変更後のコストを示しています。関連リソースのAmazon DynamoDBを含め、大幅なコストの削減が実現できました。 このようにコスト可視化によって効果が明確に見えるようになったことは、コスト最適化の業務に取り組むうえでのモチベーション向上に繋がりました。 最後に 本記事では、マルチテナントのAWSアカウントとKubernetesにおけるコスト可視化の課題に対し、AWSコスト配分タグの活用とKubecostの導入による解決事例を紹介しました。同様の課題を抱えている方がいれば、ぜひ参考にしてください。 SREにおいてコスト最適化は重要なミッションです。今回ご紹介したコスト可視化の仕組みを利用し、ZOZOTOWNの信頼性向上の一環として取り組んでいきます。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。DevRelブロックの @wiroha です。2024年3月22日〜24日に「 try! Swift Tokyo 2024 」が開催されました。ZOZOはGOLDスポンサー・DIVERSITY & INCLUSIONスポンサーとして協賛し、ブースを出展しましたので現地のレポートをお届けします! 目次 はじめに 目次 会場 スポンサーブース アンケートの回答 印象に残ったセッション Swiftの型推論を学ぼう コード署名を楽しく乗り切る方法 Party、その他 アフターイベント LT1. SwiftPM マルチモジュール構成への第一歩 LT2. SwiftとC++を利用した画像処理プログラミング LT3. Introducing Pkl 感想戦 最後に 会場 会場は前回(2019年)と同じ、ベルサール渋谷ファーストです。try! Swiftのマスコットキャラクター「Riko」ちゃんのパネルやポスターが出迎えてくれます。 セッションルームはとても広く、たくさんの人が入れるようになっていました。朝から満員のようでした! 休憩ができるBreak Areaではかわいらしいお菓子やコーヒーなどが提供されていました。食べるのがもったいなくなります! こちらはスポンサーブースやAsk the Speakerコーナーのあるエリアです。休憩時、昼食時は特に大きな賑わいを見せ、皆さま交流を楽しんでいました。 スポンサーブース 各社さまざまな工夫を凝らしたブースが出展されていました。ZOZOのブースはたくさんのパネル・ノベルティにマネキンで存在感を出していました。 今回のメイン企画は「みんなの失敗」です。ZOZOには「#みんなの失敗」というSlackチャンネルがあり、失敗を共有することで共に学び、時に笑い、「日々進歩」しようという社内企画が行われています。その出張版として、ブースで皆さまの「開発の失敗」や「技術的負債」をお聞きしました。参加してくださった方には、失敗を水に流せるようにオリジナルトイレットペーパーのノベルティを差し上げました! トイレットペーパーの柄はZOZOのロゴをモチーフにしており、「ZOZO」の文字もこっそり隠れているんです! みなさんは気付きましたか? アンケートの回答 1日目のお題、「水に流したい開発の失敗」には最終的にパネルに貼りきれないほどの回答をいただきました! ヒヤリハットで済むものから、「これは深刻そう…」というものまで、さまざまな失敗が集まりました。なぜそうなったか、その後どうしたのかといった会話が弾みました。 2日目のお題は「水に流したい技術的負債」です。一度回答した方も「どんな回答が増えたか見に来ました」とたずねてくださる方が多数いました。これから勉強をはじめる方や新規開発中の方など、失敗や負債がまだない方には最近勉強していることを回答いただきました。 「英語」という回答には納得の声が出ました。try! Swiftは英語話者の参加が多いため、勉強しようというモチベーションが上がりますね。 「Apple Vision Proを買いましたか?」の質問は、悩んでいる方・次世代機を待っている方が多数ではあるものの、既に持っている・もうすぐ手に入るという方もいました! ZOZOでは会社でApple Vision Proを保有しており(技適特例申請済み)、ブースで体験できるようにしたところ一時は行列ができるほど盛況しました。 AirPlayでApple Vision Proで見ているものを共有しながらデモしている様子 印象に残ったセッション ここからは参加したエンジニアより、セッションの感想を紹介します。 Swiftの型推論を学ぼう speakerdeck.com 技術開発部のばんじゅんです。わたしはおもちメタルさんの「Swiftの型推論を学ぼう」をおすすめしたいと思います。コンパイラの理論的な部分はとっつきにくいものも多いですが、興味を持って調べるとおもしろい分野でもあります。このセッションでは、型推論ではまず押さえておきたい単純な例から始めて、まるで手で推論を計算するかのように学ぶことができます。一歩発展して、SwiftらしくOptionalの扱いの一部も計算できます。簡単に説明されていますが二度三度みてもよいくらい内容は詰まっていると感じます。より多くの人がコンパイラをつくる技術に興味をもつ入口のひとつになっていると良いなと思いながらセッションを聞きました。 登壇資料のスクリプト付きバージョン も公開されているので、内容をテキストで確認したいときはそちらが便利です。 コード署名を楽しく乗り切る方法 ZOZOTOWN開発1部iOSブロックの荻野です。このセッションでは、アプリをリリースするときに必ず通るコード署名について聞くことができました。発表の最初、「コード署名でつまずいたことがある方はいますか?」という問いかけに対して、会場にいたほとんどの人が手を挙げていました。自分と同じようにコード署名に苦い思い出のある人がこんなにいるんだと安心しました(笑)。 セッションの中では、コード署名において必要な要素を4つに分解し、それらをパズルのピースのように見立てていました。そうすることで起きているエラーのどこに問題があるのかが視覚的にわかりやすくなり、どう解決したらよいのかがすんなり理解できました。 発表の最後では、多くの参加者が「コード署名が楽しくなったと感じた」と手を挙げており、とても面白くためになる発表でした。発表資料は現時点では未公開のようで、公開されたらぜひ見ていただきたいです。 Party、その他 他にもSpeaker Dinner、スタンプラリー、Party、ワークショップなど企画が盛りだくさんのカンファレンスでした。 アフターイベント 開催翌週の3/27(水)、アフターイベントとして「 try! Swift Tokyo 2024 After Talk 」を開催しました。try! Swift Tokyo 2024のDIVERSITY & INCLUSIONスポンサーとして協賛しているピクシブ株式会社、株式会社ZOZO、STORES 株式会社による共催イベントです。モバイル開発に関するLTと、try! Swift Tokyo 2024の感想戦を行いました。当日の様子はYouTubeのアーカイブで公開しています。 www.youtube.com LT1. SwiftPM マルチモジュール構成への第一歩 speakerdeck.com ピクシブ株式会社 / 山本 小龍 @shoryu927 LT2. SwiftとC++を利用した画像処理プログラミング speakerdeck.com 株式会社ZOZO / 加藤 祥真 @shoma10170806 LT3. Introducing Pkl speakerdeck.com STORES 株式会社 / 榎本 健太 @enomotok_ 感想戦 感想戦はSTORES 株式会社の坂田( @huin )さま、ピクシブ株式会社の @FromAtom さま、STORES 株式会社の榎本さま、株式会社ZOZOの加藤で行いました。全体を振り返って楽しかった点や印象に残っているトーク、逆に難しかったので復習したいトーク、思い出に残った企画などを語り合いました。アフターイベントにご参加くださったみなさま、ありがとうございました! LT・感想戦の登壇者のみなさま 最後に try! Swift Tokyo 2024本編からアフターイベントまで、交流と学びを楽しませていただきました。オフラインカンファレンスは普段会えない方々とお話ができる貴重な機会です。このような場を提供くださったtry! Swift Tokyo 2024の運営の皆さま、本当にありがとうございました! ZOZOではカンファレンスに参加する方には参加費用の補助、登壇する方には登壇支援を行っています。一緒にサービスを作り上げてくれるiOSエンジニアを募集していますので、ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co カジュアル面談も実施していますので、「直近での選考応募は考えていないけども話を聞いてみたい」という方からもご応募お待ちしています! hrmos.co
はじめに こんにちは、SRE部 検索基盤SREブロックの花房です。普段は、ZOZOTOWNの検索関連マイクロサービスにおけるQCD改善やインフラ運用を担当しています。 以前まで、検索基盤を支えるチームではElastic Cloudの特権アカウントをメンバーで共用していました。本記事では、2023年4月にリリースされた Elastic CloudのRBAC(Role-Based Access Control)機能 を活用して、特権アカウントの共用から脱却した取り組みについて紹介します。さらに、既存機能と組み合わせることで実現した、効率的な権限管理についても紹介します。 同様の課題を抱えている読者の方には、下記の部分で参考になれば幸いです。 Elastic CloudにおけるSSOの活用 Elastic CloudのRBACによる権限管理の実例 Elastic Cloudアカウント情報を利用した、ElasticsearchのRole Mappingによる権限管理の実例 目次 はじめに 目次 背景・課題 Elastic Cloudの利用方法 何故、特権アカウントを共用していたか 権限管理における課題 解決策 Elastic Cloudの権限管理 Organization RBACの利用 SSOによるアカウント管理の省略 Elastic Cloudアカウントを利用したElasticsearchの権限管理 Cloud Roleに対するRole Mapping SSOアカウントに対するRole Mappingの追加 ElasticsearchのオリジナルRole 共通のRoleと、チームごとのRoleの作成 TerraformによるIaC化 結果 おわりに 背景・課題 Elastic Cloudの利用方法 ZOZOTOWNの検索基盤では、大規模な商品データを扱うためにElastic Cloud上のElasticsearch 1 を利用しています。Elastic Cloudは、ElasticsearchやKibanaなどのElastic製品をクラウド上で利用できるサービスです。検索基盤を支えるチームは、それぞれ担当業務が異なりますが、全チームがElastic Cloudにアクセスします。以下に業務の例を挙げます。 Elasticsearchを利用する検索機能の開発 ノード数やマシンスペックの増強 アラート発生時の調査 一般的に、クラウドサービスへのアクセスはメンバーそれぞれのアカウントを使用すべきです。さらに、そのアカウントには担当業務にあった権限セット(以降、 Role と表します)を付与します。このように管理することで、情報漏洩などのセキュリティ上のリスクを防ぐことができます。しかし、本記事の取り組みを実施するまで、私たちは特権アカウントを共用してElastic Cloudにログインしていました。その状態を下記の図に示します。 何故、特権アカウントを共用していたか Elastic Cloudの利用を開始した当初、登録できるアカウントは1つまでという制限があったため、やむを得ず特権アカウントを共有していました。 特権アカウントを使用すると、Elasticsearchクラスタの管理を担うDeploymentに対して全ての操作が可能です。そのため、特権アカウントの共用は誤操作による重大な事故を引き起こすリスクがありました。例えば、誤って本番環境のクラスタを削除してしまうことが考えられます。 そこで、操作頻度が高くリスクも大きいDeployment管理にはIaC化を施しました。IaC化により、管理画面からDeploymentを変更・削除する機会をなくすことで事故を防いでいました。こちらのIaC化の詳細については、以前の記事を参照ください。 techblog.zozo.com その後、 Elastic Cloudのアップデート により、Organizationへのアカウント追加が可能になりました。しかし、全てのアカウントに管理者権限が付与されてしまうため、細かい権限管理はできませんでした。以上の理由から特権アカウントの共用が続いていました。 権限管理における課題 これまでに説明した利用方法の問題を整理します。 アカウントの共有によるセキュリティ上のリスク 誤操作により重大な事故を引き起こす可能性について、対策を施していたが排除はできていない 上記の問題は、単純にメンバーごとにアカウントを分離すれば解決できそうに思います。しかし、アカウント分離について下記の課題があり、状況は変わりませんでした。 チームに合ったRoleを柔軟に設定できない メンバーごとのアカウントを新たに作成する場合、アカウント管理が大きなトイルになる 以降では、上記の問題・課題の解決方法と、改善後の権限管理について説明します。 解決策 Elastic Cloudの権限管理 2023年4月、 Elastic CloudのRBAC機能がリリース されました。この機能を利用すると、Organizationへ招待したメンバーに対して細かく権限を設定できます。 私たちは、この機能を利用することで特権アカウントの共用から脱却できました。 Organization RBACの利用 Elastic CloudのRBACでは、用意されたRole(以降、 Cloud Role 2 と表します)をメンバーに付与して権限を管理します。Cloud Roleには下記の5つが存在します。 Organization owner Billing admin Admin Editor Viewer 私たちは、Deployment操作の制限のためにAdminとViewerを使用しています。そのため、今回はその2種類のみ紹介します。その他のCloud Roleの説明は、 公式ドキュメント をご確認ください。 Cloud Role 説明 Admin Deploymentの作成や、プロパティ管理、セキュリティ権限の管理が可能 Viewer Deploymentの閲覧が可能 AdminとViewerは、Deployment単位で設定可能です。私たちは、SREチームに全DeploymentのAdmin、バックエンドチームやMLチームには全DeploymentのViewerを設定しています。このように設定している理由は、バックエンドやMLチームの誤操作による事故を防ぐためです。 Deployment管理と同様に、OrganizationのRBACもIaC化したいと考えていました。しかし、Organization機能がTerraformに対応していないため、今回は断念しました。現在は手動で権限を管理していますが、下記の理由から保守は難しくなく問題にはなっていません。 検索基盤を支えるチームは合計20人程度 設定するCloud Roleは、AdminまたはViewerの2種類のみ SSOによるアカウント管理の省略 RBACのためには、利用メンバーごとにアカウントを分ける必要があります。しかし、新たにElastic Cloudアカウントを作成すると、管理者側と利用者側の両方でアカウント管理がトイルになります。そこで、Elastic CloudへのSSOログインに会社管理のGoogleアカウントを用いるようにしました。 GoogleアカウントでのSSOにより、アカウント管理のトイルを削減できました。招待作業は残りますが、退職時の削除作業やパスワード変更は不要になります。退職時は会社側でGoogleアカウントが削除されるため、自動的にElastic Cloudにもログイン不可になります。また、利用者はElastic Cloud用にアカウントを使い分ける手間がなくなりました。会社管理のGoogleアカウントは、Elastic Cloud以外にも様々なサービスで普段利用しているため、統一することで利便性の向上にも繋がりました。 SSOを利用するために特別なことは必要ありません。通常のアカウントと同様に、Organizationに招待する際、Roleを付与した上でGoogleアカウントのメールアドレスに招待メールを送るだけです。利用者は招待メールからログイン画面に進み、GoogleアカウントでSSOログインするとOrganizationに参加できます。 ここまで説明した改善により、Elastic Cloudへのアクセス方法は下記の図のようになりました。 Elastic Cloudアカウントを利用したElasticsearchの権限管理 Elastic Cloudアカウントは、Elasticsearchへのアクセスにも利用可能です。直接アクセスするのではなく、クラスタに紐づくKibanaを通してGUIでアクセスします。Deploymentの管理画面からKibanaにアクセスすると自動的にログインされますが、下記のようなログイン画面が表示されることもあります。その場合は下側の Login in with Elastic Cloud を選択してください。 一方、Elastic CloudとElasticsearchは権限管理の仕組みが分かれているため、Cloud RoleではElasticsearchの権限を定義できません。それらを関連付けるため、 Elastic Cloudのアカウント情報を利用した設定 がElasticsearchのRole Mappingに標準で追加されました。 Role Mapping はElasticsearch内のRBACを実現する機能で、以前から存在しました。 その追加された設定では、Elastic Cloudアカウントでのアクセス時、Cloud Roleに対応するElasticsearchのRoleを付与します(以降、 Default Mapping 3 と表します)。この設定のように、Elastic Cloudのアカウント情報はElasticsearchから参照可能になっています。 以下では、Default Mappingと、私たちが追加したSSOアカウントに対するRole Mappingについて説明します。 Cloud Roleに対するRole Mapping Default Mappingが適用されると、Cloud Roleに対応するElasticsearch側のRole(以降、 Stack Role と表します)が付与されます。 Default Mappingにおいて、Stack Roleとして設定されるものは Elaticsearch標準のRole です。AdminとViewerについて、Cloud RoleとStack Roleのマッピング関係を下記に示します。 Cloud Role Stack Role Admin superuser Viewer viewer つまり、アクセスしたDeploymentについて、AdminのCloud Roleを持っていればStack Roleはsuperuserになります。ViewerのCloud Roleを持っていればStack Roleはviewerになります。 私たちは、バックエンドチームやMLチームにCloud RoleとしてViewerを付与しているため、Stack Roleとしてviewerが付与されます。このviewerでは操作が閲覧のみに制限されるため、そのまま利用するにはElasticsearchに関する権限が足りませんでした。バックエンドチームやMLチームは開発において、Index自体やIndex内のドキュメントを編集する必要があるためです。そこで、オリジナルのRoleを作成して適用することにしました。 先に、オリジナルのRoleをRole Mappingで適用する方法を説明します。その後、オリジナルのRoleについて説明します。 SSOアカウントに対するRole Mappingの追加 Elasticsearchにおいて、Elastic Cloudアカウントを対象としてオリジナルのRoleを付与するには、下記の画面のように設定します。 このUser IDを条件とする方法(以降、 Custom Mapping と表します)は、Elasticのサポートの方から教えていただきました。User IDはプロフィール画面やOrganization画面から確認できます。 Custom Mappingの説明のため、上記では画面をお見せしました。実際には、私たちはGUIではなくTerraformで管理しています。Elastic CloudのOrganizationはIaC化できませんでしたが、ElasticsearchのRole MappingはIaC化可能でした。公式のプロバイダー elastic/elasticstack を使用して、下記のように設定できます。 resource "elasticstack_elasticsearch_security_role_mapping" "zozo_default_role_mapping" { provider = elasticstack.cuslter_1 # Role Mappingを作成するクラスタを指定 name = "dev-zozo-default-role-mapping" enabled = true roles = [ # 付与するRoleを指定 elasticstack_elasticsearch_security_role.zozo_default_role.name ] rules = jsonencode ( { # Elastic CloudアカウントのUser IDが一致すればRoleを付与 any = [ for user_id in local.all_ec_user_ids : { field = { username = user_id } }] } ) } ここで説明したCustom Mappingの追加により、Elaticsearch内においてもアカウントを増やさず、チームごとの詳細な権限管理を実現しました。 ElasticsearchのオリジナルRole 以下では、Custom Mappingで付与するオリジナルのRoleと、そのIaC化について説明します。 共通のRoleと、チームごとのRoleの作成 オリジナルのRoleの権限設計は下記のように進めました。 各チームに、環境・Deployment・Indexごとに必要な操作をヒアリング ヒアリング結果の必要な操作と、 Cluster Privileges・Index Privileges との照らし合わせ 運用のしやすさを考慮したRole設計 権限設計の結果、オリジナルのRoleとして下記の2種類を作成しました。実際は本番・ステージング・開発の環境ごとに、さらに分かれています。 全員が最低限必要となる権限をまとめたRole チームごとに追加で必要となる権限をまとめたRole 1を作成した理由は、各チームが最低限必要となる権限がまとまっていたため、また、チームごとのRoleを冗長にせず管理しやすくするためです。2を作成した理由は、チームごとに権限が欲しいDeploymentやIndexが異なり、共通のRoleにはまとめられない権限が存在したためです。 TerraformによるIaC化 オリジナルのRoleもIaC化が可能であり、Role Mappingと合わせて下記のように設定しています。Indexに対する権限設定では、 dynamic blocks を使用して簡素化しています。 # 共通Role resource "elasticstack_elasticsearch_security_role" "zozo-default-role" { provider = elasticstack.cluster_1 name = "dev-zozo-default-role" cluster = local.zozo_default_role_cluster_privileges dynamic "indices" { for_each = local.zozo_default_role_index_privileges content { names = indices.value.names privileges = indices.value.privileges } } } # 共通RoleのRole Mapping resource "elasticstack_elasticsearch_security_role_mapping" "zozo_default_role_mapping" { provider = elasticstack.cluster_1 name = "dev-zozo-default-role-mapping" enabled = true roles = [ elasticstack_elasticsearch_security_role.zozo_default_role.name ] rules = jsonencode ( { # 全員のElastic CloudアカウントUser IDを条件に指定 any = [ for user_id in local.all_ec_user_ids : { field = { username = user_id } }] } ) } # バックエンドチーム用Role resource "elasticstack_elasticsearch_security_role" "backend_team_role" { provider = elasticstack.cluster_1 name = "dev-backend-team-role" cluster = local.backend_team_role_cluster_privileges dynamic "indices" { for_each = local.backend_team_role_index_privileges content { names = indices.value.names privileges = indices.value.privileges } } } # バックエンドチーム用RoleのRole Mapping resource "elasticstack_elasticsearch_security_role_mapping" "backend_team_role_mapping" { provider = elasticstack.cluster_1 name = "dev-backend-team-role-mapping" enabled = true roles = [ elasticstack_elasticsearch_security_role.backend_team_role.name ] rules = jsonencode ( { # XチームメンバーのElastic CloudアカウントUser IDを条件に指定 any = [ for user_id in local.backend_team_ec_user_ids : { field = { username = user_id } }] } ) } local.tf は下記サンプルのように設定しています。 # zozo_default_roleに関する部分のみ抜粋 locals { all_ec_user_ids = [ "1234567890" , "1234567891" , "1234567892" ] zozo_default_role_cluster_privileges = [ "monitor" , "monitor_snapshot" , "manage_index_templates" , "manage_ilm" , "manage_slm" ] zozo_default_role_index_privileges = [ { names = [ "sample-1-*" ] privileges = [ "all" ] } , { names = [ "sample-2-*" ] privileges = [ "create_snapshot" ] } , { names = [ "sample-3-*" ] privileges = [ "monitor" , "read" , "view_index_metadata" ] } ] } 結果 Elastic Cloudの権限管理は、最終的に下記の図のようになりました。SREチームは、Default MappingによりElasticsearch内でsuperuserが付与されるため、チーム用Roleの設定はありません。 今回の取組みにより、特権アカウント共用による下記の問題が解消されました。 アカウントの共有によるセキュリティ上のリスク 誤操作により重大な事故を引き起こす可能性 さらに、SSO対応・Role管理・IaC化を進めたことで下記の効果も得られました。 アカウント運用管理により発生するトイルの削減 Googleアカウントを用いたSSOログインによる利便性向上 RBAC利用による柔軟かつ統一された権限管理の実現 IaC化による権限管理の保守性の向上とヒューマンエラーの防止 おわりに 本記事では、Elastic CloudのRBAC機能を活用した、安全・効率的・柔軟な権限管理を紹介しました。 今回の取組みにより、特権アカウントの共用から脱却できました。しかし、下記のようにIaC化出来ていない箇所やリスクを排除できていない課題は残っているため、引き続き改善を進めていきます。 Elastic CloudのOrganization機能はTerraform未対応のためIaC化できていないこと マイクロサービスからElasticsearchへはsuperuserでアクセスしており、意図しない動作により関係のないIndexなどを削除してしまう可能性があること ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co Elasticsearchはバージョン8.2.3以降を利用しています。 ↩ Cloud Roleは正式名称ではありません。 公式ドキュメント では、 Elastic Cloud role と表現されています。本記事では、似た名称である Elastic Stack role が登場するため、読みやすさを考慮してそれぞれ Cloud Role 、 Stack Role と表現しています。 ↩ Default Mappingは正式名称ではありません。 公式ドキュメント では、説明の後、 the default mapping と表現されています。本記事では、独自に作成したRole Mappingと区別して読みやすくするため、それぞれ Default Mapping 、 Custom Mapping と表現しています。 ↩
こんにちは、カート決済部カート決済サービスブロックの林です。普段はZOZOTOWN内のカートや決済の機能開発、保守運用、リプレイスを担当しています。 弊社ではカートや決済機能のリプレイスを進めており、これまでにカート投入のキャパシティコントロールや在庫データのクラウドリフトを実現しています。 techblog.zozo.com techblog.zozo.com 本記事では新たにクレジットカード決済処理を非同期化したリプレイス事例を紹介します。 はじめに 背景・課題 非同期化のシステム構成 パターン1 - 完全非同期化パターン パターン2 - 非同期・同期切り替えパターン パターン3 - ポーリングパターン システム構成の決定 メッセージングサービスの選定 効果 今後の展望 まとめ さいごに はじめに 本章では、非同期化前のZOZOTOWNのクレジットカード決済を用いた注文処理の流れを説明します。 ZOZOTOWNの注文情報はオンプレミス環境で稼働しているSQL ServerのCartDBとFrontDBという2つのDBで管理しています。注文リクエストがくると、CartDBに注文データを作成します。この時に作成される注文データをZOZOTOWNでは仮注文と呼んでいます。仮注文の作成後、クレジットカード与信取得のためのリクエストを行います。与信取得後、仮注文の状態を与信完了に更新します。その後、CartDBからFrontDBへ注文データを移します。FrontDBの注文データを本注文と呼んでいます。 上記の処理の流れを以下の図に示します。 背景・課題 ZOZOTOWNでは、セールの終了時刻となる日曜から月曜にかけての日跨ぎ時に注文のピークを迎えます。この時の注文数が通常時の十倍前後となります。 次のグラフはあるセールの日の日跨ぎ時の注文数の推移です。 注文がスパイクしたタイミングで、レイテンシも上がっています。レイテンシが上がることで、ユーザーの待ち時間が長くなりUXの低下が発生します。以下が注文リクエストのレイテンシの推移です。 最初は、このレイテンシの悪化の原因がDBにあると考え、日跨ぎ時のDBのCPU使用率を調査しました。以下が同じ時間帯のDBのCPU使用率の推移です。 DB自体の負荷は余裕があり安定した状態となっていました。原因を深掘りしたところ、クレジットカードの与信リクエストの遅延でした。社外のAPIになるため、我々が直接レイテンシを改善することは困難でした。さらに、今後の成長を踏まえると、ユーザーの待ち時間がより長くなることで、UXの低下が予想されました。 そこで、UXの改善のための最初の対応としてクレジットカード決済処理の非同期化を進めることとしました。 非同期化のシステム構成 非同期化を行うにあたり以下の3パターンを検討しました。 与信処理から非同期化し、注文成立していない状態でも注文完了の画面を表示する 1のパターンを用意するが、同期処理にも切り替えられるようにする 与信処理から非同期化し、フロント側ではポーリングを行い注文成立したら注文完了の画面を表示する それぞれの詳細とメリット・デメリットを以下に記載します。 パターン1 - 完全非同期化パターン このパターンではユーザーとの注文完了コミュニケーションも非同期になります。処理フローは以下のとおりです。 Webサーバが注文リクエストを受け付ける 仮注文の作成 メッセージングサービスにエンキュー Webサーバは受付完了の画面を返す Workerがデキュー クレジットカードの与信確保のリクエスト CartDBに与信結果を保存 FrontDBに本注文を作成 5以降の処理が非同期になっています。ユーザーから見た場合、エンキューしたタイミングで画面が切り替わるため与信処理のレイテンシに影響されなくなります。 メリットとしては以下が挙げられます。 与信処理のレイテンシに影響されることなく注文リクエストが完了する キューとWorkerによるキャパシティコントロールができる デメリットとしては以下が挙げられます。 ユーザーは受付完了の画面に遷移できた場合でも与信NGで注文成立しない可能性がある 注文成立しない場合は支払い方法の変更通知などをメールで知らせる必要がある 支払い方法変更の猶予を持たせるために、在庫を確保しておく必要がある 注文数が少ない時間帯でも注文NGの場合に、ユーザーコミュニケーションが非同期になってしまう パターン2 - 非同期・同期切り替えパターン このパターンはパターン1の構成と既存の構成どちらも利用します。環境変数やアプリケーションロジックにより、既存パターンとパターン1の非同期化ロジックを切り替えられるようにします。 メリットとしては以下が挙げられます。 ピーク時はパターン1を利用できる 注文数が少ない時は、既存ロジックに切り替えることでピーク時以外はパターン1のデメリットをカバーする デメリットとしては以下が挙げられます。 同期と非同期どちらも必要になるので決済フローの改修コストが増える パターン3 - ポーリングパターン このパターンでは、パターン1と同様に与信処理は非同期化を行います。UXの低下は避けたいので、フロントからポーリングを行い、注文状態を確認するようにします。本注文が作成されたら、注文完了の画面に遷移します。与信等でエラーになった場合はカートトップに戻ります。処理フローは以下のとおりです。 Webサーバが注文リクエストを受け付ける 仮注文の作成 メッセージングサービスにエンキュー Webサーバはポーリング用のIDを返却 フロントはIDを用いてポーリング開始 Workerがデキュー クレジットカードの与信確保のリクエスト CartDBに与信結果を保存 FrontDBに本注文を作成 6以降の処理が非同期になっています。非同期で本注文が作成されると5でポーリングしているところで検知し、注文完了の画面に遷移します。ユーザーから見た場合は既存のUI/UXと変わりませんが、与信確保のリクエストはキャパシティコントロールされています。 メリットとしては以下が挙げられます。 キューとWorkerによるキャパシティコントロールができる 注文リクエスト自体は与信処理のレイテンシに影響されない 注文完了のユーザーコミュニケーションを非同期にする場合でも非同期部分のフローは変更なしで実現できる デメリットとしては以下が挙げられます。 ユーザーから見た時のUI/UXは変わらないので、注文リクエストをしてから注文完了の画面に行くまでに待ち時間がかかる システム構成の決定 3つのパターンの中から弊社ではパターン3を選択しました。理由は以下のとおりです。 現在のUXから低下させる部分を作りたくない パターン1ではピーク時以外のUXが低下してしまう 今後の改修コストの増加を極力避けたい パターン2では同期と非同期の二重開発になり改修コストが増える 注文完了のユーザーコミュニケーションを非同期にした場合、ビジネス要件の調整に時間がかかる 与信失敗した場合にその注文はどうするのか 支払い方法変更を受け付ける場合、どのくらいの期間受け付けるか 今後ピーク時だけ注文完了のユーザーコミュニケーションを非同期にすることも比較的容易に可能である パターン3でキャパシティコントロールを実現しつつ、並行してユーザーコミュニケーションの非同期化のためのビジネス要件の調整を進めることにしました。 メッセージングサービスの選定 データをキューイングするためのメッセージングサービスの検討もおこないました。今回候補に上がったAWSのサービスが以下の2つです。 Amazon Kinesis Data Streams (KDS) Amazon Simple Queue Service (SQS) それぞれの特徴やメリット、デメリットの詳細はカートリプレイスPhase1のテックブログで触れているのでご参照ください。 techblog.zozo.com クレジットカード決済の非同期化では以下4点を主な検討事項としました。 厳密な順序性はいらない スパイクにも耐えられる 1リクエストが詰まっても他に影響がでないようにしたい なるべくマネージドなサービスにしたい この中で採用の決め手となったのが、「1リクエストが詰まっても他に影響がでないようにしたい」でした。 与信処理では一部のリクエストだけが遅くなることがあります。そのため、特定のリクエストが遅延した場合でも他のリクエストへの影響を減らせる構成を検討していました。 KDSの場合シャード単位で読み出すため、特定のリクエストが遅延した場合の影響はシャード全体に渡ります。 SQSの場合は、特定のリクエストが遅延してもその他のWorkerがリクエストを処理できるので影響範囲を少なくできます。 上記の点から、本件ではSQSを採用することにしました。 効果 パターン3を実装した結果、仮注文作成までが注文リクエストの役割となったため、注文リクエスト自体のレイテンシは安定化し高速になりました。非同期化の実装前後での日跨ぎ時のレイテンシの比較は以下のとおりです。 また、SQSとWorkerでキャパシティコントロールができるようになった結果、与信リクエストの遅延がなくなりました。さらには、どれくらい与信キューに溜まっているかを可視化できるようになりました。以下は、日跨ぎ時に滞留しているキュー数の推移です。 滞留しているキュー数が明確になったことで、今後のZOZOTOWNの成長率との組み合わせでどれくらいキューに溜まり、注文完了までどれくらいかかるかの計算が可能になりました。 今後の展望 今回与信のキャパシティコントロールは実現したものの、UXの改善には至りませんでした。現状は注文完了の画面表示までのユーザーの待ち時間はリプレイス前と変わりません。今後ZOZOTOWNが成長していくと、必ず待ち時間は延びていきます。そのため、ユーザーコミュニケーションを非同期にしていく必要性があります。ユーザーコミュニケーションを非同期にした場合、以下の検討事項があります。 与信失敗時の注文データの取り扱い 既存では失敗時はカートに商品を戻している 再度注文を行なってもらうより支払い方法変更できることが望ましい 支払い方法を変更できる場合はどれくらいの期間在庫を確保するか 在庫の確保期間が長ければ売り上げ損失になる可能性がある 在庫の確保期間が短ければUXの低下を招く可能性がある これらの検討事項はシステムだけではなく、ビジネス要件とも大きく関係してきます。現時点ではまだ検討中で、ビジネス側と密に連携し、今後の方針を決めていきたいと考えています。 まとめ 本記事ではクレジットカード決済の非同期化のシステム構成を紹介しました。非同期化したことにより、与信処理のキャパシティコントロールを行うことができました。非同期化を検討している方がいれば、ぜひ参考にしてみてください。今後はユーザーとのコミュケーション部分も非同期で行えるようにし、UXの向上を目指していきたいと考えています。 さいごに ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com