株式会社ZOZOのブログ - TECH PLAY

TECH PLAY

株式会社ZOZO

株式会社ZOZO の技術ブログ

1050

こんにちは、SRE部カート決済SREブロックの飯島です。普段はZOZOTOWNのカート決済機能のリプレイス・運用・保守に携わっています。またSplunkの管理者としても活動しています。 本記事ではSplunk CloudにおけるInfrastructure as Code(IaC)についてご紹介します。 背景 Splunkに対して感じていた課題 Splunk CloudのIaC化検討 Splunk Appを使ったIaC App API IaC対象のSplunkリソース 既存のリソースのエクスポート IaCの全体像 Appのディレクトリ構成 CI/CDパイプライン 検証の詳細 デプロイの詳細 効果、メリット 終わりに 背景 弊社では様々な用途でSplunkを活用しています。過去にいくつかテックブログを公開していますので、興味のある方はぜひご覧ください。 techblog.zozo.com techblog.zozo.com Splunkには、オンプレミスやクラウドプロバイダーにインストールして運用するSplunk Enterpriseと、SaaSとして提供されるSplunk Cloudの2種類があります。弊社では後者のSplunk Cloudを利用しています。 Splunkに対して感じていた課題 冒頭でも説明した通り、弊社ではSplunkを様々な用途で活用しており、その重要性は導入時よりも高まっています。一方で以下の課題が生じていました。 SPLの習得難易度が高く新規利用の開始までのハードルが高い コード管理されていないためダッシュボードなどがいつの間にか変更されていることがある 何の用途で作成したか不明なリソースが存在する IaCを導入すると以下のメリットが生まれ、上記の課題を解決できます。 Splunkが不慣れでも他のコードを参考にして効率よくリソースの作成、変更が可能 コード管理されているとSplunkを触ったことがない人にも勧めやすい コード管理ツール(例えばGitHub)に変更履歴が残る コード管理ツールでレビューできる/レビューが受けられる Splunk CloudのIaC化検討 上述した課題を解決するためにSplunk Cloudをコード管理する方法を検討しました。 社内でインフラ構成管理ツールとしてTerraformを利用しているケースが多かったため、まずはTerraformでの管理を検討しました。しかしSplunk CloudではTerraformで管理できるリソースが一部に限られており、ダッシュボードなどのコード管理に対応できないことから課題解決に至らないと判断しました。 補足としてSplunk Enterpriseはほぼ全てのリソースがTerraformに対応しているため、Splunk Cloudについても今後対応リソースが増えていく可能性は十分あります。 github.com Splunk Appを使ったIaC そこで今回Splunk App(以下、App)と、Splunk社から提供されるApp用の2つのAPIを使ってIaCを実現しました。 App Appとは、Splunkの設定を集約して管理するための単位です。デフォルトではSearch & ReportingというAppがあらかじめ利用可能になっています。ユーザー自身でAppを新規作成したり、 Splunkbase からAppをインストールしたりもできます。 今回Search & Reportingの既存リソースを、新規作成したAppに移行してコード管理する方針としました。 API 以下が使用した2つのAPIです。 AppInspect API Appの品質と準拠性を検証するためのAPI(ref. Validate your private app ) Admin Config Service (ACS) API AppをSplunk Cloudにデプロイすることが可能(ref. Install an app ) デプロイするにはAppInspect APIの検証に合格する必要がある IaC対象のSplunkリソース 弊社でよく利用されているダッシュボードとアラート・レポートを対象としました。 Appを使ったIaCでは、Terraformのようにリソースを インポート して既存リソースをコード管理下に置く手段がありません。リソースをコードで定義すると、新規リソースとして作成されます。そのため、既存リソースを新規リソースに置き換えることが難しいもの(例えばIndex)は対象外としました。 既存のリソースのエクスポート 既存リソースの構成ファイルはエクスポート可能です。Splunk Enterpriseでは利用者自身でエクスポート可能ですが、Splunk Cloudではこの機能が存在せずSplunkサポートに依頼する必要があります。 今回既存のリソースが集約されているSearch & Reporting Appをエクスポートし、IaC対象のダッシュボードとアラート・レポートの構成ファイルを入手しました。 IaCの全体像 Appの構成ファイルはGitHubで管理しています。また、AppInspect APIとACS APIを使用し、GitHub ActionsのWorkflowを利用して、アプリの検証とデプロイを行うCI/CDパイプラインを構築しました。 Appのディレクトリ構成 Appは 開発者ガイド にある通り、決められたディレクトリ構成に準拠する必要があります。もし準拠できていない場合、AppInspect APIの検証に失敗します。 以下はAppの必要最低限のディレクトリ構成に、ダッシュボードとアラート・レポートを追加した構成です。 . ├── bin │ └── README ├── default │ ├── app.conf │ ├── data │ │ └── ui │ │ ├── nav │ │ │ └── default.xml │ │ └── views │ │ └── README │ │ └── sample_dashboard.xml │ └── savedsearches.conf ├── metadata │ └── default.meta └── static ├── appIcon.png ├── appIconAlt.png ├── appIconAlt_2x.png └── appIcon_2x.png 以下は上記の構成の主なファイルの説明です。 ファイル 説明 ./default/app.conf Appの設定 ./default/savedsearches.conf アラート・レポートの設定 ./default/data/ui/views/dashboard.xml ダッシュボードの設定 ./metadata/default.meta 各種リソースのアクセス権限の設定 CI/CDパイプライン GitHub上でPRを作成すると、ダッシュボードのXMLファイルのLintと、AppInspect APIを使用したAppの検証が実行されます(CI)。マージすると再度CIが実行され、その後ACS APIを使用したAppのデプロイと(CD)、CI/CDの結果のSlack通知が実行されます。 以後、Appの検証とデプロイの処理について説明します。 検証の詳細 以下がAppの検証処理の流れです。 splunk.comにログインしてアクセストークンを取得 -(1) Appのコードをtarコマンドでgzip圧縮 -(2) (1)と(2)を添付してAppInspect APIを実行 AppInspectの結果から検証の合否を判定 以下サンプルコードです。 # splunk.comにログインしてアクセストークンを取得 loginstatus = $( curl -u " $username : $password " --url " https://api.splunk.com/2.0/rest/login/splunk " ) access_token = $( echo " ${loginstatus} " | jq .data.token | tr -d ' " ' ) # Appのコードをtarコマンドでgzip圧縮 COPYFILE_DISABLE = 1 tar --format ustar -cvzf " ${appname} " .tar.gz " ${appname} " tarfile_path = $( realpath " ${appname} .tar.gz " ) # AppInspect API実行 request = $( curl -X POST \ -H " Authorization: bearer ${access_token} " \ -H " Cache-Control: no-cache " \ -F " app_package=@ ${tarfile_path} " \ -F " included_tags=cloud " \ --url " https://appinspect.splunk.com/v1/app/validate " ) # AppInspectの進捗を確認 request_id = $( echo " ${request} " | jq .request_id | tr -d ' " ' ) appinspect_status = " PROCESSING " while [ " ${appinspect_status} " = "PROCESSING" ]; do echo " waiting the app inspect reviewing " echo " fetch to check the status of inspecting from https://appinspect.splunk.com/v1/app/validate/status/ ${request_id} " sleep 10 status_request = $( curl -s -X GET \ -H " Authorization: bearer ${token} " \ --url " https://appinspect.splunk.com/v1/app/validate/status/ ${request_id} " ) echo ""; echo " -------------- App Inspect Status -------------- " echo " ${status_request} " | jq . appinspect_status = $( echo " ${status_request} " | jq -r .status ) done # AppInspectの成否の判定 errors = $( echo " ${status_request} " | jq -r .info.error ) failures = $( echo " ${status_request} " | jq -r .info.failure ) result = " failed " if [ " ${errors} " -eq 0 ] && [ " ${failures} " -eq 0 ]; then result = " success " fi AppInspectの結果(上記サンプルコードの status_request )には以下の項目が含まれます。errorとfailureが0であれば検証は合格です。 " status ": " SUCCESS ", " info ": { " error ": 0 , " failure ": 0 , " skipped ": 0 , " manual_check ": 0 , " not_applicable ": 0 , " warning ": 0 , " success ": 1 } AppInspectの結果の詳細はレポートで確認できます。レポートにはJSON形式とHTML形式があり、Content-Typeでファイル形式を選択できます。 # JSON形式 curl -X GET \ -H " Authorization: bearer ${access_token} " \ -H " Cache-Control: no-cache " \ -H " Content-Type: application/json " \ --url " https://appinspect.splunk.com/v1/app/report/ ${request_id} " # HTML形式 curl -X GET \ -H " Authorization: bearer ${access_token} " \ -H " Cache-Control: no-cache " \ -H " Content-Type: text/html " \ --url " https://appinspect.splunk.com/v1/app/report/ ${request_id} " デプロイの詳細 以下がACS APIを実行してSplunk CloudにAppをデプロイする処理です。 curl -X POST " https://admin.splunk.com/ ${STACK} /adminconfig/v2/apps/victoria " \ --header " X-Splunk-Authorization: ${access_token} " \ --header " Authorization: Bearer ${json_web_token} " \ --header " ACS-Legal-Ack: Y " \ --data-binary " @ ${tarfile_path} " | jq . CIで取得したアクセストークンとtarコマンドでgzip圧縮したAppのコードを添付します。またJSON Web Tokenが必要になるため、事前にSplunk CloudのUIもしくはACS APIで作成します。 効果、メリット IaC化により、他のコードを参考にしてリソースの作成や変更が可能となりました。これにより、Splunkに不慣れな場合でも効率的にリソースの作成や変更ができ、Splunkに触っていない人にもSplunkの利用を勧めやすくなりました。またGitHub上のPRで変更内容をレビューでき、その変更内容が履歴に残るメリットも得られました。 終わりに 弊社と同様にSplunk Cloudを使い、IaC化を検討している方の参考になれば幸いです。 今回Splunkのカスタマーサクセスチームの皆様には多大なご協力をいただきました。この場を借りて、心から感謝申し上げます。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは、データシステム部推薦基盤ブロックの寺崎( @f6wbl6 )です。 私たちのチームではZOZOTOWNにおけるパーソナライズ機能と推薦システムを開発しており、2022年6月のテックブログではZOZOTOWNのホーム画面をパーソナライズした事例の1つを紹介しました。 techblog.zozo.com 今回は上記記事の続編として、ホーム画面での商品訴求の単位である「モジュール」の並び順をパーソナライズした取り組みをご紹介します。本記事がパーソナライズ機能や推薦システムを開発している方の参考になれば幸いです。 目次 はじめに 目次 ZOZOTOWNのホーム画面における「モジュール」について パーソナライズ施策の比較 パーソナライズの方針 モジュール並び順パーソナライズの要件 パーソナライズ方法 概要 モデル構成 モジュールのスコア算出方法 システム構成 1.Two-Towerモデル学習パイプライン 2.商品ベクトル化パイプライン 3.モジュール並び順予測パイプライン A/Bテスト 概要 結果 A/Bテストを経て挙がった課題 定性的な課題 定量的な課題 最後に ZOZOTOWNのホーム画面における「モジュール」について まずはじめに、前回記事のおさらいとしてZOZOTOWNのホーム画面における「モジュール」について簡単に説明します。 モジュールは商品やブランド、ショップなどのコンテンツを表示させるための枠の総称で、ZOZOTOWNのホーム画面はこれらのモジュールによって構成されています。 2024年7月19日時点でのZOZOTOWNホーム画面 モジュールは訴求内容や更新頻度に応じて「定常モジュール」と「企画モジュール」の2種類に大別されます。各モジュールの違いは下表の通りです。 運用方法 更新頻度 定常モジュール 常に同じ訴求内容のコンテンツを掲載する ほぼ更新なし 企画モジュール ビジネスサイドが自由に訴求内容を企画・管理する 1~2週ごとに更新 定常モジュールは「チェックしたアイテム」や「世代別アイテムランキング」など、ファッショントレンドなどの外的要因によらず継続的に特定の内容を訴求するものです。一方、企画モジュールはビジネスサイドの企画チームがトレンドやキャンペーンに応じて柔軟に訴求内容を変更するもので、定期的に更新される点が特徴です。 企画モジュールには商品の検索条件が紐づいており、この検索条件を元に商品検索用のAPI(検索API)から取得した商品をモジュールに表示する仕組みとなっています。そのため企画チームは商品の検索条件を設定するだけで多様な訴求軸のモジュールを作成できます。 パーソナライズ施策の比較 「ホーム画面のモジュールをパーソナライズする」と一口に言ってもパーソナライズのパターンがいくつかあるため、ここではそのパターンを列挙しつつ過去の施策と今回の施策がどのパターンに該当するかを説明します。 モジュール内に表示される商品の表示順序を変える モジュール内に表示される商品の検索条件を変える 表示されるモジュールの順序を並び替える 過去のテックブログ で紹介したパーソナライズ施策は上記のうち「 2.モジュール内に表示される商品の検索条件を変える 」に該当します。前述の通りモジュールには特定の検索条件で取得した結果を表示しているため、ユーザーごとにパーソナライズしたブランドやカテゴリを検索条件に反映させることでモジュール内の商品をパーソナライズしていました。 過去のパーソナライズ施策のイメージ 今回のパーソナライズ施策は「 3.表示されるモジュールの順序を並び替える 」に該当します。モジュールそのものの並び順をユーザーごとに変化させ、ユーザーが興味を持つと考えられるモジュールを上位に表示することを目指しています。 今回のパーソナライズ施策のイメージ また、パーソナライズの対象となるモジュールは前述した「 企画モジュール 」に限定しています。定常モジュールはいわゆるマス向けの訴求軸であるため、パーソナライズの効果はそこまで期待できません。一方で企画モジュールは企画チームが多様な訴求軸のモジュールを作成できるため、パーソナライズによる効果が大きいと考えられます。 パーソナライズの方針 過去の施策ではパーソナライズの目的として「 既存購入者の来訪頻度・購入頻度の増大 」を掲げていましたが、今回の施策ではまず「 より良いモジュールの並び順とはどのような状態か 」を定義することから始めました。 ユーザーにとって最適な並び順でモジュールを表示することは長期的には購入頻度や受注金額の増大に繋がると考えられますが、短期的には興味のあるモジュールをクリックする頻度が増えるはずです。ここから「良いモジュールの並び順」を「ユーザーが興味を持ってクリックするモジュールが上から順に並んでいる状態」と定義し、まずは「 モジュールのクリック数増大 」を目指すこととしました。 この目的を元に、「ユーザーにクリックされるモジュールを優先的に表示する」という方針でパーソナライズシステムを開発しました。以降、モジュールの並び順のことを「モジュール並び順」と記述します。 モジュール並び順パーソナライズの要件 今回のプロジェクトで挙げられた要件をいくつかピックアップします。 会員ごとにパーソナライズされた並び順でモジュールを表示する パーソナライズされた並び順が定期的に更新される パーソナライズロジックのA/Bテストを実施できる モジュール並び順のパーソナライズができないユーザーに対してデフォルトの並び順でモジュールを表示できる 任意のモジュールを特定の位置に固定表示できる 1〜3番目の要件については特に違和感はないと思います。 4番目の要件は、ユーザーがパーソナライズに必要な行動ログを持たない場合やパーソナライズシステムが障害等で稼働できない状況を想定したものです。パーソナライズ機能に依存してZOZOTOWNのホーム画面にモジュールが表示されなくなる、という状況を避けるためにデフォルトの並び順を表示する要件を設けています。 5番目の要件はビジネス要求に基づくものです。ZOZOTOWNでは特定のブランド・ショップの商品の訴求を強めたり、セール期間中にセール商品の訴求を強めたりするケースがあり、そういった際に一部のモジュールを特定の位置に固定表示します。また広告系のモジュールやパーソナライズ以前から訴求効果が高かった「チェックしたアイテム」モジュールなど、表示位置を迂闊に変えるべきでないモジュールもいくつかあります。今回のプロジェクトではこれらのモジュールはパーソナライズの対象外として特定の位置に固定表示しています。 パーソナライズ方法 ここからはメイントピックであるモジュール並び順のパーソナライズ方法について説明します。 概要 モジュール並び順のパーソナライズにはユーザーとモジュールの相性を表すスコアを利用しており、このスコアの降順にモジュールを配置することでパーソナライズを実現しています。モジュールのスコアを計算する方法の概念図を以下に示します。 パーソナライズ方法の概念図 ユーザーを表現するベクトルはZOZOTOWN内での回遊行動をもとに作成し、モジュールを表現するベクトルはモジュールに表示される商品の情報をもとに作成しています。モジュールのベクトルはそのモジュールに表示される商品ベクトルの集合と考えられるため、商品ベクトルの集合とユーザーベクトルを用いてモジュールに対するスコアを計算します。 このパーソナライズ方法を実現するには ユーザー情報と商品情報をベクトルにするための仕組み が必要になります。次節ではこのベクトル化に必要な機械学習モデルとシステム構成について説明します。 モデル構成 ユーザー情報や商品情報をベクトル化する手法は多々ありますが、今回は弊社の別のパーソナライズシステムでも利用されている Two-Towerモデル を採用しました。Two-Towerモデルはドメインが異なるデータを同一のベクトル空間にマッピングする2つのエンコーダーから成っています。これらのエンコーダーを使ってユーザーと商品それぞれのベクトルを獲得します。 Two-Towerモデルの概念図 モデルの学習方法や使用したライブラリについては以下の通りです。 項目 内容 学習の方針 商品閲覧を最適化 モデル構築に使用したライブラリ TensorFlow Recommenders 近似最近傍探索の手法 ScaNN モジュールのスコア算出方法 ユーザーベクトルと商品ベクトルを使ってモジュールのスコアを決めますが、スコアの算出方法はパーソナライズ結果に大きく影響を与えるため、オフラインでの実験時にいくつかのパターンを試しました。 全商品ベクトルの平均をモジュールのベクトルとみなし、 ユーザーベクトルとのコサイン類似度をモジュールのスコアとする 全商品ベクトルの各次元でmaxをとったベクトルをモジュールのベクトルとみなし、 ユーザーベクトルとのコサイン類似度をモジュールのスコアとする ユーザーベクトルと各商品ベクトルのコサイン類似度を算出し、 得られたコサイン類似度の平均値をモジュールのスコアとする ユーザーベクトルと各商品ベクトルのコサイン類似度を算出し、 得られたコサイン類似度の最大値をモジュールのスコアとする 上記のスコア算出方法に対して定量・定性評価を実施した結果、案4の算出方法が定量的・定性的に優れていたことから案4を採用しました。 設計時は案3を採用する想定でしたが、定性評価をしてみると意図しない・もしくは意図がわからない推薦をしているものが多い結果となっていました。これはモジュールに掲載される商品では必ずしも統一感があるわけではなく、そうした商品のスコアの平均を取るとスコアが平滑化されてしまうことが原因と考えています。 商品スコアの最大値を取ることで「ユーザーの興味を惹く商品が少なくとも1つ以上ある」という条件を満たすモジュールを優先的に表示することになります。しかし、必ずしもそうした商品がモジュールの先頭に表示されているわけではないので、商品の表示位置を考慮した重み付けなど改善の余地があると考えています。 システム構成 Two-Towerモデルの学習から予測結果のサービングまでを含めた全体のシステム構成は以下のようになっています。 パーソナライズシステムの構成図 モジュール並び順を予測するためにVertex AI Pipelinesを用いたバッチシステムが3つ存在します。以下ではそれぞれのシステムの役割を説明していきます。 1.Two-Towerモデル学習パイプライン このパイプラインは定期的にTwo-Towerモデルを学習し、学習済みモデルをVertex AI Model Registryに登録することを目的としています。先述した通りTwo-Towerモデルからはユーザーと商品それぞれをベクトル化するエンコーダーが得られるため、後続システムではこれらのエンコーダーを使ってベクトル化を行います。図中ではユーザーと商品それぞれのエンコーダーを User tower , Item tower と記述しています。 2.商品ベクトル化パイプライン このパイプラインは、モジュールに表示される商品のベクトル化を目的としています。モジュールに表示される商品の情報は商品検索用のAPIから取得し、Item towerを使ってこれらの商品情報をベクトル化します。ベクトル化された商品情報はBigQueryに保存され、後続システムであるモジュール並び順予測パイプラインで使用されます。 運用上の理由から、モジュールに表示される商品リストは1日に複数回更新されることがあります。そのためこのパイプラインは1日に数回実行し、モジュールに紐づく商品ベクトルが最新の状態となるようにしています。 3.モジュール並び順予測パイプライン モジュールの並び順を予測するためのパイプラインです。Vertex AI Model Registryに格納されている学習済みのUser towerを使用し、BigQueryから取得したユーザー情報をベクトル化します。またBigQueryにはモジュールに紐づく商品のベクトルも格納されているため、これらのベクトルを使ってモジュールのスコアを計算し、モジュールの並び順を決定しています。 予測結果はBigtableに保存され、リアルタイムにリクエストを処理する推薦APIが会員IDごとのモジュール並び順を取得する構成となっています。 A/Bテスト 概要 モジュール並び順のパーソナライズによる効果を評価するために、ZOZOTOWN会員を対象として約1か月のA/Bテストを実施しました。Control群とTreatment群を比較すると以下のような変化が生じます。 今回の施策ではすべてのモジュールがパーソナライズされるわけではなく、ビジネス的な要望から常に特定の位置に固定表示されるモジュールがある点に留意ください。例えば上図の「モジュールA(固定)」と記載されているモジュールはA/Bの割り振り関係なしで上から2番目に固定表示されています。 結果 A/Bテストの結果サマリを以下に示します。 指標 備考 結果(T/C比) ホーム画面訪問者の受注金額 ホーム画面にランディングしたユーザーの合計受注金額 100.1 (%) モジュール経由の受注金額 モジュールに表示されている商品の合計受注金額 100.1 (%) モジュール経由の受注点数 モジュールに表示されている商品の合計受注商品点数 100.4 (%) モジュール掲載商品のインプレッション数 モジュールに表示されている商品の合計インプレッション数 105.2 (%) モジュール掲載商品のクリック数 モジュールに表示されている商品の合計クリック数 103.2 (%) 受注系の指標はControlと比較して大きなアップリフトはありませんでしたが、 モジュールに掲載されている商品のインプレッションや閲覧に関する指標は大きく改善している ことがわかります。このことから、ユーザーはモジュールに興味を持ってホーム画面を回遊してより多くのモジュールを閲覧し、モジュールから商品ページに遷移する頻度が増えたと考えられます。 一方で受注系の指標に特段の変化がなかったことから、購入するほどの商品は提示できていなかったか、1か月という期間ではユーザーの購入体験を変えるほどの効果は計測できなかったと考えられます。 受注系の指標まで改善すれば理想的な結果でしたが、まずはプロジェクトの目的である「 モジュールのクリック数増大 」は達成できたと言える結果となりました。 A/Bテストを経て挙がった課題 上述した課題点も含め、A/Bテストを経て挙がった定性・定量的な課題と改善案について簡単に紹介します。 定性的な課題 「訴求軸は同じだが並んでいる商品が微妙に異なるモジュール」が連続して表示される 見たことのないカテゴリの商品が表示されにくい どちらの課題もモジュールのスコア算出で使用しているTwo-Towerモデルの最適化方針に原因があると考えられます。Two-Towerモデルは「次に閲覧する商品を予測する」という問題設定で学習しているため、ユーザーが直近で閲覧しそうな商品を提示することには成功しています。しかし、 その結果モジュールに掲載される商品が同じカテゴリやブランドに偏ってしまう という傾向が確認されました。 極端な例では以下のように全く同じ商品がファーストビューに並んでいるケースを確認しています。 この課題はアルゴリズムの設計時点で考慮事項として挙げられていたものの、日々の運用でカバーできるものと判断して対策をしていませんでした。 改善案として同じ商品が並んでいるモジュールを除外するルールベースのアプローチや、 MMR(Maximal Marginal Relevance) といったナイーブなリランキング方法の導入が考えられます。また今回はパーソナライズ方法としてモジュールのスコアを降順に並べただけのシンプルな方法を採用していますが、機械学習モデルでリランキングするなどの方法も考えられ、まだまだ改善の余地を残しています。 定量的な課題 受注系の指標がアップリフトしていない 特定カテゴリで各種指標が低下している 受注系の指標がアップリフトしていない原因として、前述の通りモジュールに表示される商品がユーザーにとって購入意欲を喚起するほどのものではなかったことが挙げられます。この課題の対処法としてパーソナライズロジック自体を改善するのはもちろんですが、 パーソナライズに使用されるモジュールの訴求軸を多様にする というアプローチが考えられます。 今までは全てのユーザーに同じモジュールを表示する都合上マス向けの訴求に偏る傾向がありましたが、これからは多種多様な訴求軸でユーザーに合ったモジュールを提示できます。この「多種多様なモジュール」を企画するのはビジネスサイドの企画チームであるため、モジュール並び順のパーソナライズを改善するには企画チームとの密な連携が欠かせません。 2つ目の課題はユーザーに閲覧される頻度が多くないカテゴリに関して閲覧や受注系の指標が悪化している、というものです。今まではモジュールの企画チームがその時訴求したい内容をモジュールで表現してユーザーに提示することで、カテゴリごとの閲覧や受注のばらつきが少ない状態でした。モジュール並び順がパーソナライズされた後はそうしたカテゴリごとのばらつきなどが意識されていないため、特定の期間で特定カテゴリの訴求を強める仕組みなどを検討しています。 最後に 本記事ではZOZOTOWNのホーム画面に表示するモジュールの並び順をパーソナライズするシステムとその効果について紹介しました。今回取り上げた部分以外にも改善すべき箇所が大量にあるので、これからも1つずつ改善していくことでユーザーにとってより良いZOZOTOWNを提供できるよう邁進していきます。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
こんにちは、技術本部 SRE部 基幹プラットフォームSREチームの斉藤です。普段はZOZOの持っている倉庫システムやブランド様が触る管理ページなどのサービスのオンプレミスとクラウドの構築・運用に携わっています。またDBREとしてZOZOTOWNのデータベース全般の運用・保守も兼務しております。 7月11日、12日に行われた「 db tech showcase 2024 」に、DBREから5名のエンジニアが参加しました。この記事では会場の様子と印象に残ったセッションについてご紹介します! db tech showcaseとは 会場の様子 セッションレポート おわりに db tech showcaseとは 国内最大規模のデータとデータベース関連のカンファレンスです。このイベントでは、データベースの専門家やエンジニア、IT業界のリーダーたちが一堂に会し、新しい技術やソリューション、事例、ノウハウを共有します。 db tech showcaseの主な内容には以下のようなものがあります。 講演:データベース技術の最新トレンド、事例や実践的なアプローチについての講演が行われます。 展示:企業やベンダーが最新の製品やサービスを展示し、デモンストレーションします。 ネットワーキング:業界の専門家や同僚と交流する機会が提供され、情報共有やビジネスの機会が広がります。 www.youtube.com 会場の様子 入口 db tech showcase 2024はTKP市ヶ谷カンファレンスセンターで開催されました。受付を終えるとパスが発行され、イベントのパンフレットが配布されました。 EXPO データベースに関連する企業のブースが並んでいました。セッションの間で立ち寄ったOracle社のブースではHeatWaveの話題で盛り上がり、セッションさながらの事例や最新情報を熱弁頂きました。他にも、休憩スペースでセッションの感想で盛り上がってる人達がいたり、ミニセッションも開催されていたりしました。EXPOエリアではこうした交流が盛んに行われいて会場全体に活気がありました。 セッション 2日間で75以上のセッションが行われました。 db tech showcase 2024セッションスケジュール その他 企業の各ブースでは魅力的なグッズを配布していました。 セッションレポート DBREのメンバーが気になったセッションを紹介します。 MySQLアプリケーション開発を爆速にする最新手法 Amazon Bedrockで検証!データベース移行時のクエリ修正を簡素化するLLMの活用法 爆速成長中のFindyがぶつかった課題と解決に向けた実践手法 【出光興産様事例紹介】SAP DB運用の課題と今後の計画 SQL Server 技術支援のための動作の調査 - dbts2024 版 - MySQLアプリケーション開発を爆速にする最新手法 基幹プラットフォームSREチームの斉藤です。ZOZOのメインデータベースはSQL Serverを使用していますが、マイクロサービス化が進んでいるシステムではAurora MySQLを採用しています。MySQLの最新情報や運用に使用できるツールの事例などをキャッチアップすべく本セッションに参加させていただきました。 MySQLとVS Codeの統合 VS Codeで利用できるオープンソースの拡張機能として、MySQL Shell for VS Codeが提供されています。MySQL WorkbenchのEOLが迫っている中で後継となるもので、MySQLの運用には欠かせない機能になっていくのではないだろうかと思います。MySQL WorkbenchからはSQLエディタやスキーマの確認・データの検索結果の表示など主要な機能は全て継承されていると思われます。さらに「MySQL Shell for VS Code」はただのMySQL Workbenchの代わりではなく、VS Codeを使用してきた開発者とMySQL Workbenchを使用してきた管理者が共通して使用できるIDEとして設計されています。NotebookというエディタからはSQLだけではなくJavaScriptやTypeScriptを実行し結果をグラフで表示できます。これらの機能を使用してシステム開発が効率化されていると感じました。 MySQL Shell for VS Codeついては公式ドキュメント「 MySQL Shell GUI / MySQL Shell for VS Code 」を参照してください。 MySQLのJavaScriptサポート MySQL Enterprise Editionの機能でストアドプロシージャや関数をJavaScriptで作成し実行できます。そのストアドプロシージャや関数は実行するSQL上でも呼び出すことができ、処理の幅が広くなりました。 MySQLでJavaScriptを使用するできるメリットは次の通りです。 高い表現力 JavaScriptアプリケーション開発者向けの大規模なツールのエコシステム サードパーティライブラリの充実 人気のある開発言語の1つで、1300万人もの開発者が存在する MySQLのJavaScriptサポートついての情報はThe Oracle MySQL Japan Blogの「 MySQLのJavaScriptサポートについて 」で詳しく紹介されております。 MySQL REST Service MySQL REST ServiceはMySQL Routerの拡張機能として提供されています。RESTを使ってHTTP経由でMySQLのデータを参照、更新ができます。MySQL Shell for VS Code上から設定し運用します。 基本的なアーキテクチャ 画像引用元: The Oracle MySQL Japan Blog「 MySQL REST Serviceの紹介 」 MySQL REST Serviceついての情報はThe Oracle MySQL Japan Blogの「 MySQL REST Serviceの紹介 」で詳しく紹介されております。 感想 MySQLは「MySQL Shell for VS Code」やJavaScriptサポート、「MySQL REST Service」などの最新機能により、開発効率と運用性が大幅に向上し、現代のアプリケーション開発に重要なソリューションへと進化を続けていると感じました。 Amazon Bedrockで検証!データベース移行時のクエリ修正を簡素化するLLMの活用法 カート決済SREの遠藤です。 DBのバージョンアップや異なるDBへリプレースする際、クエリの互換性が課題になることがあります。この課題に対し、昨今注目されるLLM(大規模言語モデル)を活用してクエリの修正工数を減らせるかの検証デモが行われました。 現在、ZOZOTOWNで利用しているSQL Serverのサーバー分離とバージョンアップを担当しており、LLMを使ったクエリ修正に興味がありこのセッションに参加しました。 内容 異なるDBMS製品への移行や、バージョンアップに伴うクエリ修正の例が5つほど紹介されました。まず、何も工夫をせずLLMに対してクエリ修正を指示した場合、半分以上が間違った回答でした。こうしたハルシネーション(生成AIの嘘)を防止するために、よくある工夫をプロンプトに取り入れることで精度が向上しました。 クエリ互換性に関する内容であることの記述 専門家としての視点の強調 思考の連鎖を促す指示(step by step) 事例の提供(Few-Shotプロンプティング) また、RAG(検索拡張生成)としてAmazon Bedrockを使い、DBMS製品の公式リリースノートをナレッジベースとすることでさらに精度が向上しました。 Bedrockの場合、ナレッジベースをS3に格納するのですが、以下のような加工をすることでさらに精度が向上しました。 リリースノートからSQLの互換性に関係のない記述を削除 Markdown形式に変換 最終的に、5つのデモ全てにおいてクエリを正しく修正できていました。100%の精度は保証されないものの、LLMはSQL変換タスクにも有用であり、修正工数を大幅に減らすことができそうです。 感想 ChatGPTをはじめとしたLLMはすでに業務で活用していますが、ここまで具体的にDB移行業務に活用できる例を見ることができ、新たな発見がありました。LLMでは100%正しい回答が得られるとは限らないため、人力でのチェックは必須です。そこでRAGを利用することにより、ナレッジベースを元に回答の根拠も示してくれるため、ハルシネーションが発生しても比較的簡単に正当性を判断できそうです。 今回のセッションではOracleDBからPostgreSQLへの移行でしたが、弊社ではSQL ServerやMySQLを採用することが多いためそちらでも試してみたいと思いました。また、弊社ではテーブル設計ガイドラインを作成し、日常的にDBREがテーブル設計レビューを行っています。ガイドラインをナレッジベース化することで、テーブルのCREATE文を元に一次レビューを自動化できるかもしれません。 アプリケーション開発ではGitHub CopilotをはじめとしたLLMによる開発のサポートが既に充実しています。しかし、今後SQLの開発にも同様のことが行えるようになり、DBA/DBREの働き方も大きく変わってくると感じました。 爆速成長中のFindyがぶつかった課題と解決に向けた実践手法 基幹システム本部・物流開発部の黒岩です。 普段はZOZO社内で使用している基幹システムのリプレイスをする傍ら、DBREとしても活動しています。db tech showcaseは今回初めての参加でしたが、どの発表も内容が幅広く、興味深いセッションが多かったです。 私はファンディ株式会社 @gessy0129 げっしーさんの「爆速成長中のFindyがぶつかった課題と解決に向けた実践手法」というセッションについて紹介します。 DB面での課題と解決方法 サービスが急成長し、多くのユーザーに使われていく中で直面したDB面での課題とその解決方法が紹介されました。 .tal{text-align:left !important;} 性能問題 解決方法 DB負荷が高まり、CPU使用率が上昇 RDSからAuroraに移行してリソース利用を効率化 AuroraをARMベースのインスタンスに変更し、同じ性能をより低コストで実現 SQLのレスポンスが悪化 Performance Insighsを活用してクエリパフォーマンスを把握し、ボトルネック箇所を可視化 クエリの実行計画を分析して足りないインデックスを特定、インデックスを追加 インデックスが使われないクエリの形を修正(例:IN句の中に大量のデータが入っている、LIMITの数が適切でない場合など) DBエンジニアとアプリケーションエンジニアの協力 セッションで印象に残ったのは、スピーカーのげっしーさんが言った「性能試験は総合格闘技」という言葉でした。この言葉通り、DBエンジニアだけで解決するのではなく、アプリケーションエンジニアと密に連携して問題解決に取り組んだ例が紹介されていました。具体的には、以下2つの取り組みが印象的でした。 ReaderとWriterのインスタンスノードの振り分け AuroraのReaderインスタンスが有効に活用されていない課題に対して、DBエンジニアとアプリケーションエンジニアがペアプロしながら解決した事例です。DBエンジニアがReaderインスタンスの役割や性能向上の仕組みを説明し、アプリケーションエンジニアがその知識をもとにアプリケーションコードを改修して、書き込み操作時にはWriterインスタンス、読み込み操作時にはReaderインスタンスへ分けるようにしたそうです。これにより、読み込み処理時にReaderインスタンスが使われるようになり、効率的な負荷分散を実現したとのことでした。この取り組みでは、両者がそれぞれの専門知識を共有し、リアルタイムでコードの変更とその効果を確認しながら進めたことが成功の鍵となったそうです。 アプリケーションコード内のクエリ最適化 もう1つの課題として、より複雑な処理をクエリで一括処理するか、データを分割して取得した上で、アプリケーション側で処理するかという課題があったそうです。ここでも相互にペアプロなどで検証しながら性能を検証し、例えば「MySQLが苦手とする集計処理」について、クエリで一括処理していた部分を一部アプリケーション側で処理することで、DBの負荷を軽減した例が紹介されていました。 感想と今後の取り組み このセッションを通じて、DBエンジニアとアプリケーションエンジニアの協力がDBの負荷軽減において非常に重要であることを再認識しました。現在、弊社でも基幹DBの負荷軽減を図るためにクエリチューニングを進めていますが、この取り組みはまだ効果が限定的です。今回のセッションで学んだように、DBの性能課題に真摯に向き合い、基幹システムに関わるチーム全体で性能問題解決に取り組む姿勢を大切にし、今後もさらなる協力と改善を進めていきたいと思います。 【出光興産様事例紹介】SAP DB運用の課題と今後の計画 基幹システム本部・データ連携ブロックの馬場です。現在ZOZOTOWNの基幹システムで使用しているデータベースの性能改善を課題としています。 社内システム統合時に発生したデータベースのスローダウンに対し、どのようなツールを利用し課題対応しているか、という事例紹介のセッションに参加しました。 MaxGauge スローダウンが発生しているボトルネッククエリを調査すべく、データベース監視ツールである「MaxGauge」が紹介されました。「MaxGauge」はデータベースサーバにエージェントを導入することで、リアルタイムにSQLのトラッキングを行い、蓄積されたトラッキングデータにより、パフォーマンス低下対象の事後分析が可能です。 弊社ではDPMツールとして「Datadog」を導入していますが、情報収集に掛かるデータベース側への負荷や、クエリの取得間隔等を比較してみたいです。 MAJESTY 次にデータベース開発支援ツールの「MAJESTY」の紹介です。パフォーマンスが低下しているクエリに対し、最適なインデックス設計をアドバイスしてくれることで効率的に性能分析・改善が可能です。最大の特徴は「アクセスパターン分析」を実装しており、テーブルへのアクセスをパターン化し、最適なアクセスパスを分析することです。これにより、SQL単体を分析する手法よりも大幅にコストを抑えた性能改善が実現可能です。 感想 今回の事例ではSQL Serverを利用し、基幹システムに性能問題を抱えている境遇が弊社と似ているため参考になるセッションでした。 チューニングを行うにはデータベースに対し一定の習熟度が必要になりますが、社内のエンジニア全員が同じ習熟度を保つことは難しいです。本セッションで紹介されたツールを導入することにより、誰もが同じ視点で性能改善を実施し、日々の性能改善コストが大幅に削減できる環境が作れると良いと思います。 SQL Server 技術支援のための動作の調査 - dbts2024 版 - 技術本部SRE部カート決済SREブロックの伊藤です。 こちらのセッションはMicrosoft MVPの小澤さんが、SQL Serverの挙動について、実演を交えて調査方法を紹介してくれるセッションとなっていました。例えば、クエリストアや拡張イベントを有効化する場合にクエリのスループットにどの程度影響が出るかをどのように調べるか、といった内容です。 内容が濃密なこともあり、事例全てを紹介していただくことは時間の都合上できませんでしたが、会場を巻き込んで挙手制でアンケートを取りながら紹介事例を選ぶ姿は、オフラインイベント感を感じる一幕でした。 事例の中で私が最も印象に残ったのは「オンラインでのインデックス再構築による同時実行性の低下の有無」です。 オンライン再構築による同時実行性の低下の有無 SQL Serverではオンラインでのインデックス操作に対応しています。 ONLINE オプションは、このようなインデックス操作中に基になるテーブルまたはクラスター化インデックス データ、ならびに関連付けられた任意の非クラスター化インデックスへ同時ユーザー アクセスを可能にします。 たとえば、あるユーザーがクラスター化インデックスを再構築している最中に、そのユーザーと他のユーザーが基になるデータの更新やクエリを続行できます。 引用元: オンラインでのインデックス操作の実行 この時に、ロックは一切かからないのかを解説交えながら検証の実演が行われました。具体的には以下のような手順です。 長時間かかるようなSELECT文を実行する SSMSから実行するときには結果を出力しないようにクエリオプションで破棄する設定にしておく この時 sys.dm_tran_locks などでロックの状況を確認すると共有ロックがかかっている状態が見られる バックグラウンドでSELECTを大量に実行しておく Ostress を使うことで並列に大量のクエリを流し続けておくことができる REBUILD INDEX WITH(ONLINE=ON)を実行する 最終段階でSch-Mのロックが必要になるため1の処理と競合してロックの解放待ち状態となる この時に2で実行していたクエリをブロックする可能性がある 発生の有無はロックパーティションの状態次第 CPUのコア数で挙動が変わる部分でもある ロックタイムアウトの活用でブロッキングの連鎖は防ぐことができる 後日実際に自分の環境でも試してみた 上記の内容を改めて実際に試してみました。これは過去にオンラインでインデックス作成が行われた際にブロッキングの発生を観測したことがあり、ユーザーに影響するものなので対策含めて運用ドキュメントに取り入れていきたいという思いがあったからです。 実際に流したクエリは以下の通りです。 -- 長時間かかるSELECT文 SELECT a.SampleColumn FROM _testTable AS a CROSS JOIN _testTable AS b -- Ostressから実行したバックグラウンドで動かすSELECT文 SELECT TOP( 1 ) * FROM [dbo].[_TestTable] tablesample( 1 percent) WITH (NOLOCK) -- インデックスのリビルド ALTER INDEX [IX__TestTable_SampleIndex] ON [dbo].[_TestTable] REBUILD WITH ( ONLINE = ON ) GO 上記のクエリをそれぞれ流したところ、インデックスリビルドの最終段階でSch-Mのロック要求が発生していることを確認できました。 sys.dm_tran_locksから確認したロック情報 Datadogが入っている検証環境ということもあり、Datadogからも確認したところ、ブロッキングが連鎖してOstressで流していたクエリも巻き込まれていることが確認できました。 Datadog Database MonitoringのBlocking Queriesからのキャプチャ これに対し、インデックスリビルド時に SET LOCK_TIMEOUT を付けて同様に再現したところロック要求のタイムアウトの発生を確認できました。インデックスのリビルドは失敗するため、改めて実施する必要はありますが、これでユーザー影響を抑えることができます。 -- 1秒ロックが取れなかったらインデックスのリビルドを中断する SET LOCK_TIMEOUT 1000 ; ALTER INDEX [IX__TestTable_SampleIndex] ON [dbo].[_TestTable] REBUILD WITH ( ONLINE = ON ) GO エラーメッセージが出力されて処理が中断 実演を見た上で自分でも試してみて、インデックス操作時の挙動及び調査方法への理解を深められました。インデックス操作時の SET LOCK_TIMEOUT 指定の必須化などは弊社内での運用にも取り入れていこうと思います。 おわりに 今回のセッションを通じて多くの知見を得ることができました。セッション以外にも、企業ブースや他社のエンジニアとの交流を通じて、多くの学びと刺激を受けました。実際に現地に足を運ぶことで得られるものは非常に大きかったです。 db tech showcase 2024で学んだことをZOZOのデータベースシステムの向上に活かしていこうと思います。 ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! corp.zozo.com
はじめに こんにちは。WEARバックエンド部SREブロックの 春日 です。普段は WEAR というサービスのSREとして開発・運用に携わっています。本記事では、約60%のコスト削減に成功した NATゲートウェイ の通信内容の調査方法と通信量の削減方法についてご紹介します。 目次 はじめに 目次 背景 コストの把握 NATゲートウェイの通信内容の把握 CloudWatchメトリクスでの確認 VPCフローログでの確認 リゾルバーでのクエリログでの確認 調査結果をもとにNATゲートウェイ経由での通信量を削減する AWSサービスとの通信 Datadogとの通信 WEARのAPIとの通信 ECRパブリックリポジトリとの通信 結果 まとめ 背景 ZOZOではより効果的な成長を目指してコストの最適化を進めています。コストの増大はサービスの拡大を鈍化させる原因となるため、常に最適な状態に保つことが必要です。WEARでも不要なコストを可能な限り削減し適正化すべく、コスト把握と対応を続けています。 コストの把握 まずはコストを把握します。AWSのコストは AWS Cost Explorer(以下、Cost Explorer) で確認できます。WEARでは『Elastic Container Service for Kubernetes』『S3』『CloudFront』に次いで『EC2その他』に料金がかかっていました。『EC2その他』の料金がそこまで高いことは想定外だったため、『EC2その他』の内訳を確認します。フィルターのサービスを『EC2 - Other』、グループ化の条件のディメンションを『APIオペレーション』とすることで、API単位で料金を確認できます。結果は以下のグラフの通りです 1 。 内訳を確認すると、コストの3分の2ほどがNATゲートウェイのコストでした。さらに詳細に内容を確認します。先ほどまでのレポートパラメータを解除し、フィルターの『APIオペレーション』を『NatGateway』、グループ化の条件のディメンションを『使用タイプ』にします。 NATゲートウェイの料金に関するドキュメント を確認すると、『NATゲートウェイあたりの料金(USD/時)』と『処理データ1GBあたりの料金 (USD)』で構成されています。グラフを見ると、NATゲートウェイの起動時間による料金よりも、NATゲートウェイのデータ処理に関する料金が圧倒的に高いことがわかります。つまり、NATゲートウェイを経由して大量の通信が行われているということが読み取れます。 次章では、NATゲートウェイを経由する通信を詳しく調査します。 NATゲートウェイの通信内容の把握 CloudWatchメトリクスでの確認 まずは Amazon CloudWatch(以下、CloudWatch) でNATゲートウェイの CloudWatchメトリクス を確認します。メトリクスの詳細は 公式ドキュメント に記載があります。 WEARでは、 BytesOutToDestination が BytesInFromDestination の2倍ほどの量でした。これは、NATゲートウェイを経由した外向きの通信量が、NATゲートウェイを経由した内向きの通信量の2倍あることを示します。 VPCフローログでの確認 次に、 VPCフローログ を用いて、より詳細な通信内容を確認します。VPCフローログにはVPC内部のネットワークインターフェイス間の通信内容が記録されています。 Amazon S3(以下、S3) バケットに出力されたVPCフローログを Amazon Athena(以下、Athena) でクエリして通信内容を確認します 2 。 VPCフローログのAthenaテーブルは以下の内容で作成したことを前提に説明します。 VPCフローログテーブル作成クエリ CREATE EXTERNAL TABLE `vpc_flow_logs_table`( `version` int COMMENT '' , `account_id` string COMMENT '' , `interface_id` string COMMENT '' , `srcaddr` string COMMENT '' , `dstaddr` string COMMENT '' , `srcport` int COMMENT '' , `dstport` int COMMENT '' , `protocol` bigint COMMENT '' , `packets` bigint COMMENT '' , `bytes` bigint COMMENT '' , ` start ` bigint COMMENT '' , ` end ` bigint COMMENT '' , `action` string COMMENT '' , `log_status` string COMMENT '' , `vpc_id` string COMMENT '' , `subnet_id` string COMMENT '' , `instance_id` string COMMENT '' , `tcp_flags` int COMMENT '' , ` type ` string COMMENT '' , `pkt_srcaddr` string COMMENT '' , `pkt_dstaddr` string COMMENT '' , `region` string COMMENT '' , `az_id` string COMMENT '' , `sublocation_type` string COMMENT '' , `sublocation_id` string COMMENT '' , `pkt_src_aws_service` string COMMENT '' , `pkt_dst_aws_service` string COMMENT '' , `flow_direction` string COMMENT '' , `traffic_path` int COMMENT '' ) PARTITIONED BY ( `logdate` string COMMENT '' ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' STORED AS INPUTFORMAT ' org.apache.hadoop.mapred.TextInputFormat ' OUTPUTFORMAT ' org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat ' LOCATION ' $LOCATION_OF_LOGS ' TBLPROPERTIES ( ' projection.enabled ' = ' true ' , ' projection.logdate.format ' = ' yyyy/MM/dd/HH ' , ' projection.logdate.interval ' = ' 1 ' , ' projection.logdate.interval.unit ' = ' HOURS ' , ' projection.logdate.range ' = ' 2022/01/01/00,NOW ' , ' projection.logdate.type ' = ' date ' , ' skip.header.line.count ' = ' 1 ' , ' storage.location.template ' = ' $LOCATION_OF_LOGS/${logdate} ' , ' typeOfData ' = ' file ' ) $LOCATION_OF_LOGS はVPCフローログの出力先S3パスに読み替えてください。 WEARではAthenaテーブルの作成を Terraform の aws_glue_catalog_table で行っています。上記のクエリは aws_glue_catalog_table から作成されたテーブルを SHOW CREATE TABLE で出力したクエリのため、公式ドキュメントとは表現が一部異なっています。 次のようなクエリ 3 でVPC内部からNATゲートウェイを経由した外向きの通信を確認します。SQL内にコメントしてある箇所は適宜自分の環境に読み替えてください。 SELECT pkt_dst_aws_service, SUM (bytes) AS bytesTransferred FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 調査対象の日付(UTC) GROUP BY pkt_dst_aws_service ORDER BY bytesTransferred DESC このクエリでは、特定の日付でVPC内部からNATゲートウェイ経由で送信したトラフィック量をAWSサービス 4 ごとに確認しています。この例では1日で調査していますが、Athenaはスキャンしたデータ量に料金が比例します 5 。サービスの規模によってはまず時間単位でクエリし、スキャン量が許容できることを確認してください。 結果の例を以下の表に記載します。 pkt_dst_aws_service が - である箇所はAWSが管理していないIPに向けた通信です。表の数値はWEARの実際の値ではなくダミーデータです。今後出てくるAthenaのクエリ結果はすべて実際の値ではなく、ダミーデータを記載します。 pkt_dst_aws_service bytesTransferred AMAZON 106000 EC2 4500 - 2000 DYNAMODB 1000 CLOUDFRONT 3 GLOBALACCELERATOR 1 続いて、NATゲートウェイを経由してVPC内部で受信するトラフィックも確認します。 SELECT pkt_src_aws_service, SUM (bytes) AS bytesTransferred FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr NOT LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 調査対象の日付(UTC) GROUP BY pkt_src_aws_service ORDER BY bytesTransferred DESC pkt_src_aws_service bytesTransferred EC2 21000 AMAZON 6000 - 5000 CLOUDFRONT 1500 DYNAMODB 1000 GLOBALACCELERATOR 1 この時点で Amazon DynamoDB(以下、DynamoDB) の ゲートウェイエンドポイント が不足しており、NATゲートウェイを経由して通信してしまっていることが読み取れます。 Datadog のように、 IPアドレス範囲を公開 しているサービスを利用している場合、VPCフローログの pkt_dstaddr (送信先IPアドレス)だけで把握も可能です 6 。しかし、VPCフローログのみでは AMAZON の内訳など、具体的にどのエンドポイントに向けて通信しているかがわかりません。これらを把握するため、VPC内部の名前解決の際のクエリログを活用してより詳細に調査します。 リゾルバーでのクエリログでの確認 リゾルバーでのクエリのログ記録 を設定すると、VPC内部で行われた名前解決のクエリログをS3に保存できます。VPCフローログで確認できる送信先のIPアドレスとクエリログの名前解決の結果を突き合わせることで、NATゲートウェイを経由して行われた通信先のドメインが把握できます。 ドキュメント に従ってクエリログのAthenaテーブルを作成します。 リゾルバーのクエリログのAthenaテーブルは以下の内容で作成したことを前提に説明します。 リゾルバーのクエリログテーブル作成クエリ CREATE EXTERNAL TABLE `vpc_dns_query_logs_table`( `version` string COMMENT '' , `account_id` string COMMENT '' , `region` string COMMENT '' , `vpc_id` string COMMENT '' , `query_timestamp` string COMMENT '' , `query_name` string COMMENT '' , `query_type` string COMMENT '' , `query_class` string COMMENT '' , `rcode` string COMMENT '' , `answers` array<struct<rdata:string, type :string,class:string>> COMMENT '' , `srcaddr` string COMMENT '' , `srcport` int COMMENT '' , `transport` string COMMENT '' , `srcids` struct<instance:string,resolver_endpoint:string> COMMENT '' , `firewall_rule_action` string COMMENT '' , `firewall_rule_group_id` string COMMENT '' , `firewall_domain_list_id` string COMMENT '' ) PARTITIONED BY ( `logdate` string COMMENT '' ) ROW FORMAT SERDE ' org.openx.data.jsonserde.JsonSerDe ' STORED AS INPUTFORMAT ' org.apache.hadoop.mapred.TextInputFormat ' OUTPUTFORMAT ' org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat ' LOCATION ' $LOCATION_OF_LOGS ' TBLPROPERTIES ( ' projection.enabled ' = ' true ' , ' projection.logdate.format ' = ' yyyy/MM/dd ' , ' projection.logdate.interval ' = ' 1 ' , ' projection.logdate.interval.unit ' = ' DAYS ' , ' projection.logdate.range ' = ' 2022/01/01,NOW ' , ' projection.logdate.type ' = ' date ' , ' storage.location.template ' = ' $LOCATION_OF_LOGS/$VPC_ID/${logdate} ' , ' typeOfData ' = ' file ' ) $LOCATION_OF_LOGS はリゾルバのクエリログの出力先S3パス、 $VPC_ID はログを記録しているVPCのIDに読み替えてください。 VPCフローログテーブルと同様にAthenaテーブルの作成をTerraformで行っており、上記は SHOW CREATE TABLE で出力したクエリです。そのため、公式ドキュメントとは表現が一部異なっています。 次のようなクエリでVPCフローログとリゾルバーのクエリログを突き合わせます。 SELECT R.query_name, SUM (F.bytesTransferred) AS bytes_day, ROUND ( SUM (F.bytesTransferred) * 30.0 / ( 1000 * 1000 * 1000 ), 2 ) AS GB_months FROM ( SELECT pkt_dstaddr, SUM (bytes) AS bytesTransferred FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 調査対象の日付(UTC) AND pkt_dst_aws_service = ' AMAZON ' GROUP BY pkt_dstaddr ) F LEFT JOIN ( SELECT DISTINCT query_name, answer.rdata FROM " vpc_dns_query_logs_database " . " vpc_dns_query_logs_table " -- リゾルバーのクエリログテーブルが存在するデータベース.リゾルバーのクエリログテーブル CROSS JOIN UNNEST(answers) as st(answer) WHERE answer. type = ' A ' AND logdate = ' YYYY/MM/dd ' -- 調査対象の日付(UTC) ) R ON F.pkt_dstaddr = R.rdata GROUP BY R.query_name ORDER BY bytes_day DESC このクエリでは、大きく分けて3つのことを行っています。 VPCフローログテーブルからNATゲートウェイを経由するトラフィックの送信先IPアドレスを取得(クエリの F 部分) リゾルバーのクエリログテーブルからドメインとIPアドレスの対応表を作成(クエリの R 部分) 1と2をIPアドレスで結合し、送信先ドメインごとのトラフィック量を取得 クエリの結果を確認すると、 firehose.ap-northeast-1.amazonaws.com. と sqs.ap-northeast-1.amazonaws.com. に対するトラフィック量が多いことを確認できました。WEARでは、アプリケーションのログを aws-for-fluent-bit を用いて Amazon Data Firehose(以下、Firehose) に送信しています。このサービスに対する送信がほとんどであり、 インターフェイスVPCエンドポイント が不足していることに気付けました。 Amazon Simple Queue Service(以下、SQS) も同様に、VPCエンドポイントが不足していることが判明しました。 他の箇所についても同様に調査すべく、 pkt_dst_aws_service = 'EC2' などに変更しながらトラフィック量を確認していきます 7 。 その結果、以下のドメインに対しての通信量が多いことを確認できました。 d5l0dvt14r5h8.cloudfront.net. に見覚えはありませんでしたが、AWSサポートに確認したところ、 Amazon ECR パブリックリポジトリ(以下、ECRパブリックリポジトリ) であることが判明しました(2024年7月現在)。 *.datadoghq.com WEARのAPI d5l0dvt14r5h8.cloudfront.net. これで通信内容が判明しました。NATゲートウェイ経由で大量に通信しており、削減効果が見込めそうな宛先は以下の通りです。 AWSサービス Datadog WEARのAPI ECRパブリックリポジトリ 次章からはこの結果を元に、可能な限りNATゲートウェイを経由せずに済むように対応します。 調査結果をもとにNATゲートウェイ経由での通信量を削減する AWSサービスとの通信 AWSサービスをNATゲートウェイ経由で通信させないためには、VPCエンドポイントが必要です。ただし、通信するすべてのAWSサービスに対してVPCエンドポイントを用意すればいいとは限りません。VPCエンドポイントにも起動時間による料金と、データ処理による料金が発生するためです。 インターフェイスエンドポイントの料金 を確認し、 NATゲートウェイで通信する場合の料金 との損益分岐点を確認します。 NATゲートウェイはすでに存在し、外部への通信に利用しています。NATゲートウェイは削除できないため、NATゲートウェイの起動時間に関するコストは考慮しないことにします。また、インターフェイスエンドポイントはAZごとに起動時間の料金がかかります。WEARでは3AZを利用しているため、3つとして計算します。 ap-northeast-1 リージョンの料金は以下のようになっています(2024年7月現在)。 NATゲートウェイの料金表 NAT ゲートウェイあたりの料金 (USD/時) 処理データ 1 GB あたりの料金 (USD) USD 0.062 USD 0.062 インターフェイスエンドポイントの料金表 各 AZ の VPC エンドポイント 1 つあたりの料金 (USD/時間) USD 0.014   AWS リージョンで 1 か月に処理されるデータ 処理データ 1 GB あたりの料金 (USD) 最初の 1 PB 0.01 USD 次の 4 PB 0.006 USD 5 PB以上のもの 0.004 USD 1か月あたりの通信量(GB)を とし、以下の式を満たす を計算します。VPCエンドポイントの起動時間は 24時間*30日*3AZ で計算しています。また、調査時に概算した結果、1ヶ月に1PB以上は使っていないため、最初の1PBの料金で計算します。 すると となるため、月に581GB以上通信するのであればNATゲートウェイ経由よりもVPCエンドポイント経由の方が安いということが導けます。 そのため、WEARではFirehoseとSQSのインターフェイスエンドポイントを追加で作成することにしました。ゲートウェイタイプのVPCエンドポイントの場合は追加料金なしで利用できる 8 ため、DynamoDBのVPCエンドポイントも作成します。 Datadogとの通信 Datadogは AWS PrivateLink(以下、PrivateLink) を経由して通信する方法を提供しています 9 。ただし、調査の過程でWEARではこの方法は断念しました。 Datadogには サイト という概念があります。各サイトは完全に独立しており、サイト間でデータの共有はできません。WEARでは Amazon Elastic Kubernetes Service(以下、EKS) のリージョンと使用しているDatadogサイトの場所が異なっていました。その場合、『VPCピアリングを使用した他のリージョンからの接続』ですが、リージョン間の通信は、2024年7月時点で 1GBあたり$0.09 かかってしまいます。そのため、WEARではDatadogへの通信に関してはNATゲートウェイを経由することを許容しました。ご利用中のDatadogサイトのPrivateLinkとデータ送信元が同一リージョンにある場合はPrivateLinkを用いる方法を検討してみてください。 WEARのAPIとの通信 調査結果から、WEARのWebアプリケーションからAPIへの通信がNATゲートウェイを経由して行われていることがわかりました 10 。通信経路の概略は以下の図の通りです。AWS間の通信のためインターネットには出ていませんが、NATゲートウェイを経由して通信してしまっています。 これらは同一VPCに存在しているため、VPC内部のみで通信を完結させたいところです。WEARのEKS内のPodは Application Load Balancer(以下、ALB) の配下に存在します。調査時点ではインターネット向けのALBのみ存在しましたが内部向けのALBも作成し、VPC内部からの通信は内部向けのALBに対して行うようにします。 WEARでは、ALBとALBのエイリアスレコードの作成を AWS Load Balancer Controller と ExternalDNS を用いてIngressに専用のアノテーションを付与することで行っています。 既存のIngressを踏襲し、新たに内部向けALB用のIngressを作成します。 alb.ingress.kubernetes.io/scheme アノテーションのデフォルト値は internal ですが、後述する理由により明示的に internal を設定しておきます。 以下に内部向けALB作成のサンプルIngressを記載します。また、内部向けALBを作成するプライベートサブネットに自動検出用のタグ 11 が付与されていることを確認してください。 apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : api-internal namespace : api annotations : kubernetes.io/ingress.class : alb alb.ingress.kubernetes.io/scheme : internal external-dns.alpha.kubernetes.io/hostname : api.wear.jp # インターネット向けIngressのものと同じ # (以下略) spec : rules : - http : paths : - path : / pathType : Prefix backend : service : name : api port : number : 80 次に、作成する内部向けALBのエイリアスレコードを登録するためのプライベートホストゾーンと、プライベートホストゾーン用のExternalDNSを新たに作成します。プライベートホストゾーン名はエイリアスレコードのレコード名と一致させます。 ここで1つ注意しなければならないことがあります。 すでに通信が行われているドメインに対して新たにプライベートホストゾーンを作成する場合は、エイリアスレコードを作成するまではEKSのVPCにプライベートホストゾーンを関連付けてはいけない ということです。 プライベートホストゾーンを作成した時点で、関連付けされているVPC内部の通信はそのプライベートホストゾーンで名前解決を試みます。しかし、プライベートホストゾーンの作成とエイリアスレコードの作成は同時にできないため、名前解決に失敗してしまいます。プライベートホストゾーンは作成時に必ず1つ以上のVPCを関連付けなければならないため、使用していないVPCのみを一時的に関連付けておきます。 以下に Terraform を用いたサンプルコードを記載します。ここでは、使用していないデフォルトのVPCを一時的にプライベートホストゾーンに関連付けしています。 resource " aws_route53_zone " " private_api " { name = " api.wear.jp " vpc { # 一時的にdefault VPCを指定。 vpc_id = data.aws_vpc.default.id vpc_region = " ap-northeast-1 " } force_destroy = false } # 一時的にdefault VPCを指定するためのデータソース data " aws_vpc " " default " { default = true } 同じドメインでパブリックホストゾーンとプライベートホストゾーンを出し分けるためにプライベートホストゾーン用のExternalDNSを新たに作成します 12 。以下のオプションで起動します。 --aws-zone-type=private --annotation-filter=alb.ingress.kubernetes.io/scheme=internal --domain-filter=${プライベートホストゾーン名} 内部向けALBのためのアノテーションがついているリソースのみを対象に設定しています。これが、デフォルト値にもかかわらず明示的にIngressにアノテーションを設定する理由です。 元々起動していたパブリックホストゾーン用のExternalDNSには --annotation-filter=alb.ingress.kubernetes.io/scheme=internet-facing を用いて再起動し、インターネット向けALB用のリソースのみを対象にします。 ExternalDNSの準備ができたら内部向け用IngressをEKS内に作成し、内部向けALBとALBのエイリアスレコードが作成されていることを確認します。一時的に関連付けておいたVPC内部からdigコマンド等で名前解決し、プライベートアドレスに解決されることも確認しておきます。確認後、プライベートホストゾーンをEKSのVPCに関連付けし、一時的なVPCの関連付けは解除します。 以下にサンプルコードを記載します。ここでは、プライベートホストゾーンに関連付けされているVPCを、使用していないVPCからvariablesに設定されたEKSのVPCに変更しています。 variable " vpc_id " { type = string description = " EKSのVPC ID " } resource " aws_route53_zone " " private_api " { name = " api.wear.jp " vpc { vpc_id = var.vpc_id vpc_region = " ap-northeast-1 " } force_destroy = false } 最終的な通信経路の概略は以下の図の通りです。これで、WEARのWebアプリケーションからAPIへの通信がVPC内部で完結するように設定できました。 ECRパブリックリポジトリとの通信 WEARでは、aws-for-fluent-bitを初めとする、複数のコンテナイメージでECRパブリックリポジトリのものを多用しています。 ドキュメント には以下のような記載があります。 現在、VPC エンドポイントは Amazon ECR パブリックリポジトリをサポートしていません。プルスルーキャッシュルールを使用して、VPC エンドポイントと同じリージョンにあるプライベートリポジトリでパブリックイメージをホストすることを検討してください。 上記の案内通り、プルスルーキャッシュルールを使用することにします。プルスルーキャッシュルールを使用すると、DockerHubやECRパブリックなどにあるリポジトリを自分のAWSアカウントのプライベートリポジトリにキャッシュしておくことができます。あらかじめ手動でイメージをプッシュしておく必要はなく、自分のリポジトリからプルしようとした際にイメージが存在しなければ、自動的に設定先のリポジトリからプルしてイメージを格納しておいてくれます。 詳細は ドキュメント をご参照ください。また、WEARでは Amazon Elastic Container Registry(以下、ECR) 用のVPCエンドポイントはすでに作成してあったため、新たに作成する必要はありませんでした。 以下にTerraformを用いたサンプルコードを記載します。 resource " aws_ecr_pull_through_cache_rule " " ecr_public " { ecr_repository_prefix = " ecr-public " upstream_registry_url = " public.ecr.aws " } 設定完了後、マニフェストのイメージを以下のように書き換えます。 $ACCOUNT_ID はプライベートリポジトリが存在するAWSアカウントのIDです。また、WEARでは元々DatadogのコンテナイメージをHelmのデフォルト値である gcr.io/datadoghq からプルしていましたが、このタイミングでECRに切り替えました 13 。 - image: public.ecr.aws/aws-observability/aws-for-fluent-bit + image: $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/aws-observability/aws-for-fluent-bit これで、初回プル時にはNATゲートウェイを経由してイメージがプルされますが、その後はVPCエンドポイント経由でプライベートリポジトリからプルされるようになりました。 結果 対応完了後、NATゲートウェイを経由する通信量がどのくらい減ったのかを確認します。まずは、以下のクエリでVPC内部からNATゲートウェイを経由した外向きの通信に対して対応前後の削減量を確認します 14 。 SELECT B.pkt_dst_aws_service AS pkt_dst_aws_service, ROUND ( CAST (B.bytes_day- COALESCE (A.bytes_day, 0 ) AS double)/B.bytes_day* 100 , 2 ) AS Reduction_percentage FROM ( SELECT pkt_dst_aws_service, SUM (bytes) AS bytes_day FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 対応前の日付(UTC) GROUP BY pkt_dst_aws_service ) B LEFT JOIN ( SELECT pkt_dst_aws_service, SUM (bytes) AS bytes_day FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 対応後の日付(UTC) GROUP BY pkt_dst_aws_service ) A ON B.pkt_dst_aws_service = A.pkt_dst_aws_service ORDER BY B.bytes_day DESC pkt_dst_aws_service Reduction_percentage AMAZON 99.91 EC2 -2.82 - -2.14 DYNAMODB 100.0 CLOUDFRONT 93.75 GLOBALACCELERATOR 100.0 結果を確認すると、 AMAZON への通信量が99.91%、 CLOUDFRONT が93.75%、 DYNAMODB への通信量が100.0%削減できていることがわかりました。VPCエンドポイントがうまく作用しているようです。増えている箇所もありますが、通信量は日によって誤差があるため対応によるものではありません。NATゲートウェイを経由してVPC内部に受信する通信に関しても確認します。 NATゲートウェイを経由する内向き通信の削減量確認クエリ SELECT B.pkt_src_aws_service AS pkt_src_aws_service, ROUND ( CAST (B.bytes_day- COALESCE (A.bytes_day, 0 ) AS double)/B.bytes_day* 100 , 2 ) AS Reduction_percentage FROM ( SELECT pkt_src_aws_service, SUM (bytes) AS bytes_day FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr NOT LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 対応前の日付(UTC) GROUP BY pkt_src_aws_service ) B LEFT JOIN ( SELECT pkt_src_aws_service, SUM (bytes) AS bytes_day FROM " vpc_flow_logs_database " . " vpc_flow_logs_table " -- VPCフローログテーブルが存在するデータベース.VPCフローログテーブル WHERE srcaddr NOT LIKE ' x.y.% ' -- VPC CIDRのネットワーク部分(例:VPC CIDRが`172.168.0.0/16`の時、`172.168.`) AND dstaddr in ( ' x.y.a.b ' , ' x.y.c.d ' , ' x.y.e.f ' ) -- NatGateway IP AND action = ' ACCEPT ' AND logdate BETWEEN ' YYYY/MM/dd/00 ' AND ' YYYY/MM/dd/23 ' -- 対応後の日付(UTC) GROUP BY pkt_src_aws_service ) A ON B.pkt_src_aws_service = A.pkt_src_aws_service ORDER BY B.bytes_day DESC 結果は以下の通りです。WEARのAPI( EC2 )への通信がVPC内部で完結したこと、プルスルーキャッシュルールによって CLOUDFRONT や外部サービスへの通信回数が減ったことで通信量が大幅に減っています。 pkt_src_aws_service Reduction_percentage EC2 89.15 AMAZON 98.22 - 63.65 CLOUDFRONT 99.14 DYNAMODB 100.0 GLOBALACCELERATOR 100.0 Cost Explorerで対応前後の1日毎のグラフを確認します。最終的に対応が完了したのは6/6頃です。グラフの通り、大幅にコストを減らせました。NATゲートウェイのコストだけで言うと、80%ほど削減できました。 しかし、NATゲートウェイを経由しなくなった分、VPCエンドポイントのコストが増えているはずです。そちらも確認します。APIオペレーションに『VpcEndpoint』も追加し、グラフを確認します。 VPCエンドポイントの通信コストを加味しても、コストを大幅に削減できています。対応前のNATゲートウェイとVPCエンドポイントの総額で計算すると、最終的には60%ほど削減できました。 まとめ 本記事ではNATゲートウェイの通信内容の調査と通信量の削減方法について紹介しました。VPCフローログとリゾルバーのクエリログを確認することで詳細な通信内容を把握できました。通信内容に応じて適切な対応をした結果、約60%のコストを削減できました。NATゲートウェイのコスト削減を検討している方がいれば、ぜひ参考にしてみてください。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com 結果画像のy軸はマスク処理を施してあります。今後出てくるCost Explorerの画像はすべてy軸がマスク処理済みのものです。 ↩ Amazon VPC フローログのクエリ ↩ 参考: サンプルクエリ - Amazon CloudWatch Logs ↩ ここでのAWSサービスはすべてのサービス名ではなく、VPCフローログの pkt-src-aws-service フィールドの値で表示されるもの(参考: VPC フローログを使用した IP トラフィックのログ記録 - Amazon Virtual Private Cloud ) ↩ Amazon Athena の料金 ↩ Datadogへの通信は、ほとんどが pkt_src_aws_service = 'EC2' に内包されています。 ↩ わかりやすさのため pkt_dst_aws_service ごとにクエリを実行していますが、このカラムにはパーティションが設定されていないため、この条件句によってスキャン量を減らすことはできません。Athenaのスキャン量による料金を減らしたい場合、 pkt_dst_aws_service にパーティションを設定することを検討するか、この条件を削除し、1回のクエリですべてを出力してください。 ↩ ゲートウェイエンドポイント - Amazon Virtual Private Cloud ↩ AWS PrivateLink を介して Datadog に接続する ↩ 背景: WEAR Webフロントエンドリプレイスのアーキテクチャ選定とNext.jsへの移行 ↩ キー名: kubernetes.io/role/internal-elb 、値:1のタグが必要(参考: Amazon EKS でのアプリケーション負荷分散 ) ↩ 参考: ExternalDNSでPrivate Hosted ZoneとPublic Hosted Zoneにレコードを出し分ける | DevelopersIO ↩ Docker 環境のコンテナイメージ ↩ わかりやすさのために1つのクエリにしていますが、対応前の結果は最初にクエリした際どこかにメモしておき、対応後の日付だけクエリして手動で比較する方がAthenaの料金上良いと思います。 ↩
こんにちは、ZOZOTOWN開発本部でZOZOTOWN iOSの開発を担当している らぷらぷ です。 今年のWWDCもワクワクする情報が目白押しでしたね。個人的にはApple Intelligenceが今後どんな進化を果たし、日常生活をどう変えていくのかが楽しみです。 本記事では、ZOZOのiOSエンジニアが注目したセッションや、参加したラボで得た知見を紹介します。 現地参加されたメンバーによるレポートもありますので、ぜひ御覧ください。Appleのスタッフや各国の開発者との交流や、現地に行く人向けのアドバイスなどまとまっております。 techblog.zozo.com オンライン組メンバーのキャッチアップ Sessions UI updates Translation API Apple Vision Pro Labs Xcodeによって実行されるResolve Packagesの時間について SPM移行で遭遇したバグや不明点を聞いてみた メモリ関連で気になることをひたすら聞いてみた まとめ オンライン組メンバーのキャッチアップ 現地に参加しなかったオンライン組メンバーは、各自キャッチアップした情報をMiroに集め、毎日30分から1時間ほどビデオ通話でシェアし合いました。このブログに書きたいセッション、ラボで聞きたいこと、社内の他のチームにも共有したいこと、いち開発者として気になることなど、思い思いに話しました。 特にセッションは「可能な限り全部見たい!」という気持ちになりますが、KeynoteとPlatforms State of the Union、Recapを除くと全117セッションあります。とはいえ、見るべきセッションを選ぶのはなかなか難しいです。最初の2、3分を見て判断しようかな…と見始めたら最後まで見てしまったこともあります。 セッションの要約があればじっくり見るべきか判断しやすいかも、ということで今年はAIチャットボットにセッションの字幕を要約してもらってから内容を深ぼるか判断しました。例として「 Bring your app to Siri 」を挙げましょう。概要は以下のように記載されています。 Learn how to use SiriKit and App Intents to expose your app's functionality to Siri and Apple Intelligence. Discover which intents are already available for your use, and how to adopt App Intent domains to integrate actions from your app into the system. Find out what metadata to provide, how to make your entities searchable with Spotlight, how to integrate your app with in-app search, and much more. 以下が、こちらのセッションの字幕をAIチャットボットに要約してもらった結果です。 .typesquare_tags ol {width: calc(100% - 1em) !important; margin-left:0 !important;} .typesquare_tags ul {margin-left:1em !important; width: calc(100% - 3em) !important;} Apple IntelligenceとAssistant Schemasの導入により、Siriとアプリケーションのインテグレーションがより強力かつ柔軟になりました。開発者は新しいAPIを活用することで、より自然で個人化されたユーザー体験を提供できるようになります。この新しい機能は、iOS 18以降で順次展開される予定です。 既存のフレームワーク: SiriKit: iOS 10で導入され、システムが提供するインテントを使用します。例えば、音楽の再生やテキストメッセージの送信などの一般的なアクションに適しています。 AppIntents: iOS 16で導入され、より柔軟なアプリ統合を可能にします。SiriKitのドメインと重複しない機能に適しています。 Apple Intelligenceによる改善: 新しい大規模言語モデルにより、以下の改善がなされています: より自然な会話 文脈理解の向上 個人化された体験 画面上の情報の理解と活用 新しいApp Intent domains: 12の新しいドメイン(メール、写真、カメラ、スプレッドシートなど)が導入され、100以上の新しいアクションをサポートします Assistant Schemas: 事前定義された「スキーマ」に基づいてインテントを実装します。これにより、コードが簡素化され、コンパイル時の検証が可能になります 個人コンテキスト機能: アプリ内検索: ShowInAppSearchResultsIntentを拡張し、Siriがアプリの検索機能を直接利用できるようになります。 セマンティック検索: 新しいIndexedEntityAPIを使用して、アプリのコンテンツをSiriの意味検索に利用可能にします。 テストと実行: ショートカットアプリを使用して、新しいインテントをテストできます。 将来的には、これらのインテントが自動的にSiriと連携するようになります。 これだけ情報が揃っていると、動画を見てさらに掘り下げるかどうかを判断しやすくなります。掘り下げる場合、このようにセッション動画の構成と詳細が分かっていると、集中して視聴すべきパートを絞ることができます。さらに、要約文内の気になるキーワードについてAIチャットボットに質問しておくことで理解を深められます。 今回この方法を試したことで、動画中のスライドによる解説を理解する時間をグッと短縮できました。 Sessions UI updates ZOZOTOWNのiOSアプリの開発を担当している荻野と、WEARのiOSアプリの開発を担当している山田です。 今回の発表でも、SwiftUIの幅広いアップデートがありました。これまでは、SwiftUIの機能が少ないため社内導入を見送っていた人もいると思います。しかし、SwiftUIはますます使いやすくなっており、もう見逃すことはできなくなってきているのではないでしょうか。まだセッションを見ていない方、まずは「 What’s new in SwiftUI 」からご覧ください。 特に注目すべきは、ズームトランジションのアニメーションが新たに加わったことです。ズームトランジションとは、別の画面へ遷移する際に画面がズームするようなアニメーションです。ZOZOTOWNやWEARでは以前から独自にズームトランジションを実装していましたが、今回のアップデートで標準のズームトランジションが利用できるようになったのは大きな進歩です。さらに、SwiftUIだけでなくUIKitでも対応しているのは非常に嬉しい発表でした。詳細は「 Enhance your UI animations and transitions 」のセッションで確認でき、ドキュメントベースでは「 Enhancing your app with fluid transitions 」で、Viewの状態変化を図を用いてわかりやすく説明しています。ZOZOTOWNのiOSアプリでもズームのアニメーションを行なっている部分があるので、今後の改善に活かしていきたいです。 また、「 Add personality to your app through UX writing 」で紹介されたアプリに個性を出すためのUXライティングにも注目しました。このセッションは、アプリ内の文章をデザインするときに考える声・トーンの一貫性、いわゆるトンマナをAppleのHuman Interface Designチームがどう合わせているのかを解説しています。 具体的には、アプリを人に見立てたとき、どんな性格になるか形容詞を考え、それらをグルーピングします。これがアプリの声の定義となり、声の定義を満たすように文章を作ります。例えば、お金を貯めることが目的のアプリでも、子供向けの貯金アプリか長期的な投資を支援するアプリかどうかで、表現するべき言葉はまったく異なります。 ZOZOTOWNやWEARでも、それぞれのアプリでお客様が洋服を買う・コーディネートを投稿するモチベーションを最大化できるような文章を作るように心がけています。セッションを通してまだまだ改善できる部分があると感じたので、デザイナーやビジネスサイドにもこのセッションを共有しようと思います。 Translation API こんにちは、FAANS部iOSチームの加藤です。今年のWWDCでは英語が苦手な私にとって、とても嬉しいAPIの紹介がありました。そうです! Translation APIです! Translation APIに関するセッションは「 Meet the Translation API 」です。ぜひご覧ください。 Translation APIはAppleの新しいAPIであり、Swiftで作られたアプリに強力な翻訳機能を簡単に組み込むことができます。Translation APIは、Appleの翻訳アプリにおいて翻訳対象である20言語に対応しており、入力された言語の種類を自動で認識して、任意の言語に翻訳できます。また、翻訳したい言語の翻訳モデルを事前にダウンロードしておくことで、オフラインの環境でも翻訳が行える点も非常に便利です。 ZOZOの開発するアプリでは、アイテムの詳細説明や、アイテムに対するレビューを閲覧できる機能が備わっています。日本語を読めないユーザーがZOZOのアプリを使用する場合には、アイテムの詳細説明やレビューを理解することが難しく、アプリに掲載されているアイテムの良さが伝わらないかもしれません。こういった問題も、Translation APIを用いた翻訳機能を導入することで解決でき、より多くのユーザーがZOZOのアプリを使ってくれるきっかけになるかもしれません。 私も英語が苦手で英語との間に分厚い壁を感じていたので、Translation APIを組み込んだアプリがこの壁を打ち破ってくれることをとても楽しみにしています。 Apple Vision Pro ARやVRといったXR領域のリサーチや検証などを担当している創造開発ブロックの @ikkou です。頭はひとつしかないのにVRヘッドマウントディスプレイやARグラスはたくさん持っています。昨年のWWDC23で発表されたApple Vision Proがいよいよ日本でも発売されました。 去年のWWDC23レポート記事 で私は『決してお安いお買い物ではありませんが、アーリーアダプター気質のある開発者は間違いなく買うでしょう。もちろん私も買います。』と書きましたが、予告通りApple Vision Proを買いました。当時は約50万円想定だったものが円安の影響で約60万円になったのは想定外の出費となりましたが、今も毎日使っています。家族の一瞬を立体的に残せる空間ビデオは最高ですし、この文章もApple Vision ProをMacの仮想ディスプレイとして利用して書いています。サンクコスト効果も働いていますが、少なくとも現時点では “装着する” というひと手間をかけるだけの価値があるデバイスだと感じています。 そんなApple Vision Proですが、Appleが力を入れていることはWWDC24のvisionOSに関連するセッション数の多さからも伝わってきます。非常に多くのvisionOS関連セッションが用意されていますが、中でも「 Design great visionOS apps 」は必見です。Appleのプラットフォームにとって初めての空間コンピューティングであるApple Vision Proに最適化したアプリを作るための重要なリファレンスとなるセッションです。 また、空間コンピューティングが当たり前になった世界では、ブラウザの世界も2Dから3Dに変容を遂げます。つまりvisionOS対応アプリをリリースせずとも、空間Webへの対応が当たり前になる世界線も十分に考えられます。そういった観点から「 Optimize for the spatial web 」セッションも非常に重要です。このセッションはiOSエンジニアだけでなくWebフロントエンドエンジニアも認識しておくべき内容に溢れています。 Apple Vision Proは非常に高価なデバイスであるため、キャズムを超えるまで時間がかかると考えています。そういった状況で今すぐvisionOS対応アプリを開発するべきか悩ましいかもしれませんが、開発者視点だけで言えば着手するべきだと考えています。盛り上がりを形作る今が特に楽しい時期です。もちろんビジネス的な側面で言えば、まだまだ母数が少ないであろうApple Vision Proに開発リソースを割くのであれば、別のところに割くべき、という考え方もあります。しかし、長らくXRを推している身としては、今こそ機運が高まって欲しいと願わずにはいられません。 Labs Xcodeによって実行されるResolve Packagesの時間について ZOZOTOWN開発本部のとしです。今回はXcodeによって実行されるSPMのResolve Packagesに毎回かなりの時間がかかっていることが、開発をしていく中でネックになっていたので、この問題について相談しました。 結論から言うとこのLabを通じて、クリティカルな解決方法が見つかったわけではありません。しかし、画面を共有することでAppleのエンジニアが意図していない挙動であること、ワークアラウンドを発見できたこと、逆に自分たちが考えていた手法は実行できないということなどが分かりました。このLabでの結果によって今後の調査のアプローチの方向性がわかったので意義のあるLabになりました。 やはり、Apple側としてもその不具合を再現するプロジェクトとFeedback Assistantを使用したBug Reportingがあるとかなり助かるようでした。 SPM移行で遭遇したバグや不明点を聞いてみた 計測プラットフォーム開発本部の中岡です。私は以前執筆した計測フレームワークをCocoaPodsからSPMに移行する作業の中で遭遇したバグや不明点について2つ質問しました。 techblog.zozo.com 1つ目がObjective-CのSwift Packageのtarget.nameに”-”が含まれている場合、リソースバンドルのアクセスに失敗する問題です。ラボの前に自分なりにOSSのSwift Package Managerを調査して原因となっているであろう箇所を 修正するPR を提出していました。そしてこれについて議論をしたのですが、結果としてSwift Pacakge ManagerではなくXcode側のバグだろうとのことでした。そのためLabsが終わった後にバグを再現させるプロジェクトを作りフィードバックを送りました。 2つ目がswiftSettingsについてです。計測フレームワークにはswiftSettingsを使った分岐処理があり「これを利用する側から設定を変更できないか」という相談をしました。結果として、現状Swift Packageにはそのような機能はありませんでした。しかし以下のforumsでPackage traitsという機能が議論されていることを教えていただきました。まさに求めていた機能なので今後に期待です。 forums.swift.org メモリ関連で気になることをひたすら聞いてみた ZOZOTOWN開発本部のらぷらぷです。最近メモリ効率を上げるための調査や対応に興味があります。Appleのエンジニアはどんな関心を持ってメモリ効率について学んでいるのかが気になり、Performance, power, and stability consultationにてお聞きしました。 お話ししたエンジニアの方曰く、まずはメモリを観察することを好きになり、毎日メモリ使用量を観察して妥当な使用量を見極め、異常値が発生してから分析することを続けましょうとのことでした。よく考えればiOSに限らないパフォーマンスチューニングの観点と一緒ですね。 その他、Memory graphの生成の詳細を解説してくれたり、「 Analyze heap memory 」を例にメモリー安全なコードを紹介してくれたりしました。メモリー安全の話からSwift Concurrencyの話に移り、InstrumentsでSwift Concurrencyをデバッグする方法も教えていただきました。 次々と会話が進み、30分では足りない時間を過ごしました。 具体的なコードの課題をもっていなくても、こうしてAppleのエンジニアの考えていることを聞けて、そこからセッションの視聴を提案されるのもラボの魅力のひとつです。 まとめ 本記事では、WWDC24における弊社iOSエンジニアの取り組み、注目したセッション、ラボから得た知見をお伝えしました。今年も数多くのセッションやラボから学びを得ることができました。これらをZOZOのサービスの進化にどう活かすか、今後の課題として取り組んでいきます。 ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co corp.zozo.com
はじめに こんにちは、推薦基盤ブロック、新卒1年目の 住安宏介 です。普段は推薦システムの開発・運用を担当しています。 2024年6月に開催されたコンピュータビジョン・パターン認識分野において世界最高峰の国際会議の1つであるCVPR(Conference on Computer Vision and Pattern Recognition)2024に参加しました。参加レポートとして発表内容や参加した感想を紹介いたします。また、最後にZOZO NEXTが行っているワークショップのスポンサー活動についてZOZO Researchの清水から紹介いたします。 目次 はじめに 目次 CVPR とは 開催地のシアトルについて 学会のスケジュール 企業展示ブースの様子 ポスターセッションの雰囲気 採択数増加に伴うポスターセッションの懸念とその実際 特に、印象に残った研究発表 SLICE: Stabilized LIME for Consistent Explanations for Image Classification [Highlight] CAM Back Again: Large Kernel CNNs from a Weakly Supervised Object Localization Perspective Comparing the Decision-Making Mechanisms by Transformers and CNNs via Explanation Methods [Award Candidate] Learning to Rank Patches for Unbiased Image Redundancy Reduction Identifying Important Group of Pixels using Interactions CVFAD におけるスポンサーの取り組み さいごに CVPR とは 先述の通り、 CVPR はコンピュータビジョン・パターン認識分野における世界最高峰の国際会議の1つです。近年、CVPRはそのプレゼンスを大きく高めており、採択された論文の影響力も著しく向上しています。Google Scholarのh5-indexによる全分野の学術誌・国際会議のランキングで、Nature、New England Journal of Medicine、Scienceに次いで4位にランクインしています。このことは、CVPRにおける研究成果が、量だけでなく質においても非常に高く評価されていることを示しています。CVPRは、そのプレゼンスの高まりとともに、コンピュータビジョンとパターン認識の分野における最新の研究成果を発表する場として、世界中の研究者や開発者から高い注目を集めています。 開催地のシアトルについて 開催地となったシアトルは、美しい自然環境に恵まれた都市として有名です。例えば、日本で人気のコーヒーの名前の由来となったマウント・レーニアや、ユネスコ世界遺産に登録されているオリンピック国立公園があります。また、シアトルは世界的に有名なテック企業が集まる都市でもあります。そのため、CVPRに参加された研究者の間では、これらの企業のオフィスツアーに参加する動きも見られ、活発な交流が行われていました。 シアトルの街並み 会場となったシアトルコンベンションセンター 学会のスケジュール 学会の スケジュール は以下のように構成されています。 1日目から2日目 ワークショップ チュートリアル 3日目から5日目(本会議) キーノート パネルディスカッション オーラル発表 ポスター発表 企業展示 AIアートギャラリー この中で特に興味深かった企業展示ブースの様子、ポスターセッションの雰囲気、そして私が予期していた採択数増加に伴うポスターセッションの懸念とその実際について紹介します。 企業展示ブースの様子 企業展示ブースでは、各会社が最新の研究成果や実際の活用事例を紹介していました。50を超える企業が参加しており、どのブースも大変賑わっていました。また、展示内容も非常に興味深くワクワクするものばかりで、とても楽しい時間を過ごせました。特に興味深かったのは Zoox の無人運転車両です。この車両は運転席が不要であるため、興味深いことに座席が対面で配置されていました。私はペーパードライバーであるため、導入されることを楽しみに待っています。 企業デモの様子 Zooxの無人運転車両 ポスターセッションの雰囲気 ポスターセッションとは、発表者が研究内容をポスター形式で展示し、聴講者と直接対話しながら説明と議論をする発表形式です。最も権威のある国際会議の1つであるため、参加者の熱気がとても感じられました。また、発表された研究の多くが魅力的であり、一人の発表者に多くの聴講者が集まる場面も多々見られました。多数の聴講者に対応するため、チームで発表するケースも見られました。 国内の学会では最初から最後まで説明し終えた後に質問を受ける流れが多いですが、今回は概要を話してから適宜聴講者の質問を元に会話するという形が主流でした。私もこの形で大学院時代の研究内容を発表しました。 実際のポスターセッションの様子1 採択数増加に伴うポスターセッションの懸念とその実際 本会議での論文採択数は年々増加しており、すべての興味のある論文を把握しきれない、またはすべての興味のあるポスターを聴講できない可能性が懸念されました。今年のポスターセッションは各90分で、約450件のポスターが展示されていたため、単純計算で1つのポスターにかけられる時間は約12秒しかありません。このため、事前に聴講するポスターを決めておくなどの工夫が必要でした。 しかし、この懸念にもかかわらず、ポスターセッションは予想以上に効率よく回れました。その理由の1つに、CVPRが提供する採択論文の推薦システムを活用することで、事前に興味のある論文を特定できたことが挙げられます。このシステムにより、興味のある論文を事前にチェックするのが非常に容易になりました。 また、興味のあるポスターが会場内で分散していると移動や探索が大変になる可能性がありました。ですが、分野ごとにポスターが配置されていたため、関連する研究を効率的に回れ、思ったよりも負担ではありませんでした。実際に、私はセッション内で大体6から9件ほどの研究を聴講でき、満足感が高かったです。 実際のポスターセッションの様子2 www.scholar-inbox.com 特に、印象に残った研究発表 以上では、会議中の様子を紹介しました。ここからは、私が興味を持っているコンピュータビジョン分野の深層学習モデルの説明可能性について紹介いたします。 SLICE: Stabilized LIME for Consistent Explanations for Image Classification [Highlight] Revoti Prasad Bora、Kiran Raja、Philipp Terhörst、Raymond Veldhuis、Raghavendra Ramachandra 深層学習モデルの説明可能性を高めるための有名な手法の1つとして、LIME(Local Interpretable Model-agnostic Explanations)という手法があります。LIMEとは、Deep Neural Networkなどの複雑なモデルを線形モデルのような簡単なモデルで局所的に近似することによって、各特徴量の貢献度を測定します。しかし、LIMEの説明には一貫性がないという問題があります。例えば、同じ入力データに対してLIMEを複数回適用した場合でも、適用ごとに特徴量の貢献度のランキングが異なったり、貢献度の符号の正負が逆転したりすることがあります。 この論文では、LIMEの手法を改善し、一貫した説明を提供できるように、SLICE(Stabilized LIME for Consistent Explanations)という手法を提案しました。この手法では、LIMEの説明において貢献度が正と負に変動する特徴を除去することにより、説明の安定性を向上させています。具体的には、符号エントロピーに基づいて不安定な特徴を特定し、それらを除去することで、一貫性のある貢献度を求めています。 結果を見ると、このSLICEという手法は平均実行時間が通常のLIMEの約4倍かかります。LIMEより安定した説明ができるため、今後の説明手法の選択肢の1つになると考えています。 CAM Back Again: Large Kernel CNNs from a Weakly Supervised Object Localization Perspective Shunsuke Yasuki、Masato Taki 近年、CNN(Convolutional Neural Network)の性能向上を目指してカーネルサイズを拡大したラージカーネルCNNが注目されています。先行研究による分析では、性能向上の理由としてCNNのカーネルサイズの拡大により、CNNが広範囲の特徴から情報を集めるようになり、そのような有効受容野の拡大が原因だと述べられていました。 この研究では、実際にラージカーネルCNNの性能向上の理由が有効受容野の拡大であるのか検証し、その妥当性を弱教師ありオブジェクトローカリゼーション(WSOL)の観点から網羅的に評価しました。実験結果から、ラージカーネルCNNの精度向上の理由が有効受容野の拡大だけで説明することは難しいことがわかりました。代わりに、著者らはその理由として、特徴マップの改善が原因であることを示唆しました。具体的には、古典的なモデルの判断根拠の可視化手法のCAM(Class Activation Mapping)を使用して通常のCNNとラージカーネルCNNを比較していました。その結果、通常のCNNは局所領域のみを活性化するのに対し、ラージカーネルCNNはオブジェクト全体を活性化することがわかりました。さらに、この傾向を活かしラージカーネルCNNとCAMを単純に組み合わせた手法でWSOLの精度を検証した結果、複雑な構成を持つ最先端のCNNベースのWSOL手法に匹敵する性能を持つことを示しました。 個人的には、古典的な可視化手法であるCAMが、最新のCNNの性能向上の理由を説明していることから、同様に他の説明手法を最新のCNNやViT(Vision Transformer)に対して適用した際に新たな説明が得られるのかが気になります。 Comparing the Decision-Making Mechanisms by Transformers and CNNs via Explanation Methods [Award Candidate] Mingqi Jiang、Saeed Khorram、Li Fuxin 多くの研究で、TransformerとCNNの判断根拠の違いが議論されています。特に、TransformerはCNNよりも形状に着目して判断するという知見があります。この研究では、そのような新たな知見を得るために「sub-explanation counting」と「cross-testing」と呼ばれる評価指標を提案しました。 1つ目の指標のsub-explanation countingは、モデルが画像内の複数の画像パッチを構成的に見て判断しているのか、それとも一部の重要な画像パッチに強く依存しているのかを調べる手法です。まず、MSEs(Minimally Sufficient Explanations)と呼ばれる、完全な画像と同程度の予測確率を持つ最小限の画像パッチセットを取得します。次にそのMSEsの一部の画像パッチを削除して予測確率を計算し、完全な画像の予測確率との尤度比を求めます。もし、尤度比が高ければ、その画像パッチの組み合わせが重要であり、モデルは複数の画像パッチを同時に着目しているため「構成的」と判断されます。逆に、重要な画像パッチを削除すると予測結果が大きく変わるものを「分離的」と判断されます。この手法によると、Transformerは構成的であり、CNNは分離的であることが示されていました。 2つ目の指標のcross-testingは、モデル間の判断根拠の類似性を評価するための指標です。もし、2つのモデルが同様の視覚的特徴に依存している場合、cross-testingの高いスコアを獲得します。その結果、類似したタイプのアーキテクチャや学習法のモデルでは、予測に使用する特徴が似ていることが確かめられていました。 個人的には、実際にアプリケーションに応用する際には、どちらかのアーキテクチャのモデルがベースとなるのか、それともケースバイケースでアーキテクチャを使い分けるべきかが気になります。 Learning to Rank Patches for Unbiased Image Redundancy Reduction Yang Luo、Zhineng Chen、Peng Zhou、Zuxuan Wu、Xieping Gao、Yu-Gang Jiang 深層学習モデルの説明可能性の文脈ではないのですが、類似した研究の方向性として冗長な情報の削減というものがあり、興味深い考え方をしていたので紹介いたします。画像において、近くにあるピクセルがしばしば似た色や明るさを持つことがあり、このような類似した情報を冗長な情報といいます。推論の高速化や効果的な伝送のためには、この冗長性を削減することが重要です。 この研究は、冗長な情報を削減するためにMAE(Masked Auto Encoder)を用いた自己教師あり学習フレームワークLTRP(Learning to Rank Patches)を提案しました。MAEとは、画像パッチをマスクした入力に対して元の入力画像を出力するように再構成することで特徴表現を学習する手法です。この手法は、画像再構成の難易度に差を持つことを知られています。例えば、犬の胴体をマスクしても元々の犬とほとんど同様な再構成は容易ですが、尻尾をマスクすると再構成は難しくなることがあります。著者らはこのような難易度の差に目をつけ、その画像パッチの情報量を測定する指標として再構成の難易度を使用しました。つまり、再構成が難しい画像パッチは重要な情報を含むと考えられ、再構成が容易な画像パッチは冗長な情報と見なすということです。LTRPの具体的な手法は、まず事前学習されたMAEを利用して画像パッチの擬似スコア(再構成画像の崩れ具合のスコア)を生成します。そして、このスコアを疑似ラベルとした教師データにより、各パッチの重要度のランク付けを推論する代理モデルを学習します。この代理モデルにより、任意の入力画像に対し重要な画像パッチを特定でき、特定されなかった画像パッチは冗長な情報として削減できます。 再構成が難しいというネガティブな要素に直面した場合、それを改善する方向で研究が行われると思うのですが、それに情報量を結びつけることでポジティブな効果を生み出そうとした発想がとても興味深いと思いました。 Identifying Important Group of Pixels using Interactions Kosuke Sumiyasu、Kazuhiko Kawamoto、Hiroshi Kera 私が発表した論文を簡単に紹介したいと思います。 画像分類モデルの振る舞いを理解するために、画像内のピクセルが推論に与える影響を貢献度として測定し、可視化することがよく行われています。既存の手法であるGradCAMやAttention rolloutでは、画像内の各ピクセルが推論に与える影響を測定して可視化しています。しかし、貢献度の高いピクセルを集めたピクセル群が必ずしも高い貢献度を持たないことがあります。例えば、海辺にいる鴨の画像分類を考えると、単純には主に鴨の部分にフォーカスすれば良いと考えられます。ですが、鴨は海の上に生息する傾向があるため、背景に写っている海辺の情報を鳥の情報と一緒にモデルに入力した方が分類を補助するために役立つ可能性が高いことが考えられます。実際に従来手法では、鴨の体や嘴といった情報のみを集めていたのですが、確信度が低く、一方で鴨の体だけでなく海などの情報も含めた方がより高い確信度を持つことがわかりました。 この研究では、モデルにピクセル群を入力したときに推論へ大きく影響を与えるピクセル群を特定する手法MoXI(Model eXplanation by Interactions)を提案しています。具体的には、ゲーム理論で用いられるShapley Valueと相互作用値という2つの量を計算することで、個々のピクセルの貢献度に加えて、ピクセル間の協調による貢献度を考慮できる手法となっています。 CVFAD におけるスポンサーの取り組み ZOZO Researchの清水良太郎です。最後に、CVPRで毎年開催されているファッションやアートに関するワークショップ Workshop on Computer Vision for Fashion, Art, and Design (CVFAD)について紹介いたします。CVFADは今年で7回目の開催であり、毎年様々な大学や企業からファッション関連の最新の研究が発表されています。 ファッション関連のAI技術の研究者にとっては毎年欠かすことのできないイベントであり、ZOZO NEXTも今年はスポンサーとして関わりました。今年の採択率は38%と、ワークショップながら厳しい査読プロセスを経て、8件の研究が発表されました。なお、採択された論文は、毎年CVPR WorkshopsのProceedingsに掲載されています。 私個人としても、今年は「A Fashion Item Recommendation Model in Hyperbolic Space」が採択され、現地で発表してきました。この研究は、画像情報を用いた推薦モデルの学習に、双曲空間における距離尺度を導入し、優れた精度を達成したという内容です。双曲空間は、我々の生活しているユークリッド空間と呼ばれる平坦な空間と異なり、曲がった空間の一種です。原点から離れるほど空間が広くなるという特徴から、階層的な構造を有したデータの埋め込みと相性が良いとされています。推薦のデータにおいては、わかりやすい階層構造だけでも、例えばユーザとアイテム。また、アイテム間の人気度から生じる階層構造が存在します。 ファッション画像推薦モデルの学習によって得られる双曲空間のイメージ 本研究では、双曲空間においてファッション画像特徴量を用いた推薦モデルを学習し、その精度や学習されたユーザ・アイテム空間に関する多角的な考察をしました。詳しくは、 論文 をご一読いただけると幸いです。 CVPRはコンピュータビジョン・パターン認識分野において世界最高峰の国際会議であるため、そのワークショップに対する採択の難易度も年々向上しております。特に、一部のワークショップは、一流の国際会議やジャーナルに匹敵するような評価を受けています。実際にGoogle Scholarが発表しているh5-indexを基準にしたランキングでは、(2024年7月5日現在)Computer Vision & Pattern Recognition分野の全学術誌・国際会議を通して7番目の評価をされています。会社の企業理念である「世界中をカッコよく、世界中に笑顔を」を叶えるべく、世界中のファッションに関わるAI研究者や技術者から認知をしてもらうことはとても重要な一歩です。このような場で継続的に研究成果を発表するだけでなく、スポンサー企業として積極的に参加することにより、ZOZO/ZOZO NEXTのプレゼンスの向上に大きく貢献すると考えています。 さいごに 今回は、CVPRに参加した感想や内容の一片をお伝えしました。特に経験して良かったことは、トップ層の研究者や開発者たちが持つ知的好奇心の強さと行動力を直接感じることができたことです。このような熱意に触れられたことは、将来の糧になると感じています。ここで得られた知見や経験を今後の開発に取り入れ、より良いサービス開発をしていきたいと思っています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com hrmos.co
はじめに こんにちは、CISO部の 兵藤 です。日々ZOZOの安全を守るためSOC業務に取り組んでいます。 本記事ではMicrosoftのDefender for Endpointを用いてAppleのmacOSに対してセキュリティ対策するTipsについて紹介します。 また、CISO部ではその他にもZOZOを守るための取り組みを行っています。詳細については以下の「OpenCTIをSentinelに食わせてみた」をご覧いただければと思います。 techblog.zozo.com 目次 はじめに 目次 背景と概要 前提知識 Microsoft Defender for Endpoint導入方法 macOSとWindowsでのDefender for Endpointの機能差分 macOSへのLive Response機能 Live ResponseのBashスクリプトの登録と実行 スクリプトのTips デバイス分離の際の通知スクリプト 子プロセスまで切るスクリプト フォレンジックアーティファクト取得スクリプト まとめ おわりに 背景と概要 ZOZOではmacOSのエンドポイントの保護にMicrosoft Defender for Endpointを利用しています。Microsoft Defender for Endpointは、macOSに対してもエンドポイント保護ができます。 ですが、Windows端末に対して行うことができるインシデント対応時のエンドポイトの機能とmacOSの機能には差分があるため、独自でmacOSに対して分析調査を行う必要があります。そのための簡易なスクリプトを用意し、インシデント対応に活用しています。その事例について本記事で紹介します。 前提知識 Microsoft Defender for Endpoint導入方法 ZOZOではユーザ端末を管理するために、Microsoft Intuneを利用しています。Microsoft Intuneは、Windows端末だけでなくmacOSにもFirewallやGateKeeperなどセキュリティの設定プロファイルやアプリを配ることができます。Microsoftのライセンスを購入している組織はこういった形でmacOSに対してもIntuneで管理することがあるかと思います。 Microsoft Defender for EndpointもIntuneによって配ることが可能です。詳しくは 公式ドキュメント をご覧ください。構成プロファイルが多いですが、必要な構成プロファイルとなるためIntuneで配布する必要があります。 macOSとWindowsでのDefender for Endpointの機能差分 Defender for Endpointを使ったインシデント対応では、UI上の右上の3点リーダーから表示される以下の機能群を使うことが多いと思います。 Windowsでの機能 macOSでの機能 左図はWindowsでのDefender for Endpointの機能群で、右図はmacOSでの機能群です。 大きな違いは「分離スクリプトから強制解放をダウンロード」と「自動調査の開始」です。「分離スクリプトから強制解放をダウンロード」についてはWindows用のバッチファイルなのでmacOSでは使えません。「 自動調査 の開始」についてはDefender for Endpointが、FileやProcess、Driver、通信先、永続化の有無(レジストリ)などを自動で調査してくれる機能です。普段はアラームトリガーで動くのですが、手動でも実行できます。Windows特有の調査項目もあるので、これもmacOSでは使えません。 macOSへのLive Response機能 Defender for Endpointは端末に対してリモートからコマンドベースの操作が可能です。リアルタイムで端末の情報を遠隔から取得できたり、ファイルの取得や削除が可能で、インシデント対応においては何かと便利です。 macOSでもこのLive Response機能を使うことができます。macOSで利用できるコマンドは 公式ドキュメント をご覧ください。Windowsと違ってmacOSでは分離機能、アンチウィルススキャンなどがコマンドで利用可能です。また、 run コマンドについてはmacOS上でbashスクリプトの起動が可能です。 Live ResponseのBashスクリプトの登録と実行 macOSではLive Response機能を利用し端末上でBashスクリプトを利用できます。この機能で用意しておいた便利スクリプトを起動し、SOC対応に活用しています。 このスクリプトはDefender for EndpointのLive Response起動時の画面から登録できます。まずは、右上の「ライブラリへのファイルのアップロード」を選択します。 次にアップロードするファイルを選択し、アップロードします。 アップロード完了後は、Live Responseの library コマンドでアップロードしたスクリプトを確認できます。このLlibraryに保存されているスクリプトは library delete コマンドで消さない限り残るので、一度登録しておけばSOC対応中に何度も呼び出すことが可能です。 この library に存在するスクリプトの起動は以下のコマンドで行います。 run < bash-file in library > ここまでのオペレーションで、macOS上でカスタムしたBashスクリプトを実行できます。 スクリプトのTips スクリプトを侵害された可能性のある端末で回すことになるので、侵害の度合いや対応フェーズによってスクリプトを回す、回さないの判断があると思います。それについてはアラームの出方によって都度判断することをお勧めします。 デバイス分離の際の通知スクリプト Defender for Endpointではデバイスのネットワーク(以降NW)上からの分離が可能です。この機能を使うとデバイスのユーザーはネットワーク通信を用いた操作ができなくなるので、業務が止まってしまいます。出社している場合は同フロアや同僚の助けを借りて対処が可能ですが、リモートワークが主体の弊社ではSOCの対応なのか、NWでの不具合なのか判断がつきません。 Windows環境の場合は、デバイス分離の際にユーザーへ通知する機能 1 が存在します。macOSではこのような機能はありません。 Windowsでのデバイス分離の通知 macOSでもユーザが判断するために、デバイス分離の際にカスタムスクリプトを回すことによって、端末のユーザに通知できます。以下にスクリプトの例を示します。 script_message = ' display dialog "コンピュータウィルスの感染の疑いがあるため、SOCにてPCを隔離いたしました。\n現状の詳細について、緊急のお問い合わせはこちら\n\n<電話番号を記載>" with title "SOC_Alert" with icon caution buttons {"OK"} ' /usr/bin/osascript -e " ${script_message} " 社内の緊急時の連絡先や対応フローに沿って手順を記載すると、ユーザも安心して今後の対応に進むことができると思います。 子プロセスまで切るスクリプト Defender for EndpointのLive Response機能では remediate と言ったコマンドがあります。このコマンドについては以下のように公式ドキュメント 2 に記載されています。 デバイス上のエンティティを修復します。 修復アクションは、エンティティの種類によって異なります。 ファイル: 削除 プロセス: イメージ ファイルを停止、削除する サービス: イメージ ファイルを停止、削除する レジストリ エントリ: 削除 スケジュールされたタスク: 削除 スタートアップ フォルダー項目: ファイルを削除する このコマンドによってプロセスを遠隔から切ることができるのですが、子プロセスまでまとめて切ることができません。 そこでmacOSに存在する便利なコマンド pkill を用いたスクリプトで、親から子プロセスまで切ることができます。 # pkill.bash /usr/bin/pkill -P " $1 " このスクリプトをLive Responseで実行する際には以下のようにします。 run pkill.bash < 親のPID > 不審なプロセスから作成される子プロセスをまとめて切ることができるので便利です。 フォレンジックアーティファクト取得スクリプト Defender for Endpointでは「調査パッケージの収集」の機能を利用すると、フォレンジックに役立つ情報をリアルタイムで収集できます。 lsof や kextstat コマンド出力、 .zsh_history ファイルなど多彩なログを収集可能です。収集できる情報については 公式ドキュメント を参照してください。 この「調査パッケージの収集」機能で目ぼしい情報は収集できます。ですが、各インシデントによっては別途ブラウザのログや、ASL(Apple System Log)、SFLファイルを確認したいことがあります。 これらのアーカイブファイルがどんなログなのか確認したい場合は、JSAC 2022で行われた An Introduction to macOS Forensics with Open Source Software のスライドを参考にしてみるとわかりやすいです。 どういったログなのか理解していないと思わぬ情報の見落としに繋がる可能性があります。そこで、事前に取得できるアーティファクトを確認しておくことが重要です。 例えば「 com.apple.LaunchServices.QuarantineEventsV2 のログはデフォルトでは wget などのダウンロードでは com.apple.quarantine 拡張属性がつかないので記録されない」などです。 これらのmacOSの各種アーティファクトを追加で取得する際に便利なツール AutoMacTC を用いてスクリプト作成しました。 AutoMacTCはセキュリティベンダーであるCrowdStrikeが公開しているmacOSのフォレンジックツールです。このツールを使うことで、macOSの各種ログや設定ファイルを取得できます。 その他のフォレンジックツールはmacOSの性質上、再起動が必要です。そこで、リアルタイムフォレンジックを行えるツールで、各種ログを取得できるAutoMacTCを選定しました。 また、AutoMacTCはmacOSにデフォルトであるPython 3で動作し、追加のパッケージも pip などでインストールしないため、迅速にアーティファクトを取得できます。最近はメンテナンスされていないのが難点ではあるので、その点は注意が必要です。こちらの Issue などのように収集対象が変わっている場合があります。 このツールをDefender for EndpointのLive Responseで使うためには、諸々の準備が必要です。 まずAutoMacTCで利用するmodulesをzip圧縮しておきます。 zip -r modules.zip modules この modules.zip ファイルと automactc.py を事前にLive Responseのlibraryに登録しておきます。そして以下のカスタムスクリプトもlibraryに登録しておきます。 # automactc.bash folder = " /Library/Application Support/Microsoft/Defender/response/ " #Live Responseがputfileコマンドでファイルを置くディレクトリ modulefile = " modules.zip " pyfile = " automactc.py " /usr/bin/unzip -o " ${folder}${modulefile} " -d " ${folder} " /usr/bin/python3 " ${folder}${pyfile} " -m all -o " ${folder} " 登録した後はzipファイルやPythonスクリプトを putfile コマンドで展開し、カスタムスクリプトを run するだけでAutoMacTCが動作します。動作後のアーティファクトは getfile コマンドで取得できます。 追加で様々なアーティファクトを取得でき便利です。 まとめ Defender for EndpointをMacで活用するためのTipsを紹介しました。macOSでのインシデント対応においては、Windowsとは異なる機能差分があるため、独自のスクリプトを用意しておくと便利です。 ZOZOではこれからもSOCの運用体制を整備し、ZOZOの安全性の向上を図っていきたいと考えています。 おわりに ZOZOでは、一緒に安全なサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクから是非ご応募ください! corp.zozo.com デバイス分離 ↩ ライブ応答を使用してデバイス上のエンティティを調査する ↩
はじめに こんにちは。DevRelブロックの @ikkou です。6月26日に「 WWDC24 報告会 at LINEヤフー, ZOZO 」を開催しました。WWDCに参加したLINEヤフーとZOZOのエンジニアが新しく発表された技術や得た知見・情報などを共有するイベントです。今年はオフラインのみで開催しました。 登壇内容まとめ LINEヤフーとZOZOの社員によるLTとゲストを招いてのパネルディスカッションを行い、その後は交流会で盛り上がりました。 なお、本イベントはAppleがNDAを締結した開発者にのみ公表している情報を取り扱っており、参加はApple Developer Programに加入している方に限定して実施しました。本レポートもLTの詳細は割愛し、雰囲気をお伝えできればと思います。 コンテンツ 登壇者 Essential Highlights: SwiftUI updates たなたつ / LINEヤフー What’s new in controls Fuya / ZOZO AccessorySetupKit 鎌倉和弘 / LINEヤフー Swift Charts: Vectorized and function plots yamaken / LINEヤフー What's new in privacy 松原良和 / LINEヤフー Introduction to Swift Testing! shoma / ZOZO What's new in SwiftData 大岡湧汰 / LINEヤフー Panel Discussion freddi / LINEヤフー jollyjoester / メルカリ akkie76 / DeNA 交流会 発表風景 LINEヤフーのたなたつさんによるLT LINEヤフーの松原さんによるLT ZOZOのshomaさんによるLT 発表資料 一部の登壇者については、イベント当日に投影していた資料からNDAに抵触する箇所を省き、公開情報のみで構成した資料を公開しています。 speakerdeck.com Panel Discussion 乾杯で始まり和やかな感じで進行したパネルディスカッション 名物“乾杯er”である jollyjoester さんの「カー→ンパ↑ーイ!」とともにカジュアルな雰囲気でパネルディスカッションがはじまりました。 今回のパネルディスカッションは、LINEヤフーの freddi さんの他、グループ外からのゲストとしてメルカリの jollyjoester さんとDeNAの akkie76 さんの3名で進行しました。私たちのWWDC報告会として社外ゲストをお招きしての実施は初めての試みでしたが、それぞれがWWDC24の現地で感じたことを中心にWWDC24を振り返りました。 交流会 パネルディスカッションの後、交流会を実施しました。昨年に続きApple JapanからTechnology Evangelistの豊田さまにご参加いただき、参加者からのさまざまな質問に答えていただきました。 最後に みなさまご参加ありがとうございました。WWDCの報告会は毎年の恒例イベントです。また来年も開催できることを楽しみにしています! ZOZOでは一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co corp.zozo.com
はじめに はじめまして。2024年に新卒として株式会社ZOZOに入社しました。 佐藤仁 と申します。 この記事は4か月間にわたる内定者アルバイトの体験記です。アルバイトの概要、チームの文化、実際に行ったタスク、反省点、フィードバックをご紹介します。 目次 はじめに 目次 内定者アルバイトについて 内定者アルバイトでの働き方 実際にアルバイトをした部署の概要 ZOZOTOWN開発1部フロントエンドブロックの取り組み 週一回の輪読会 Findy Team+による開発運用の改善 実際に行ったタスク 似合うアノテーション 背景 開発体制 実装箇所 得られた知見 フィードバックと反省点 最後に 内定者アルバイトについて 内定者アルバイトとは内定承諾から入社までの期間にアルバイトの雇用契約を結び、内定先で就業できるものです。 選考時に志望していた職種以外の求人に応募できます。同期を見ているとフロントエンドを志望したけど内定者アルバイトではSREに参画する人もいました。なので専門外の技術を学ぶには良い機会になりそうですね。 内定者アルバイトでの働き方 私は2023年1月末に内々定をいただき、同年12月から翌年3月の大学卒業までの4か月アルバイトを行いました。 ZOZOTOWN開発部門の社員にはフルフレックス制度が導入されていますが、内定者アルバイトはシフト制なので、私は10時から17時、1日6時間勤務を週3日、フルリモートで働きました。 メンターとは朝に相談会の時間を設け、業務での悩み事を共有しました。業務の事以外にも一人暮らしに向けた引っ越しの相談なども付き合ってもらえました。MTGの時間を定期的に用意してもらえたのでフルリモートでも問題なく働けました。 私は新潟にいたので、オフィスへ出勤するのが難しかったのですが、千葉にいる内定者アルバイトの同期は出社して働く人もいました。 実際にアルバイトをした部署の概要 私が参画した部署はZOZOTOWN開発本部ZOZOTOWN開発1部Webフロントエンドブロックです。 受け持つ案件は大規模・中規模案件がメインで、サービスに関わる新機能の実装を主に担当します。2023年12月時点ではスタッフは10名程度で構成されていました。 チームのコミュニケーションはSlackとGoogle Meet、デザインツールはFigma、仕様に関してはConfluenceとMiro、タスク管理にはGitHub Projectsを使っています。 チームで印象的だった取り組みを2つ紹介します。 ZOZOTOWN開発1部フロントエンドブロックの取り組み 週一回の輪読会 毎週金曜日に1時間読書する時間を設け、その後1時間読んだ内容について議論し、チームに導入できることはないか相談します。読む本は皆で相談しながら、今のチームにとって必要な本を選んでいます。 内定者アルバイトの期間では「 フロントエンド開発のためのテスト入門 今からでも知っておきたい自動テスト戦略の必須知識 」を読みました。開発1部では新規機能の開発に追われテストをしっかり書く機会がないこともありましたが、テスト文化を築く良いきっかけになりました。 本以外にも社員の方に翻訳してもらった Google Engineering Practices Documentation を読みました。PRの書き方や小さなPRを作る重要性、コメントをする上での心構えをチームで共有できました。 Findy Team+による開発運用の改善 Findy Team+は1PRあたりの平均マージ・クローズ時間や変更行数などの数値を分析できるツールです。開発1部ではこれを用いて週に1回、PRのオープンからマージまでのサイクルタイムやマージされたPRの数、1PRあたりの変更行数を振り返ります。 Google Engineering Practices Documentation を輪読会で読んでから、PRの粒度が小さくなり、PRをオープンしてからマージされるまでの平均時間が改善されました。 別の部署での取り組みがブログにあるので興味のある方は下記リンクからご覧下さい。 techblog.zozo.com 実際に行ったタスク 4か月の間、色々なタスクを担当しましたが、開発期間が長かったものを1つ紹介します。 似合うアノテーション ZOZOTOWN会員にどちらのコーデ画像の方が似合っているかアノテーションをしてもらう機能です。 背景 ZOZOTOWNは創業25年目を迎え、 経営戦略 として新たに「ワクワクできる『似合う』を届ける」が追加されました。 服と服、人と服の似合うデータをもとにモデルを開発したい。そのために「似合う」に関する大量のデータが必要だが現在大量にデータ取得できる経路がない。という経緯で、似合うに関するデータを大量に収集するためのアンケート機能を実装する必要がありました。 開発体制 部署内のテックリード、メンターと自分の3人でフロントの部分を実装しました。issueはGitHubのProjectsに案件ごとでまとめられており、issueと紐付けてPRを出していきました。 検証に上がった後は結合テストで開発者たちによる実機検証をしました。その後はQAエンジニアに動作確認をしてもらい、リリースという流れです。週に1回バックエンドのチームと共有会もあり、進捗や疑問点を共有しました。 実装箇所 APIの部分はメンターの方が既に実装していたので、私はそのAPIを使ったhooksやコンポーネントの実装を担当しました。 シンプルなUIで内定者アルバイトとして適切な難易度だったと思います。ただ、ZOZOTOWNは歴史あるプロダクトなだけあって普段のフロントエンド開発では見ないディレクトリ構成をしていたり、社内独自のライブラリがあったりしたので慣れるまで時間がかかりました。 一番難しかったのは途中離脱を防止するアラートの実装です。このページではアンケートを回答すると画面が変わりますが、URLのパスが変わるわけではありません。そのため、ブラウザバックをする時にアンケート開始前のホーム画面に戻ってしまうので、前の回答ページには戻れないと伝える必要がありました。 JavaScriptにはHistoryAPIがあります。このHistoryAPIはブラウザ上の閲覧履歴を操作できるものです。また、ブラウザバックのハンドリングには popstate イベントを使いました。 このAPIを利用して、アンケートに回答すると最初の1回だけ履歴を追加します。その後、ブラウザバックをした時にpopstateイベントを検知してアラートを表示していました。 しかし、iOSのsafariでは履歴の追加後にブラウザバックをするとアラートが出なくなる挙動を発見しました。この問題を解決できず、最終的にはアラート機能を外す結果となりました。 得られた知見 GitHub Projects内で管理されていたissueが細分化されていました。一見issueの量が多く見えて大変と感じるかもしれませんが、1つのissueが小さいことからPRの粒度も小さくなり、Reviewerの負担が減りました。このようなことからタスクをしっかり細分化することで開発サイクルを早められると気付けました。 ブラウザ・バージョンの互換性について意識するようになりました。開発者としては当たり前な部分だと思いますが、今までやってきた業務ではおざなりにしていたんだなと実感できました。エラーが起きた時、もしくはエラーはないが想定通りの動作が確認できない時、別のブラウザで同様のエラーが見られるか確認するようになりました。ブラウザ依存のAPIは互換性を意識する必要があると学べました。 セマンティックなマークアップ、アクセシビリティに対する意識も上がりました。私は内定者アルバイトを始める前、ある程度UIの実装ならできると慢心していましたが、レビューを受ける中で自分がいかに無知か再確認できました。小さなコンポーネント1つでもアクセシビリティのことを考慮すると改善の余地がたくさん出てきました。Reviewer方の知見の広さに感嘆するばかりでした。 フィードバックと反省点 内定者アルバイト最終日にMTGがあり、いくつかフィードバックをいただきました。 作業スピード、スキルは申し分ない 自分で仕事をガツガツ進めていく姿勢が良かった チームの輪読会・朝会で積極的に話せている などありがたいお言葉をいただきました。作業スピードが早いのは良いことですが、その反面PRの書き方が雑でレビュアに負荷を与えていたという反省点があります。動作確認の手順が書かれていない、概要の情報量が不足している、仕様の抜け漏れがある、メンバーが前提知識で知っていることをわざわざ書いていて冗長などの指摘をいただきました。 また、指摘箇所を修正した後連絡がないなどチーム開発をする中でもメンバーに寄り添った立ち回りができていなかったと思います。メンターからも言われたことですが多少時間がかかっても良いから抜け漏れのないPRを出して周囲に気遣いができるようになろうと思いました。 もう1点、レビューにもっと参加して欲しかったというフィードバックもいただきました。これは私も同じことを感じていて、せっかくZOZOのエンジニアたちのコードを読めるというのにコードリーディングにしっかり時間を割けず、勿体ない事をしたなと反省しています。配属後は1日の中で必ず一度は他の人のPRを見てレビューする習慣をつけようと考えています。 最後に ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに 技術評論社様より発刊されている Software Design の2024年5月号より「レガシーシステム攻略のプロセス」と題した全8回の連載が始まりました。 本連載では、ZOZOTOWNリプレイスプロジェクトについて紹介します。2020年に再始動したZOZOTOWNリプレイスでは、「マイクロサービス化」が大きなカギとなりました。今回は、SRE部が行った、リプレイス方針の決定から導入ツールの選定、マイクロサービスのリリース方法の改善までを紹介していきます。 目次 はじめに 目次 ZOZOTOWNリプレイスにおけるSRE部の方針 IaCの導入 IaCとは プラットフォーム基盤におけるIaC CI/CDの導入 CI/CDとは GitHub Actions 変更のあるインフラリソースのみをCIの対象とする工夫 Canary Releaseの導入 Canary Releaseとは ZOZO API GatewayとALBによるCanary Release IstioによるCanary Release Progressive Deliveryの導入 Progressive Deliveryとは Progressive Deliveryツールの選定 Flagger GitOpsの導入 GitOpsとは GitOpsツールの選定 Flux2 マイクロサービスのリリース方法の改善 改善前の課題 課題を解決したCDパイプライン Required reviewers OCIRepositoryに変更 改善の結果 おわりに こんにちは。株式会社ZOZOの技術本部 SRE部の籏野( @gold_kou )と堀口( @makocchi6 )と申します。第2回は、ZOZOTOWNリプレイスにおける、SRE部によるIaC(Infrastructureas Code)やCI/CD関連の取り組みを中心にまとめます。 ZOZOTOWNリプレイスにおけるSRE部の方針 ZOZOTOWNの開発に従事するSite Reliability Engineerは、技術本部のSRE部という組織に所属しています。2020年にZOZOTOWNのリプレイスを本格的に再開した際に、SRE部としていくつかの方針を決定しました。 まずは、AWSの導入です。ZOZOTOWNはもともと、オンプレミスのインフラ構成になっており、スケールアウトに課題がありました。たとえば、大規模なセールでは平常時の3倍以上のリクエストを受けますが、そのピークトラフィックを捌さばくために相応のインフラ設備を購入する必要があります。これには数ヵ月といったリードタイムが発生するため、準備も入念に行う必要があるうえ、スケールアウトが不十分でシステムエラーが多発するケースもありました。AWSを導入することでその問題を解決することにしました。 また、マイクロサービスはコンテナで管理することにしました。コンテナ技術を利用することで、処理速度および起動速度が速い、Amazon ECR(以下、ECR)などのイメージレジストリを利用できる、などさまざまなメリットを享受できます。 そして、コンテナ化されたマイクロサービスはAmazon EKSのKubernetes(以下、K8s)クラスター上で稼働させることにしました。ZOZOでは、このマイクロサービスが稼働するK8sクラスターを「プラットフォーム基盤」と呼んでいます。プラットフォーム基盤は、マルチテナンシー方式で、複数のマイクロサービスをnamespace単位で区切り、単一のK8sクラスター上で稼働させています。マルチテナンシー方式を採用した理由は、管理のしやすさやリソース効率の良さなどに加えて、リプレイスの速度を上げるためです。当時のZOZOの状況では、マルチテナンシー方式を採用することで、マイクロサービスの構築をパターン化して量産する体制を整えやすいと判断しました。 ほかにもこの時点で、IaC、CI/CD、Canary Release、Progressive Deliveryの導入を決定しました。また、それらを導入する中で、GitOpsの導入やマイクロサービスのリリース方法を改善するようになりました。本記事では、これらの取り組みを紹介します。 IaCの導入 IaCとは IaCは、インフラ構成をコード化して、そのプロビジョニングを自動化する手法です。次の効果が期待できます。 Gitによるバージョン管理とレビュー環境の提供 構築作業におけるヒューマンエラーの削減 新規メンバーのキャッチアップが容易 自動化による開発効率とリリーススピードの向上 コードの再利用 インフラへのCI/CD導入 プラットフォーム基盤におけるIaC プラットフォーム基盤のインフラはほとんどがIaC化されています。具体的には、AWSリソースはAWS CloudFormation(以下、CloudFormation)、K8sリソースはK8sのマニフェスト、DatadogリソースとPagerDutyリソースとSentryリソースはTerraformによりIaC化されています。 CI/CDの導入 CI/CDとは CI/CDは、CI(継続的インテグレーション)とCD(継続的デリバリー)の組み合わせです。CIはコードの変更を起点にコードの静的分析、ビルド、テスト、成果物の生成などの実行を自動化する手法です。一方、CDはCIで検証・テストされたコードや成果物を目的の環境に自動でデプロイする手法です。CI/CDを導入することで、本来は人が行う必要のない作業が自動化され、結果的に空いた時間で本質的な作業に人が取り組めます。これは、メンバーのモチベーション向上と、組織全体の生産性向上につながります。 プラットフォーム基盤におけるインフラのCI/CDでは、AWSリソースとK8sリソース、Terraformリソースの変更を検知して、リソースを更新します(図1)。前提として、IaCが導入されている必要があります。 図1 プラットフォーム基盤におけるインフラのCI/CD GitHub Actions CI/CDのツールはGitHub Actions(GHA)を採用しました。2020年当時は、GHAがまだサービスとしてリリースされて間もなく、マトリックスビルドができるなどの利点はあるものの、機能的に他のツールと比べて大きな優位性はないという社内評価でした。しかしながら、GitHubとの統合性の高さや将来性をふまえGHAを採用しました。 変更のあるインフラリソースのみをCIの対象とする工夫 前提として、プラットフォーム基盤のインフラを管理するGitHubリポジトリは、直下に「cloudformation」「k8s」「terraform」というそれぞれのIaCのディレクトリが存在する構成になっています(図2)。そしてそれぞれの配下に、環境(dev、stg、prdなど)やマイクロサービスなどに応じたディレクトリが存在します。 図2 プラットフォーム基盤のインフラに関する GitHubリポジトリの中身 この工夫では、OSSの tj-actions/changedfiles を利用し、Pull Request(以下、PR)で変更のあったディレクトリ情報のみを取得して、その情報を後続のJobへ渡すようにしました。後続のJobでは、変更のあったディレクトリに関するインフラリソースに関してのみ処理されます。結果として、すべてのインフラリソースに対してCIを実行していたころよりも、大幅にCIの時間を短縮できました。また、マイクロサービスがどれだけ増えても実行時間が長期化することはなくなりました。 Canary Releaseの導入 Canary Releaseとは Canary Releaseは、新しいバージョンのアプリケーションを段階的にデプロイする手法です。段階的にリリースすることで、新しいアプリケーションにバグがあった場合でもユーザーへの影響を最小限に抑えられます。また、リリース作業をする人(以下、リリーサー)の心理的負担も軽減できます。 ZOZO API GatewayとALBによるCanary Release 最初に、内製のZOZO API GatewayとAWSのApplication Load Balancer(以下、ALB)によるCanary Releaseをプラットフォーム基盤に導入しました。 ZOZO API Gatewayの加重ルーティング機能を利用して、各マイクロサービスのCanary Releaseを実現しました *1 。 そして、ALBの加重ルーティング機能を利用して、ZOZO APIGateway自体のCanary Releaseを実現しました。ZOZO API Gatewayの前段にはIngressリソースであるALBが存在します。ZOZO API GatewayのPrimaryリソースとCanaryリソースをそれぞれ用意し、それらをターゲットグループとして任意の加重率を設定します。加重率の設定は、リスト1のようにIngressリソースのannotationで行います。この設定を適用すると、AWS Load BalancerControllerはIngressリソースの変更を検知し、ALBのListener Ruleを更新します。 alb.ingress.kubernetes.io/actions.forward-external-traffic : | { "Type" : "forward" , "ForwardConfig" :{ "TargetGroups" :[ { "ServiceName" : "zozo-api-gateway-primary" , "ServicePort" : "80" , "Weight" : 90 } , { "ServiceName" : "zozo-api-gateway-canary" , "ServicePort" : "80" "Weight" : 10 } ] } } リスト1 ALBの加重ルーティング設定例 IstioによるCanary Release 前述したZOZO API GatewayとALBによるCanary Releaseを導入した後に、プラットフォーム基盤にIstioを導入しました。マイクロサービス間通信およびプラットフォーム基盤外への通信において、一貫した通信制御を提供するサービスメッシュが必要だったためです。 Istioの導入に伴い、Canary Releaseの手法を、IstioによるCanary Releaseに変えました。リスト2は、Istioの DestinationRule と Virtual Serviceにより、ZOZO API GatewayをCanary Releaseする設定例です。DestinationRuleでは、hostやsubsetsのprimaryとcanaryを定義します。VirtualServiceでは、destination ごとにDestinationRuleで定義したhostとsubsetを指定し、weightで加重率を設定します。この設定を適用すると、istiodにより自動的にistioproxyのconfigが更新され、ZOZO API Gatewayへのトラフィック加重率が変更されます。 apiVersion : networking.istio.io/v1alpha3 kind : DestinationRule metadata : name : destinationrule spec : host : zozo-api-gateway.ns.svc.cluster.local subsets : - name : primary labels : version : zozo-api-gateway - name : canary labels : version : zozo-api-gateway-canary --- apiVersion : networking.istio.io/v1alpha3 kind : VirtualService metadata : name : virtualservice spec : hosts : - zozo-api-gateway.example.com // 省略 http : - route : - destination : host : zozo-api-gateway.ns.svc.cluster.local subset : primary weight : 90 - destination : host : zozo-api-gateway.ns.svc.cluster.local subset : canary weight : 10 // 省略 リスト2 IstioによるCanary Releaseの設定例 ZOZO API Gatewayだけでなく、ほかのマイクロサービスも同様の方法で Canary Releaseできます。 Progressive Deliveryの導入 Progressive Deliveryとは Progressive Deliveryは、Canary Releaseも含め、より広範に新しいバージョンのアプリケーションを安全にリリースするための概念です。そこには、Blue/GreenデプロイメントやA/Bテストなどに加えて、Canary Releaseの自動化も含まれます。 前述のとおり、プラットフォーム基盤には IstioによるCanary Releaseを導入しました。しかしながら、Canary Releaseの進行における判断コストや加重ルーティングの進行、切り戻しなどの作業に関して運用コストが高いという課題がありました。そこで、Progressive DeliveryによるCanary Releaseの自動化を導入しました。 Progressive Deliveryツールの選定 Progressive Deliveryツールの候補としてArgo Rollouts、Spinnakerなどがありましたが、ZOZOのプラットフォーム基盤では次の理由からFlaggerを採用しました。 Istio との連携をサポートしており、自動でVirtualServiceの加重を変更できるため Datadogのメトリクス取得をサポートしており、判断基準に使用できるため いずれも、もともとは人が実施していた作業を自動化するだけなので、導入イメージが湧きやすかったという点が大きかったです。 Flagger Flaggerは、Progressive Deliveryを実現するKubernetes Operatorです。Flaggerを導入する主なメリットは2つです。 1つ目は、Canary Release作業の工数削減です。Flaggerはメトリクスの取得・分析、判断、加重率の変更作業などをすべて自動化してくれます。 2つ目は、Canaryリソースのコスト削減です。K8sのHorizontalPodAutoscalerの仕様上、min Replicasを0にはできません。したがって、Canary Release時以外でもCanaryのPodを最低1つは常時起動しておく必要がありました。しかし、Flaggerを導入すればそれが必要なくなります。 なお、プラットフォーム基盤では、Flaggerは次のように動作します(図3) *2 。 図3 プラットフォーム基盤におけるFlaggerの動作 加重変更 : VirtualServiceのweightを変更 スケールアウト/イン : K8sの設定を変更 メトリクス取得 : Datadogにクエリを発行 通知・アラート : Slackに通知 GitOpsの導入 GitOpsとは GitOpsは、Gitリポジトリを唯一の真実の情報源(Single Source of Truth)とし、K8sクラスターが自身の状態をGitリポジトリと同期するCD方式です。いわゆるPull型と呼ばれ、定期的にGitリポジトリをチェックし、変更があった場合はその変更を自動的に反映します。 一方、これまでプラットフォーム基盤にはCIOpsと呼ばれる、Push型のCD方式を導入していました。GitHubのPRのマージをトリガーにCIが走り、K8sクラスターへGHAがapplyをしていました。つまり、GHAにはapplyを実行するための強い権限が付与されていました。 CIOpsからGitOpsにすることで、CIはGHAの責務、CDはGitOpsツールの責務として分離し、GHAから強い権限を剥がせます。また、ワークフローのシンプル化や高速化も期待できます。 GitOpsツールの選定 ZOZOのプラットフォーム基盤ではFlux2を採用しました。類似のOSSとしてArgoCDが挙げられます。Flux2を採用した理由は、すでにプラットフォーム基盤で利用している FlaggerがFlux2と同じFlux Projectに所属しており、親和性の高さを期待できたからです。 Flux2 Flux2は、GitOps Toolkitと呼ばれるいくつかのコンポーネントにより動作します。たとえばSource ControllerやKustomize Controllerです。 Source Controllerは、Gitリポジトリ、Helmリポジトリ、バケットなどからアーティファクトを取得します。プラットフォーム基盤では、GitRepositoryというカスタムリソースを使用して、プラットフォーム基盤のインフラを管理しているGitリポジトリからK8sマニフェストを取得しています。 Kustomize Controllerは、Source Controllerによって取得したマニフェストをfetchし、それらをクラスターに適用します。Kustomizeという機能を利用して、マニフェストのカスタマイズやパッチ適用も行えます。Kustomizationというカスタムリソースを管理します。 リスト3は zozo-web-gateway というマイクロサービスのGitRepositoryとKustomizationの例です。GitRepositoryは、プラットフォー ム基盤のインフラを管理するGitHubリポジトリのreleaseブランチに対して、secretに保存されているSSH鍵を使って1分間隔でfetchする設定です。Kustomizationは、そのGit Repositoryの k8s/prd/zozo-web-gateway というディレクトリに存在するK8sマニフェストを1分間隔でfetchして、クラスターに展開する設定です *3 。 apiVersion : source.toolkit.fluxcd.io/v1 kind : GitRepository metadata : name : zozo-web-gateway namespace : zozo-web-gateway spec : interval : 1m0s ref : branch : release secretRef : name : zozo-web-gateway-flux-secrets-202307120000 url : ssh://git@github.com/xxx --- apiVersion : kustomize.toolkit.fluxcd.io/v1 kind : Kustomization metadata : name : zozo-web-gateway namespace : zozo-web-gateway spec : interval : 1m0s path : ./k8s/prd/zozo-web-gateway prune : false sourceRef : kind : GitRepository name : zozo-web-gateway suspend : false リスト3 GitRepositoryとKustomizationのYAML設定例 マイクロサービスのリリース方法の改善 改善前の課題 プラットフォーム基盤の構築当初は、masterブランチからreleaseブランチ宛のPR(以下、Release PR)を作成すると本番環境のCIパイプラインが動作し、PRをマージするとCDパイプラインが動作するという方法でリリースしていました。この手法のメリットは、リリース前の動作確認が可能で、リリース手順も簡単であることでした。しかし、1つのRelease PRで複数のチームの複数のマイクロサービスのリリースを管理することになるため、リリース時のチーム間での調整コストが発生したり、リリーサーを制限できなかったりなどの課題がありました。 課題を解決したCDパイプライン Required reviewers GitHubのEnvironmentsに、Deployment protection rulesという機能が存在します。たとえば、Required reviewersというruleを使用すると、そのEnvironmentに関して特定のチームや個人による承認を必須とします。図4は、pf_infra_sreというEnvironmentの例です。Web UI上で設定と承認ができます。リスト4のように、Required reviewsが設定されたEnvironmentをGHAのjobで指定すると、そのjobは承認されるまで実行されなくなります。 図4 Required reviewersの設定例 release-approval : runs-on : ubuntu-latest environment : name : pf_infra_sre リスト4 Environmentを使用したjobの例 このjobをデプロイ関連のjobのneedsに指定すれば、結果的にリリーサーを特定のチームや個人に絞れます。 OCIRepositoryに変更 Flux2のSource ControllerをGitRepositoryからOCIRepositoryに変更しました。OCI Repositoryは、GitRepositoryと同じくSource Controllerが管理するカスタムリソースの1つです。OCI(Open Container Initiative)互換のレジストリを参照します。プラットフォーム基盤にFlux2を導入した当初はOCIRepositoryという選択肢がありませんでしたが、この時点では利用できるようになっていました。 GitRepositoryのままだと、PRをマージしたタイミングでGitHubリポジトリを参照してデプロイされてしまいます。これでは、そのGitHubリポジトリの権限を持つスタッフは誰でもPRをマージできてしまうので、Required reviewersでリリーサーを制限しようとしても意味がありません。 そこで、Required reviewersでの承認後にOCIRepositoryへKustomizeマニフェストをpushするGHAのjobを構築しました。OCI RepositoryにはECRを利用し、KustomizationがECR上で管理されているマニフェストを取得して、クラスターへ適用します。また、OCI Repositoryを採用することで、SSH鍵の管理が不要になりました。 改善の結果 以上の改善により、リリーサーを制限できるようになりました。また、Release PRを使う必要がなくなったので、マイクロサービスのリリースにおけるこれまで発生していたチーム間での調整作業が不要になりました *4 。 おわりに 第2回では、ZOZOTOWNリプレイスにおけるIaCやCI/CD関連の取り組みを中心に紹介しました。ZOZOTOWNのリプレイスにより、「柔軟なシステム」「技術のモダン化」「開発生産性の向上」「採用強化」という4つの効果が期待できます。今回の取り組みの紹介を例に挙げると、AWSやK8sなどのクラウド化により、スケーリングなどの観点で柔軟なシステムの構築が可能になりました。IaCやCI/CDに関する複数のモダンな技術を取り入れ、開発生産性が向上しました。そして、これまでの取り組みをテックブログで公開したり、イベントで発表したりしました。その結果、テック業界におけるZOZOの技術プレゼンスが向上し、採用強化につながりました。リプレイス前は10名以下だったインフラチームには、現在SRE部として30名以上が所属しています。 SRE部はZOZOTOWNの成長のため、今回紹介した事例に限らず、さまざまな課題の解決や取り組みをしてきました。今後もZOZOTOWNのリプレイスを進めていきます。 本記事は、技術本部 SRE部 ECプラットフォーム基盤SREブロックの籏野 光輝と、技術本部 SRE部 商品基盤SREブロック ブロック長の堀口 真によって執筆されました。 本記事の初出は、 Software Design 2024年6月号 連載「レガシーシステム攻略のプロセス」の第2回「ZOZOTOWNリプレイスにおけるIaCやCI/CD関連の取り組み」です。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com *1 : 【ZOZOTOWNマイクロサービス化】API Gatewayを自社開発したノウハウ大公開! *2 : カナリアリリースを自動化!Flaggerでプログレッシブデリバリーを実現した話 *3 : CIOpsからGitOpsへ。Flux2でマイクロサービスのデプロイを爆速にした話 *4 : ついに最強のCI/CDが完成した 〜巨大リポジトリで各チームが独立して・安全に・高速にリリースする〜
はじめに こんにちは。DevRelブロックの @wiroha です。6月25日に「 ZOZO物流システムリプレイスの旅〜序章〜これまでとこれから 」を開催しました。ZOZOのエンジニアが物流システムリプレイス開発事例を紹介するイベントです。 登壇内容まとめ 弊社から次の3名が登壇しました。各発表はYouTubeにアップロードしてありますので、見逃した方やもう一度見たい方はぜひご覧ください。 コンテンツ 登壇者 分散メッセージングシステムを用いた新発送サービスのアーキテクチャ 作田 航平 ここがつらいよ 物流マイクロサービス 真田 洋輝 リプレイスを安心安全に 〜段階的リプレイスと等価比較〜 上原 駿 分散メッセージングシステムを用いた新発送サービスのアーキテクチャ www.youtube.com speakerdeck.com 作田からは発送サービスのアーキテクチャについて、リプレイス前後の比較やメリット・課題などを発表しました。分散メッセージングシステムを使用することで、基幹システムで障害が発生しても発送業務が継続できるように疎結合な新システムが構築できました。一方すべての課題を解決できたわけではなく、今後の課題も紹介されました。 ここがつらいよ 物流マイクロサービス www.youtube.com speakerdeck.com 真田からは物流マイクロサービスのつらいポイントにフォーカスした発表を行いました。実際に遭遇した課題とその対策を紹介し、Xでも「ありそう」「リアル」といった反響をいただきました。イレギュラーフローも含めた現場運用の理解やバランス感覚が必要で、物流システムの特性を感じました。 当日はたくさんの質問をいただきました。その中で回答できなかったものについて、回答を掲載します。 Q:ダウンフラグONはサーキットブレイカーのようなものですか? A:ダウンフラグ(サーキットブレーカーでいう状態)を自動で更新するか手動で更新するかの違いはありますが、同じようなものです。障害発生時にサーキットブレーカーのように通信自体を遮断するかどうかについてはまだ検討中ですが、障害発生時用の挙動に切り替わる点は同じです。 リプレイスを安心安全に 〜段階的リプレイスと等価比較〜 www.youtube.com speakerdeck.com 上原からは入荷リプレイスの概要と進め方について発表しました。入荷はもし止まってしまうとさまざまな業務に影響が出る重要な部分です。既存課題の解決をしながら安全にリプレイスするため、フェーズを分けて段階的に進めることにしました。旧実装と新実装後で等価比較する仕組みにより、安全にリプレイスを進めることができていました。 こちらの発表に関しても、当日いただいた質問への補足回答をいたします。 Q:モジュラモノリスのアプリケーションを複数人で開発する中で苦労されたエピソードがあれば教えてください。 A:モジュラーモノリス内には、発送と入荷があり、テストデータの準備に苦労しました。というのも発送と入荷の開発は、それぞれのチームに分かれており、お互いの開発状況をあまり把握しておらず、入荷用にデータを修正したり、逆に発送用にデータを修正したりしなくてはならないなどがありました。 Q:具体的にどういう部分(テーブルなど)が原因で基幹DBから分離ができないのでしょうか? A:具体的には在庫などでしょうか。在庫増減などは複雑な部分(ZOZOTOWNや発送などが関わる)なので、分離は難しいと考えました。 最後に 今回はたくさんの方にご参加いただき、ありがとうございました。質問も多数寄せられ、関心の高さが伺えました。今後もさまざまな分野でイベントを開催していきますので、ぜひご参加ください。 ZOZOでは一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 corp.zozo.com
目次 目次 はじめに 企画LPの儚さ 企画LPをアーカイブする! 仕様 開発について 技術選定 ちょっと技術の話 スクラム開発 ユーザーの反応 おわりに はじめに こんにちは! ZOZOTOWN企画開発部・企画フロントエンド1ブロックの秋山です。ZOZOTOWNトップでは、セール訴求や新作アイテム訴求、未出店ブランドの期間限定ポップアップ、著名人コラボなどの企画イベントが毎日何かしら打ち出されています。私はそのプラットフォームとなる企画LPをメインに実装するチームに在籍しています。 チーム特性としては以下のようなものがあります! クリエイティブコーディング寄りの実装をする機会に恵まれやすい 普段のLP案件内では、エンジニアよりも他職種(デザイナー、PM、ビジネス職)と連携することが多い 企画LPの儚さ 定常的なページと違って、開催期間が定まっている企画LPの命は儚いものです。月に20本ほどの企画LPがローンチされては、人知れずクローズしていきます。 制作陣一同、タイトなスケジュールのなか心を込めてつくりますが、目に見えやすいかたちで実績が残りません。もちろん、GitHubリポジトリ上のコードやデザインラフとしてはある程度残っていきますが、色々と容量を圧迫するものは削除していきたい所存です。そうすると、成果物は段階的に消滅することになります。 削除されないうちにも、企画量が膨大すぎて過去事例を掘り当てるのに苦労しがち、という問題もありました。「あの企画のとき、どう対応してたっけ?」と聞かれても、咄嗟に目当ての情報を見つけられなかったりします。 企画LPをアーカイブする! ちょうどデザイナーチームでも似たような課題感を抱えており、この機会にきちんとプロジェクト化して「ZOZOTOWN LP ARCHIVE」という社内ツールをつくることにしました! まず、普段から親交のあるデザイナーと2人で構想・仕様の大枠を練ったあと、PMにもジョインしてもらい、ビジネス側にもヒアリングしながらプロジェクトを進めました。この起案メンバーでは週次定例を行い、全体の仕様や運用方法を詰める作業や、各部の進捗確認をしていきました。 仕様 リアルタイム検索(フリーワード、日付、タグ検索) LP画面のキャプチャ閲覧・DL LP概要閲覧 各種ドキュメントへのリンク 上記の機能をミニマムで盛り込み、デザイナー・PM・事業部・エンジニア等、企画に関わる多方面のスタッフにとって嬉しいツールを目指します。 これはZOZOTOWNの企画コンテンツ全体としてのポートフォリオでもあります。前提として新規LPは全てアーカイブされますが、せっかくなので過去1年程度の企画はがんばってデータを集め、反映することにしました! これだけでも、200本ほどの量になります。 開発について さらに! 開発メンバーとして、元気な新卒2年生とフロントエンド道25年の大ベテランの方に入っていただけることとなり、プロジェクトが本格始動しました! メンバーそれぞれが事業案件を持ちながらの実装且つ、SRE等の他部署と連携が必要だったので、空白の期間も挟みつつ進みました。全ての工程を完了するには2か月くらいかかりました。 技術選定 社内ツールということもあり、個人的に試してみたいものを自由に使うことができました! 会社のプロダクトで技術選定を自由に行える機会はそう多くないので、わくわくします。 採用したのは、Next.js(App Router)、Panda CSSなどです。Recoil、dayjs、js-file-downloadなどのライブラリも、適宜相談・検討しながら導入しました。ヘッドレスCMSはmicroCMSを利用しました。 ちょっと技術の話 工夫した点などを開発メンバーに聞いてみましょう! こんにちは。大ベテランということで技術選定や困ったことがあったときなどのフォローとアドバイスを担当した竹口です。大ベテランということで紹介されましたが、ZOZOへは入社したてといった状態でした。ということでメンバーの技術レベルや社内の開発環境はどういったものかを把握するところからはじめました。 本記事を読んでいただくとわかると思いますが、意欲高めのメンバーが揃っていたということもありNext.jsのApp RouterやPanda CSSといったZOZOTOWN開発本部内で採用実績のない技術を使っています。社内ツールということもあって積極的にやりたいことを自分たちで選択していきました。 今までのNext.jsのルーティング方式であるPages Routerと、比較的新しいApp Routerになってから変更されたところを調べながら、メンバーでああでもない。こうでもない。といった感じで進めました。最終的には社内のホスティング環境がサーバーでレンダリングできないのがわかり、急遽SSG化してGitHub Actionsでビルドしてデプロイといった大きな変更も。柔軟に対応できるメンバーだったのは日頃から密に対話して進めていたからできたことかと思います。 工夫したのはNext.jsによる開発経験が高めのチームではなかったためPull Requestに対してファイルや変数の命名、コンポーネントの粒度など細かく事例を示したり代替案を出したりしながらレビューしたところです。 入社したてで良いメンバーと楽しく進めることができたのは良い経験となりました。 こんにちは。LPアーカイブのホーム画面実装を担当したゾイです。ホーム画面のメイン機能はリアルタイム検索でしたので、私からはリアルタイム検索の機能実装について工夫した点を話したいと思います! リアルタイム検索には企画タイトルや企画の開催期間のような基本的な情報はもちろん、技術・デザイン・企画種などのタグ検索機能を実装する必要もありました。 SSRで検索機能を実装することも検討しましたが、以下の3点を踏まえCSRで検索機能を実装することにしました。 ヘッドレスCMSにリクエストを送りすぎると負担が重い SSRの場合ロード時間が長くなりUX的に良くない LPアーカイブサイトはSPAっぽくサクサク遷移させたい そのため、状態を共有するコンポーネントが多くてもパフォーマンス的に問題ない設計を工夫する必要があり、状態管理ライブラリーの導入を検討しました。 結果から言うと、今後のメンテナンスしやすさを優先し可読性が良いと思われる Recoil を導入することになりました。 Recoilを導入した理由は次の通りです。 Recoilは atom が更新されたら該当のatomを利用するコンポーネントのみ再レンダリングするため、パフォーマンスが良い。 Recoilは Redux のような他の状態管理ライブラリーに比べて可読性が良く、今後のメンテナンスがしやすい。 新しい状態を追加することが必要になった場合Recoilだと RecoilRoot の配下に新しいatomを追加するだけで済むので、Contextのように親コンポーネントを再度ネストする必要がない。 RecoilはSSR時にメモリーリークが発生している問題があるようです。しかし、LPアーカイブでは以下の点を踏まえ、Recoilを利用しても問題ないと思いました。 LPアーカイブでは主にCSRを利用している 社内ツールなのでのスケールも比較的に小さい それでは、具体的にRecoilを利用してリアルタイム検索機能をどう実装したかを話したいと思います。 1. RecoilRootの配下に親コンポーネントを包む <RecoilRoot > < Home /> < / RecoilRoot> 2. SSR時にヘッドレスCMSから取得したデータをatomに保存する // 全データ保存用のatom const [ result , setResult ] = useRecoilState ( resultAtom ) // ヘッドレスCMSからのレスポンスが更新された場合のみ状態を更新する useEffect (() => { if ( result ! == data ) setResult ( data ) } , [ data , result , setResult ]) 3. 各々のフィルターコンポーネントの入力状態を(2)とは別のatomに保存 // 企画タイトルでフィルターするコンポーネントの例 export const TitleSearch = () => { const [ _ , setSearchInput ] = useRecoilState ( searchInputAtom ) const handleChange = ( e: React . ChangeEvent < HTMLInputElement > ) => { setSearchInput (( prev ) => ({ ... prev , title : e . target . value , })) } return ( < input type = "text" placeholder = "例)ZOZOWEEK" onChange = { handleChange } /> ) } 4. ユーザーのインプットによりフィルターコンポーネントの値が更新されたら(2)のデータをフィルターし、新しい結果を表示する import { useRecoilValue } from 'recoil' import { searchInputAtom } from '@/recoil/atom/searchInputAtom' import { resultAtom } from '@/recoil/atom/resultAtom' // (2)で取得したデータ const result = useRecoilValue ( resultAtom ) const [ filteredResult , setFilteredResult ] = useState ( result ) // (3)で更新したフィルターのデータ const searchInput = useRecoilValue ( searchInputAtom ) useMemo (() => { const filters = [ // LPのタイトルでフィルターする場合の例 const lpTitleRegex = new RegExp ( searchInput . title || '' , 'i' ) ( item: ZozoLpArchiveContent ) => lpTitleRegex . test ( item . lpTitle ) ,   // 開催期間など、その他のフィルター(省略) ] const filteredResult = result . filter (( item ) => filters . every (( filter ) => filter ( item )) ) setFilteredResult ( filteredResult ) } , [ result , searchInput ]) 秋山の方からは、CSS in JSライブラリPanda CSSの使用感の紹介です。 ZOZOTOWNのフロントエンドリプレイス環境ではNext.jsのPages Routerを採用しており、CSS in JSライブラリにはEmotionを利用しています。 今回はNext.jsのApp Routerを使いたかったので、React Server Componentがデフォルトということになります。Emotionは'use client'したコンポーネントでしか利用できない(実装を開始した2023年末時点)ため、App Routerのよさを生かすにはEmotion以外の選択肢を探す必要がありました。 今後他のメンバーが参画する可能性もあり、ZOZOTOWNフロントエンドリプレイス環境と似た書き方をできる方がいい 公式ドキュメントが丁寧 Tailwindなども検討してみたのですが、上記の理由から、Panda CSSを採用しました! Panda CSSにはいろいろな機能が搭載されています。コンポーネント内で何度も使う基本のスタイルと、マイナーチェンジしたスタイルを使い分けて書くことができる“レシピ”という機能がとても便利でした! 基本のスタイルはbaseの中に書いていきます。variants内にはbaseから変化をつけたいスタイルを書いていきます。 const Detail = styled ( 'div' , { base : { width : '620px' , padding : '20px 15px' , } , variants : { isCatch : { true : { padding : '10px' , } , } , } , }) < Detail isCatch = { true } > 略 < / Detail> 今回は利用しませんでしたが、“パターン”という機能もあり、Box、Flex、Wrap、Aspect Ratioなど便利なレイアウトパターンが用意されています。これらは適宜importして使います。 Panda CSSを使っていて個人的に感じたデメリットとしては、CSSプロパティをキャメルケースで書かないといけない&値を’ ’で囲わないといけないのがちょっと面倒なことくらいです。 const Capture = styled ( 'li' , { base : { marginRight : '20px' , } , }) 今回はEmotion、styled-componentsっぽい書き方をしてみました。Tailwind風に書くことも可能で、導入にあたって他のCSS in JSからPandaに変更する場合の学習コストはかなり低そうです。ただし自由に書ける反面、規約などで書き方をチームで統一するなどしないと破綻しそうにも思いました。 あとこれは余談ですが、⌘+Sするたび、ターミナルに🐼が出現して地味に癒されます。 スクラム開発 開発佳境の時期にはデイリースクラムを行い、毎日アイデアを出し合ったり、疑問点を都度解消したりしていました。 デイリースクラムにはGitHubのProject機能を利用しました。タスク管理はGitHubのIssueで行いました。Issue・PRは細かめに分けて、実装・レビュー共にさくさく進められたと思います。また、Slackで専用チャンネルを作り、スクラム以外の時間でも気軽に相談できる環境となっていました。 ユーザーの反応 当初想定していたユーザー層は企画LP関係のスタッフに限られており、使われ方としては以下のようなものがありました。 ビジネス職が企画立案時や営業時の参考にする PMが効果検証や要件策定時の参考にする デザイナーがアイデア帳、過去施策との比較、振り返り用として利用する フロントエンドエンジニアが過去の実装例を掘り当てる ZOZOTOWN全体の企画LPポートフォリオとして活用する いざローンチしてみると、以下のような予想外の恩恵もありました。 バックエンドエンジニアが検索リクエスト数を振り返るのにも使える 新メンバーなどは見てるだけでも勉強になりそう デザインかっこよ 眺めているだけでも楽しい UIがよくてわかりやすい ZOZOTOWNサイトと似たUIなので、馴染みのある操作方法で違和感なく説明なくてもすぐ利用できた といった、デザインを絶賛する声も。 多方面から嬉しいリアクションをもらえたのも、横のつながりを駆使して、職種の垣根を超えて力を合わせた成果です! おわりに 普段の業務での成果物はトップダウンの事業案件にまつわるものがほとんどです。しかし、今回の「ZOZOTOWN LP ARCHIVE」は現場メンバーの「こんなものを作って便利にしよう!」というアイデアから生まれました! いろいろな立場のスタッフの協力を得て、無事に成果物のローンチ・運用まで漕ぎ着けられました! それにしても、発案・構想・設計・実装・運用フロー作成と、最初から最後まで育てた成果物には愛着もひとしおです!「自分たちの城」感がすごいですね! 尚且つ、色々な部署にとって、小規模だけど有益なプロダクトである(自己満じゃない)というのがまた良かったです。 株式会社ZOZOでは、アイデア次第でこんなふうに自由度の高い開発経験をできる環境が整っています! ご興味のある方はぜひ、ご応募お待ちしております! corp.zozo.com
こんにちは。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運営の皆さん、参加者の皆さん、おつかれさまでした。また来年は松山でお会いしましょう! 現場からは以上です!