TECH PLAY

株式会社ZOZO

株式会社ZOZO の技術ブログ

988

目次 目次 はじめに マイページ画面リプレイスに伴う課題 使用したライブラリ Objective-Cでリファレンス、Swiftでテスト リファレンス画像のファイルサイズを小さく デバイスも言語も一気にテスト 複数言語のテスト自動化 複数デバイスを一気にテストする方法 いにしえVCのためのスタブデータの用意 おわりに はじめに みなさん、こんにちは! 松井です。普段はWEAR iOSアプリ開発で、コードを書く筋肉をパンパンに鍛えています。WEARアプリは、長い歴史を持っており、まだまだObjective-Cで書かれたレガシーなコードも居座っているんです。そんな中、私たちは地道にリファクタリングを進めています。そうしたObjective-CからSwiftへのリプレイス戦略において、スナップショットテストを活用したお話をしたいと思います。 スナップショットテストと聞くと、一般的にはコードの修正前後でUIが変わってしまっていないかをチェックするためのテスト手法という印象が強いでしょう。しかし今回は単なる修正ではなく、大胆なリプレイスにおいて旧と新のViewController(以下、VC)を比較した際のUIリグレッション確認のためにスナップショットテストを活用しました。スナップショットテストの力を借りて、リプレイスは無事に完了! 0.5ptの微妙なズレや、他にもいくつかリグレッションを見つけることができ大助かりでした。 マイページ画面リプレイスに伴う課題 私たちが着手したのは、投稿したコーディネートや動画、持っているアイテムやお気に入りしたコンテンツを一元管理するマイページ画面です。Objective-C製のコードで動いており、テストが書かれておらず、そもそもテストを書きづらい設計になっていました。しかもこの画面、各項目をタブで仕分けしており、1つの画面内でタブを切り替えるごとにUIがガラリと変わります。ユーザのプロフィールも表示しているので、プロフィール情報によってもUIが変化するんです。確認パターンが多すぎて、まさに開発者の強敵! 確認に工数がかかり、人的なミスも誘発しやすいです。ならばここは、スナップショットテストで対抗しましょう! スナップショットテストの使い方としては、まずObjective-Cによって書かれた旧VCでリファレンス画像を撮ります。それを元にSwiftへ生まれ変わった新VCが旧VCの見た目を再現できているかをテストすることで、解決を図ります。 使用したライブラリ ライブラリはPoint-Freeの swift-snapshot-testing を選択しました。このライブラリは、デバイスごとのプリセットが用意されているという点が優れています。後述しますが、これによりデバイスのサイズを指定しなくても良くなり、複数のデバイスのテストを一度に行うことができるんです。 Objective-Cでリファレンス、Swiftでテスト さて、Objective-CとSwiftでの見た目の違いを検証する際の、具体的なコードをお見せします。 @MainActor final class MypageViewControllerTest : XCTestCase { // リファレンス画像撮影モードを切り替える private let isRecord : Bool = true func test コーディネートタブ_投稿 1 件() async throws { let vc : UIViewController if isRecord { // VCを作成して返すメソッドは別途作成する必要があります。 vc = makeOldMypageViewController(tabType : .coordinate, dataCount : 1 ) } else { vc = makeNewMypageViewController(tabType : .coordinate, dataCount : 1 ) } snapshot(vc : vc ) } private func snapshot (vc : UIViewController ) { // テスト対象のViewControllerを表示 UIApplication.shared.firstKeyWindow?.rootViewController = vc // スナップショットテスト実行部分 // .imageHEICに関しては後述します。 assertSnapshots(matching : vc , as : [ .imageHEIC ] , record : isRecord , testName : "testコーディネートタブ_投稿1件" ) } } isRecord変数を定義し、このフラグを使うことでObjective-CとSwift、どちらのVCをテスト対象とするかを切り替えています。実際にスナップショットテストを実行するコードは、 snapshot(vc: UIViewController) メソッドの中に閉じ込めています。 上記のテストコードを実行すると、isRecordがtrueの時は旧VCのマイページ画面でリファレンス画像が生成されます。isRecordをfalseにして実行すると、リファレンス画像と新VCのマイページ画面を比較するテストが走ります。リファレンス画像どおりの見た目になっていたらテスト成功となります。失敗した場合は、失敗画像が生成されるので、リファレンス画像との差分を見比べて新VCのコードを修正します。 普通にテストを実行するだけならここまでの話で十分なのですが、swift-snapshot-testingには、強力な機能がほかにも備わっています。次のセクションからは、私たちが実際に使ってみて「これは便利だな」と感じたTipsをいくつか紹介します。 リファレンス画像のファイルサイズを小さく ここでは前節で省略した、 assertSnapshots の引数に指定した .imageHEIC について説明します。 リファレンス画像が増えたり、サムネイル画像が含まれていたりすると、どうしてもファイルサイズが大きくなってしまいます。しかもGitで管理したい場合、なおさらファイルサイズが気掛かりですね。ライブラリのREADMEを見ると多数の拡張ライブラリが紹介されており、その中に SnapshotTestingHEIC というHEIC形式でリファレンス画像を出力してくれる拡張ライブラリを発見しました。swift-snapshot-testingはPNG形式でリファレンス画像を出力しますが、PNGよりHEICのほうがファイルサイズを小さくできます。こちらを採用した結果、ファイルサイズを1/4〜1/3くらいまで落とすことができました。 上のコードに出てきた .imageHEIC というキーワードは、このSnapshotTestingHEICが提供している機能だったというわけです。拡張ライブラリは、他にもいくつか紹介されています。プロジェクトのニーズに合う拡張機能がないか確認してみると良いかもしれません。 デバイスも言語も一気にテスト WEARでは、日本語、英語、中国語(簡体字・繁体字)と複数の言語をサポートしています。そのため、言語が変わった時の表示と、複数のデバイスでの表示も確認しておきたいですね。スナップショットテストを使えばこうした色んな条件でのテストも楽チンです。言語の自動化はTestPlan、デバイスはswift-snapshot-testingが提供している各端末サイズのプリセットを使えば可能です。 複数言語のテスト自動化 まず、XcodeでTestPlanを新規作成します。TestPlanの Configurations > Application Language でテスト対象の言語を設定するだけです。あとはXcodeが自動的に、選択した言語環境でテストを回してくれます。 複数デバイスを一気にテストする方法 次に複数のデバイスを1度にテストするための、管理と実施方法について説明します。今回swift-snapshot-testingを採用した決め手となった、プリセットの出番です! まずは、テストしたいデバイスをまとめておくためのSnapshotConfigというenumを作ります。これには ViewImageConfig を使います。これこそがswift-snapshot-testingが提供する便利な機能で、それぞれのデバイスに対するプリセット情報を持っています。 import Foundation import SnapshotTesting enum SnapshotConfig : CaseIterable { case iPhone8 case iPhone13 case iPhone13ProMax case iPad9_7 func device () -> ViewImageConfig { switch self { case .iPhone8 : return ViewImageConfig.iPhone8 case .iPhone13 : return ViewImageConfig.iPhone13 case .iPhone13ProMax : return ViewImageConfig.iPhone13ProMax case .iPad9_7 : return ViewImageConfig.iPad9_7 } } } これで準備完了です。あとはテストケースで SnapshotConfig.allCases を回すだけ。一度のテスト実行で、複数のデバイスのテストが可能です。 @MainActor final class MypageViewControllerTest : XCTestCase { private let isRecord : Bool = true func test コーディネートタブ_投稿 1 件() async throws { let stubUser = makeStubUser() WRAccountManager.sharedInstance()?.setValue(stubUser, forKey : "user" ) SnapshotConfig.allCases.forEach { let vc : UIViewController if isRecord { vc = makeOldMypageViewController(tabType : .coordinate, dataCount : 1 ) } else { vc = makeNewMypageViewController(tabType : .coordinate, dataCount : 1 ) } snapshot(vc : vc , config : $0 ) } } private func snapshot (vc : UIViewController , config : SnapshotConfig ) { UIApplication.shared.firstKeyWindow?.rootViewController = vc let suffix = " \( config ) - \( Locale.preferredLanguages.first ?? "" ) " assertSnapshots(matching : vc , as : [ .imageHEIC ( on: config.device )] , record : isRecord , testName : "testコーディネートタブ_投稿1件" + suffix) } } ここで紹介した実装方法は、 メルペイiOSチームのスナップショットテストを効率化した話 を参考にしました。 いにしえVCのためのスタブデータの用意 さいごに、あなたのいにしえVCでも使えるかもしれないスタブデータの用意の仕方をご紹介します。 スタブデータの作成、今回これがなかなかの難敵でした。マイページ画面はユーザ情報によりUIが変化します。テストケースごとにこの情報を変えたいのですが、お相手は、Objective-Cで書かれたダシの効いたコード。ユーザ情報はAccountManagerクラスでuserプロパティとして保持されており、userプロパティはreadonlyになっていることから、スタブデータのセットが難しい状況でした。AccountManagerクラス自体の改修は、今回のリプレイスに関係のないコードにまで連鎖的に影響を及ぼす可能性があり、そう簡単には手出しできません。そんな時、大活躍したのが Key-Value Coding (KVC)を使う方法です。KVCはNSObjectが標準で提供している機能で、これを使えば、例えプロパティがreadonlyだったとしても、Objective-Cの変数の値をいじったり取り出したりできます。 具体的な使い方は以下のような感じです。 func test ヘッダー_性別のみ表示() async throws { let stubUser : User = makeStubUser() stubUser.sexName = "WOMAN" stubUser.height = nil AccountManager.sharedInstance()?.setValue(stubuser, forKey : "user" ) // 以下、スナップショットテスト実行コード } まずスタブのユーザ情報を持ったstubUserを作成します。上記の例では性別を女性、身長を未設定にしています。このstubUserをAccountManagerのuserプロパティにsetValueメソッドを使ってセットすることで、テストケースごとにユーザ情報をコントロールすることが可能になります。とても便利な機能ですが、あくまでNSObjectが標準で提供するものですので型チェックをスルーしてしまうところが落とし穴です。プロダクトコードでの使用は避けたほうが良いですが、今回はテストコードだったため使用しています。 おわりに Swiftに生まれ変わったマイページ画面がリリースされ、Objective-C製のマイページにさよならするとき、リグレッションの役目を終えたスナップショットテストも削除しました。役目を全うしたスナップショットテスト、おつかれさまでした! 今後も大掛かりなリプレイスをする際に活躍してくれることでしょう。この記事が、みなさんのプロジェクトでのリプレイス戦略にも何かのヒントになれば嬉しい限りです。より良いソフトウェア開発をこれからも進めていきましょう! ZOZOでは一緒に楽しく働くエンジニアを絶賛募集中です。ご興味のある方は下記リンクからぜひご応募ください。 corp.zozo.com
こんにちは。カート決済部カート決済基盤ブロックの長沼です。先日Javaアプリケーションをリリースしたのですが、リリース後にOld領域のメモリ使用量がわずかに増加し続ける現象が発生しました。本記事ではこの現象の調査にて得られた知見を共有します。 本記事で共有すること 前提知識 JVM(Java Virtual Machine)とは ヒープ領域とは GC(ガベージ・コレクション)とは GCのアルゴリズム G1GC(ガベージファースト・ガベージ・コレクタ)とは G1GCのメモリ管理 ヒープ領域の概念図 世代別領域に対するGCの基本処理 Young GC 混合GC Full GC ソフト参照 Javaの参照 ソフト参照とGC まとめ 最後に 本記事で共有すること GCの概要を理解するための前提知識を説明した後、G1GC(ガベージファースト・ガベージ・コレクタ)がメモリをどのように扱うか説明します。最後にメモリ使用量増加の原因となっていた ソフト参照 について説明します。 前提知識 まず、G1GCのメモリ管理方法を理解する際に前提となる事柄を説明します。 JVM(Java Virtual Machine)とは Javaバイトコードを実行するための仮想環境です。JDK(Javaのプログラムを開発するためのソフトウェアパッケージ)に含まれています。 ヒープ領域とは Javaプログラム実行時に生成したオブジェクトを割り当てるためのメモリ領域のことです。JVMのメモリ管理システムによって管理されます。 GC(ガベージ・コレクション)とは プログラムが使用し終わったメモリ領域を探し、割り当てていたメモリを解放し他のプログラムで利用できるようにする機能を GC(ガベージ・コレクション) と呼びます。 GCのアルゴリズム GCのアルゴリズムにはいくつか種類があり、アプリケーション起動引数により切り替えできます。 OpenJDK 17 64Bit Server VM の場合はG1GCの他に シリアル・コレクタ 、 パラレル・コレクタ 、 Zガベージ・コレクタ が選択できます。アプリケーションの特性やマシンリソースにより、最適なアルゴリズムは異なります。 G1GC(ガベージファースト・ガベージ・コレクタ)とは 大量のヒープ領域を最低限の停止時間で処理することを目標としたアルゴリズムです。マルチプロセッサ・マシンで動作することを前提として、アプリケーションの実行と同時に処理の一部を行うことでGCによる停止時間を短くします。 G1GCのメモリ管理 ヒープ領域の概念図 G1GCは世代別ガベージ・コレクションです。下の図はヒープ領域の概念図 1 です。 ヒープ領域は Young世代 と Old世代 に分類されます。Young世代はさらに Eden領域 と Survivor領域 に分類されます。Old世代は Old領域 のみとなります。 世代別領域に対するGCの基本処理 G1GCアルゴリズムでは3種類のGCが発生します。 YoungGC(Young-Only Collections) 混合GC(Mixed Collections) Full GC(Full Garbage Collections 2 ) 以降、順に処理の流れを説明します。 Young GC Young GCは主にYoung世代のYoung領域・Eden領域のメモリを管理します。 まず、オブジェクトがEden領域に配置されます。 Eden領域がいっぱいになるとYoungGCが発生します。この時、未使用オブジェクトは解放されます。使用中オブジェクトはSurvivor領域へ移されます。 空になったEden領域がいっぱいになると、再びYoungGCが発生します。この時、Survivor領域のオブジェクトが使用されていなければ解放されます。 Survivor領域がいっぱいになっていてEden領域から移動できない場合は直接Old領域へ移されます。 7〜15回 3 のYoungGCを経てもSurvivor領域から解放されなかったオブジェクトはOld領域へ移されます。 混合GC 混合GCは主にOld領域のメモリを管理します。 ヒープ領域が逼迫してくると 混合GC が発生します。混合GCによりOld領域内の未使用オブジェクトが解放されます。 なお、混合GCの前には必ずYoungGCが実行されます。上の図はYoungGC後に混合GCが発生してOld領域を解放する様子を表しています。 Full GC Full GCは混合GCが間に合わず、Old領域にて割り当て可能なメモリが不足した時に発生します。 Full GCを行っている間、アプリケーションは完全に停止します。長時間停止してしまうことがあるためこの現象は STW(Stop the world) と呼ばれ忌避されています。 ソフト参照 前半では世代別領域に対するGCの基本処理の流れを見てきました。ここからはメモリ使用量増加の原因となっていた ソフト参照 について説明します。 Javaの参照 通常の方法でオブジェクトのインスタンスを生成した場合の参照は 強い参照 と呼ばれます。これまで説明してきたのは 強い参照 に対するGCの挙動でした。 Javaの参照は他に、 ソフト参照 、 弱い参照 、 ファントム参照 があります。これらの参照を使用している場合、 強い参照 の時とはGCの挙動が異なります。 ソフト参照とGC java.lang.ref.SoftReferenceクラスを使用すると、 ソフト参照 の仕組みを利用できます。ソフト参照はヒープ領域が逼迫されるとGC対象となります。キャッシュを実装するために使用されることが多いです。 強い参照の場合、未使用であることがメモリ解放の対象となる条件でした。ソフト参照は最長不使用(LRU)アルゴリズムにより解放対象を決定します。具体的には下記の式 4 に該当するものを解放対象とします。 現在時刻(ミリ秒) - 最終使用時刻(ミリ秒) > ヒープ内空きメモリ(MB) * 1000 なお、ソフト参照のメモリ解放には複数回混合GCが必要になります。 まとめ ソフト参照はメモリ解放となる条件が強い参照と異なるため、アプリケーションの挙動を確認するためには長時間シナリオのテストが必要です。本記事の調査では開発環境にて70時間の負荷試験を行い、複数回の混合GC発生後にOld領域使用量が減少することを確認しました。 リリース後に慌てることがないよう、長時間シナリオテストを実施しましょう。 最後に カート決済部では、仲間を募集しています。ご興味のある方は、こちらからご応募ください。 hrmos.co 実際のヒープ領域は均等のサイズに細かく区切られたリージョンから構成されていて、どの世代の管理を行なうかはリージョンごとに割り当てられます。 Garbage-First (G1) Garbage Collector ↩ Webで情報を検索しやすいよう、括弧内は英語の正式名称を記載しています。 Full Garbage Collections のみ Garbage が含まれていますが、これが正式名称です。 HotSpot Virtual Machine Garbage Collection Tuning Guide ↩ 閾値は初期値が7回、最大15回です。 -XX:InitialTenuringThreshold や -XX:MaxTenuringThreshold オプションで変更できます。 Java HotSpot VM Options ↩ 参考文献: Scott Oaks(著), アクロクエストテクノロジー株式会社(監修), 寺田 佳央(監修), 牧野 聡(翻訳), “Javaパフォーマンス”, オライリー・ジャパン, 2015/4/11, 227頁. 1000はSoftRefLRUPolicyMSPerMBのデフォルト値です。 -XX:SoftRefLRUPolicyMSPerMB オプションで変更できます。 左辺と右辺で単位が異なりますが、そういうアルゴリズムです。 ↩
はじめまして。2023年4月に新卒として株式会社ZOZO(以下ZOZO)に入社しました、 財部彰太(たからべ しょうた) と申します。 この記事では、現在私が所属しているZOZOTOWN開発本部ZOZOTOWNアプリ部にて2022年5月から2023年3月までの期間で参加した内定者アルバイトについての話をさせていただきます。参加した理由、結果、そしてアルバイトを通して知った会社・チームの素敵な点を紹介させていただきます。ZOZOに興味がある人、内定後の動きに不安がある人、そもそも新卒でエンジニアとして働くか迷っている人、色々な人に読んでいただけたら幸いです。 内定者アルバイトとは 私が内定者アルバイトに参加した目的 ZOZOでの内定者アルバイトとは 実際にアルバイトをした部署、チームの話 なぜZOZOTOWN Androidチームにしたのか ZOZOTOWN Androidチームはどんなチームなのか 素敵な文化(1) チームとして新しい知識を取得するための時間を業務時間中に多く設けている 毎日の朝会後の読書会 週に1度のAndroid技術活動会 月に1度のデザイナーと共に行うMaterial Design勉強会 & 実装会 素敵な文化(2) お互いのいいところを積極的に見つけていく文化がある フルリモートワークでも問題なかったか 朝、夕に行う進捗、困りごと共有 週2回の1on1 実際に行ったタスク Android AnnotationsをZOZOTOWN Androidアプリから剥がす どのように進めたか 結果 内定者アルバイトに参加した目的は達成されたのか 余談 最後に 内定者アルバイトとは 内定者アルバイトとは、内定承諾から入社までの期間を利用して、アルバイトとして内定先で就業できる仕組みのことです。 私が内定者アルバイトに参加した目的 内定者アルバイトを行うメリットは以下の点があると考えております。 実際の業務に参加することで、自分の興味があることや得意なことを入社前に見極めることができる 内定者時代から入社後の業務や環境に慣れることができる 部署の雰囲気を知ることで、配属後のミスマッチを減らすことができる 上記のメリットを踏まえて、入社後に好調なスタートを切るために、私は内定者アルバイトに参加しました。 ZOZOでの内定者アルバイトとは ZOZOでは、就活の選考時に志望している職種以外のポジションにアルバイトとして応募、参加が可能です *1 。 入社後の活躍イメージが想像できず不安、今後の主軸としていく職種が定まっていない、配属後の業務内容の解像度を上げたい、入社までにスキルアップしたいなどの悩みや希望がある方も多いかと思います。内定者アルバイトの機会を上手く活用することで、不安解消に繋がり安心して入社できるかもしれません。 ZOZOの開発部門はフルフレックス制度を導入しており、自分にあった形で働くことができます。アルバイトについてはシフト制のためフルフレックス制度ほどの自由度はありませんが、基本的に希望に沿ったシフトが組めますので、学業に支障のない範囲での勤務が可能です。 実際にアルバイトをした部署、チームの話 私は2022年2月に内定をいただき、同年の5月から翌年2023年3月の大学卒業までの11か月間ZOZOTOWNのAndroidアプリを開発するチームで内定者アルバイトを実施しました。 なぜZOZOTOWN Androidチームにしたのか 私がZOZOで働きたいと思ったきっかけは、昔からZOZOTOWNアプリが好きだったからでした。使いやすく、デザインもとても可愛い。そして内定後、アルバイトの募集一覧を見たときにZOZOTOWNアプリの募集があり、ここしかないと思い応募しました。ZOZOTOWNアプリに関する募集はiOS/Androidともにありました。その中から、元々私がAndroidユーザーかつ、Androidエンジニアとして学習を行なっていた経緯から、Androidチームを希望しました。 ZOZOTOWN Androidチームはどんなチームなのか 2023年7月時点で、ZOZOTOWN Androidチームは10名程度の規模で構成されています。 チームのコミュニケーションはSlack、Google Meet、Miro等で行われています。チームには沢山の文化があります。全てを紹介するのは無理なので、私が特に素敵だと感じた文化を2点紹介します。 チームとして新しい知識を取得するための時間を業務時間中に多く設けている お互いのいいところを積極的に見つけていく文化がある 素敵な文化(1) チームとして新しい知識を取得するための時間を業務時間中に多く設けている 現在ZOZOTOWN Androidチーム内で行なっている勉強会は多数ありますが、この場では3つ紹介させてください。 毎日の朝会後の読書会 この会では、テックリードが中心となって本を読んでいきます。30分×週3回の頻度で行なっていて、本の長さにもよりますが、だいたい2か月で1冊読み終えているペースです。ただ読むだけではなく、テックリードによる解説や、メンバーの経験談の共有、質問の受け答えの時間が設けられており、双方向のやり取りで理解を深められる形になっています。読書会の中での疑問点や、会話の内容はMiroを使用してまとめています。 これまでに、『 読みやすいコードのガイドライン 』、『 かんたんUML入門 』、『 Clean Architecture 』といった本を読書会の中で読んできました。 週に1度のAndroid技術活動会 この会は、週に1度、1時間の頻度で行なっています。 AndroidDagashi や Android Developers Blog の新着記事、JetpackのライブラリやAndroid Studioの新しいリリース内容といった最新情報を追ったりしています。他にも、困り事の相談や、チーム内のガイドラインの更新があった時に共有する時間にもなっています。こちらもテックリードが主導して行なっています。 月に1度のデザイナーと共に行うMaterial Design勉強会 & 実装会 Material DesignはGoogleが提唱しているデザインシステムであり、Androidプラットフォームではこちらのデザインシステムに準拠することが推奨されています。こちらの会はそのMaterial Designをデザイナーと共に学習し、プロダクトに反映させていくというものです。詳細は別記事の『 AndroidアプリをMaterial Designのガイドラインに準拠させるための取り組み 』で紹介しています。 こうした時間を業務内に確保してくれることで、なかなか業務外だと本を読んだり記事を読んだりする時間が取れない人でも、会を通じて着実にインプットできます。 素敵な文化(2) お互いのいいところを積極的に見つけていく文化がある ZOZOTOWN Androidチームでは週1度振り返りが行われています。振り返りの中ではKPTを行なったり、次の1週間のタスクの計画を立てたりします。 その中で、私が特に素敵だと思った文化が、「今週のいいね」です。今週のいいねでは、この1週間でいいねと思ったことを下記のようにMiroで作られたフォーマットに書き出して発表します。 いくつか抜粋して紹介します。 積極的でいいねといった内容だったり、ちょっとした小話的な内容もあったり、この新しい取り組みがいいねと言った内容もあります。最後の1枚はネーミングセンスを鍛える取り組みについての内容ですが、こちらは同チームのテックリードによるものです。興味がある方は、別の記事でまとめられている「 ネーミングセンスを鍛える会の取り組み 」を覗いてみてください。 毎週の振り返りでは、こういった「いいね」が20個、多いときは40個集まる時もあります。褒められて嬉しいし、褒めても明るい気持ちになれるし、とっても素敵な文化だと感じています!「今週のいいね」でいい体験をして、次の週ではさらに同僚のいいところを見つけていこう、自分もいいところを作っていこうとしていけるので皆が幸せになれますね。 こういったお互いのいいねを送り合うことは、部署を越えて行なったりもしています。 フルリモートワークでも問題なかったか 内定者アルバイト期間、私はフルリモートで働きました。ZOZOのエンジニアはリモートワークをするか、オフィス出社をするか選択できます。ZOZOTOWN Androidチームはリモートワークの方が多いです。 リモートで働き始めると、場合によっては困難に直面することもあると思います。環境構築のつまずき、孤独感、人によって様々なストレスを感じるでしょう。新卒や学生のような経験が浅い場合だと尚更です。しかしZOZOTOWN Androidチームは以下のような形でアルバイトに参加でき上記の困難に遭遇せずに働くことができました。 朝、夕に行う進捗、困りごと共有 アルバイトとして入社して3か月の間、朝と夕にメンターと上長の3人で進捗や困りごとを共有する時間をいただいていました。人によっては多く感じる頻度かもしれませんが、私にとってはとてもありがたかったです。技術的な相談はもちろん、壁打ちの時間としても有効活用できました。チームに参加してすぐのタイミングだと、自分でも気づかないうちに間違った方針で進めてしまうこともあると思います。朝、夕の頻度で共有の時間をいただけて方針の修正ができてとても助かりました。 週2回の1on1 ZOZOでは社員の成長を支援するために、上長と1on1をする制度があります *2 。 こちらは正社員向けの制度ではありますが、ZOZOTOWN Androidチームではアルバイトの期間でも行なっていただきました。私のチームでは上長、テックリードとそれぞれ週1度1on1を実施しています。1on1では困りごとの共有、この先数年単位でどんなことをしていきたいか、キャリアにしていきたいかといった話や、気軽な雑談も行われます。キャリアや仕事の話ももちろんありがたかったですが、私にとっては気軽な雑談を行えるのがとても嬉しかったです。ZOZOに来るまでにもリモートワークを少しだけ経験しましたが、どうしても気軽な雑談が減ってしまい、同僚の人となりや趣味といった情報がなく、少しだけ無機質に感じることがありました。同じように感じる人も多いと思います。ZOZOでは1on1を通じて、より同僚を身近に感じることができるようになりました。結果的により何でも話せるようになり信頼が深まったように感じます。1on1のおかげで孤独感を感じることなく働くことができました。 実際に行ったタスク 約1年間の内定者アルバイトの中で、色々なタスクを行いましたが、今回は最初にいただいたタスクについて紹介しようと思います。 Android AnnotationsをZOZOTOWN Androidアプリから剥がす タスクの提案をいただくにあたって、アルバイト配属前の面談で以下のような希望を出していました。 ZOZOTOWN Androidアプリの広い範囲をよく知りたい エンジニアとしての能力を向上したい それに対して提案をいただいたのが、この「Android AnnotationsをZOZOTOWN Androidアプリから剥がす」というタスクになります。Android Annotationsはアプリの広い範囲で使われていて、改修する中でアプリの広い範囲に触れることができ、1つ目の希望と合っています。またAndroid Annotationsは古くから存在する画面で多く使われていたため、改修する中で現在チームが推進しているアーキテクチャに刷新していく必要もありました。チームが推進したいアーキテクチャを理解できるという点で、2つ目の希望にも合っています。 このタスクの背景としては、ZOZOTOWN Androidチームがビルド時間増加を課題と認識していたことが挙げられます。ビルド時間を短くするための手段として、 kaptをKSPへ移行する ことが有効です。このKSPへの移行のためには、KSPに対応していないライブラリをアプリから剥がす必要があり、Android Annotationsは剥がす必要のあるライブラリの1つでした。Android Annotationsを剥がすタスクは、自分の希望に沿うのみでなく、プロダクトの課題解決にもつながるとわかり、より意欲が高まりました。 どのように進めたか 私は当時、週に3日間(月曜日から水曜日)働いており、1日8時間の作業時間を確保していました。毎週月曜日にその週のタスクを計画し、水曜日までに完了しなかったタスクはメンターに引き継ぎました。実装後は、メンターと2人のチームメンバーによるレビューを受けて修正作業を行いました。タスクはクラス単位で分割され、修正が完了するたびにプルリクエストを提出しました。 結果 最終的に全体の5割のタスクを任せてもらい、およそ1か月半でリリースまで持っていくことができました。KSPへの移行はほかのライブラリが原因でできてはいませんが、1つ大きな障害を取り除くことはできました。 もともとの希望も叶えることができました。1つ目の「ZOZOTOWN Androidアプリの広い範囲をよく知りたい」という希望に対しては、リニューアルが行われていたホームタブ以外のタブは全てこのタスクの中で読むことができました。結果、「〇〇の画面に機能を追加する」といった話があった時に、おおよその改修箇所の目星をつけられるようになりました。 2つ目の「エンジニアとしての能力を向上したい」という希望に対しても、古い画面にチームが現在推奨しているアーキテクチャを適用していく中で、アーキテクチャへの理解を深めることができました。また非同期通信をKotlin Coroutinesに刷新する必要がある時に、理解が浅かった自分のためにメンターの方が1時間以上かけて説明をしてくださるなど、自分が予想していたよりも遥かに多くの学びの機会を頂く事ができました。 レビューでも自分では気がつけない多くの改善点を教えてくださり、こちらでも多くの学びを得ました。メンターの方、レビューをしてくださった方、本当にありがとうございました。 タスクを通して、自分が触ってきたアプリの裏側を見ることができて純粋に嬉しかったことを覚えています。またレガシー部分を刷新していく楽しさも強く感じました。アーキテクチャへの理解を深められることはもちろん、これまでユーザーに使ってもらえてきた画面が改修によってより長い期間存続できるようになると思うと、とても嬉しいです。 内定者アルバイトに参加した目的は達成されたのか 冒頭では、私が内定者アルバイトに参加する目的は入社後に好調なスタートを切ることとお話ししました。こちらの目的は達成できたと考えております。最終的に私は、社員としての本配属もZOZOTOWN Androidチームに希望を出し、そのまま配属させていただいて、自分が望む環境とのミスマッチがなく働けていると感じています *3 。入社後大きなトラブルなくタスクの着手ができ、今後どのようなことをしていきたいかといった数年単位の話も具体的に考えることができました。アルバイト期間の経験があってのものだと思います。 余談 ZOZOは2019年からDroidKaigiに協賛しており、今年のDroidKaigi 2023にもブースを出展いたします! ぜひお越しください。 そんな2022年のDroidKaigi 2022では、チケット代を負担していただき、ブースにも参加しました。DroidKaigi 2022は自分にとって、オンラインオフライン問わずに初めてのカンファレンスでした。いろんな会社のブースで、普段聞けないお話を聞けるなど、オフライン参加ならではの体験が出来たためとても感謝しています。 最後に ZOZOでは新卒、中途問わずAndroidエンジニア、そしてインターンシップを募集しています。ご興味のある方は下記リンクからぜひご応募ください。 hrmos.co 2025新卒_就業型インターン(ZOZOTOWN/Androidエンジニア): https://public.n-ats.hrmos.co/zozo/jobs/1852303323965423616 *1 : 2023年7月現在 *2 : 福利厚生について https://corp.zozo.com/recruit/welfare/ *3 : 配属方針は年度ごとに異なる可能性があります
こんにちは、ブランドソリューション開発本部フロントエンド部の田中です。普段はWebのフロントエンドエンジニアとして、FAANSのWebの開発を行なっています。 FAANSとは「Fashion Advisors are Neighbors」がサービス名の由来で、ショップスタッフの効率的な販売をサポートするショップスタッフ専用ツールです。 ショップスタッフ向けにコーデ投稿・成果確認などの機能が存在し、2022年8月に正式ローンチしました。詳しくは以下のプレスリリースをご覧ください。 corp.zozo.com 弊社の福利厚生の中に海外カンファレンスを含む勉強会やセミナーの参加費用を会社で補助してくれる制度があります。今回その制度を利用して、オランダのアムステルダムまで渡航し、JSNationとReact Summitに参加致しました。 corp.zozo.com オンラインからの参加も可能で、プレゼンテーションのライブ配信やDiscordを利用したコミュニケーションを取れたり楽しいイベントとなっています。今回の記事では現地参加ならではの経験、特に会場内の雰囲気を中心に紹介していきたいと思います! JSNationとReact Summitとは? 2023年6月1日 - JSNation 質問ルーム ブース ランチタイム 休憩スペース 景品抽選タイム OSS Awards 2023年6月2日 - React Summit Reactの誕生日 After Party 2023年6月3日 - Boat Tour & Walking Tour 気になったセッションについて Modern Web Debugging Accessible Component System Through Customization 初めて海外カンファレンスに行ってみての感想 さいごに JSNationとReact Summitとは? JSNation と React Summit は GitNation が主催するカンファレンスです。JSNationはJavaScriptに関して、React SummitはReactに関連する発表やイベントを扱っていて、Next.jsやQwikなど関連した技術に関する発表もあります。また、アムステルダムの街を探索するイベントなどエンジニア同士で交流するイベントも用意されています。 現地参加の各カンファレンスのスケジュールは以下の通りです。今回参加できなかったのですが、別日にスピーカーの人から学べるワークショップも開催されています。 日付 時間帯(CEST) イベント 場所 2023/6/1 9AM - 5PM JSNation アムステルダム 2023/6/2 9AM - 10PM React Summit アムステルダム 2023/6/3 10AM - 1PM Hangout Day アムステルダム 以降、参加したそれぞれのイベントについて詳しく紹介していきます。 2023年6月1日 - JSNation JSNationの会場内は上の画像のような雰囲気でした。 プレゼンテーションは2会場で行われ、目星をつけていたセッションを中心に見ました。各国から著名な方が登壇し会場内は盛り上がっていました。 特に印象的だったのがプレゼンテーション中にデモが動かなく、発表者がChromeのConsoleのデバッグを始めた時に、会場内で拍手がわき起こったことです。 APIからのデータ取得時にエラーが発生し、その原因はWi-Fiが接続されていなかったためでした。会場内のWi-Fiに接続しデータの取得に成功したところ、再度拍手が湧き起こり、非常に暖かい雰囲気が広がりました。予想外のトラブルも楽しめて素晴らしかったです。 質問ルーム 質問ルームでは、プレゼンテーション終了直後に発表者に対して質問が可能です。 ブース 会場内にはスポンサーの展示ブースが設置されており、FAANSのWebでも利用しているChromaticとSentryのブースを訪れて、働いているエンジニアと直接話す機会を得ました。 Storybookに関して今後進めたいこと(VRTのローカル環境での実行といったテスト機能の追加、Storybookの導入とその記述コストの軽減など)やChromatic社の働き方を話しました。 Storybookのイベントでよく見かけるYann Bragaさんにも会場内でバッタリ会い、FAANSにおけるStorybookの活用方法についてお話ししました。ロゴ付きのステッカーを頂いたり、会話後に記念写真を撮ったり一生の思い出になりました。 Sentryについては最初にFAANSのWebでどう活用しているかヒアリングがあり、その後に知らなかった機能を紹介していただけました。その中でクライアント側のコードで不具合が発生した前後のユーザーの画面を再生できる機能をデモンストレーションしていただきました。FAANSのWebでも活用してみたいと思いました。 ランチタイム チュニジア人とイタリア人のエンジニアと、現在進行中のプロジェクトや使用している技術について話しながらランチを食べました。カンファレンス参加にあたって英語に対する不安があったのですが、出会ったエンジニアは私の英語が少し不自由だとしても理解しようと努力してくれました。そして、聞き取れなかった場合はGoogle翻訳を使って日本語訳を見せてくれ、その配慮に感動しました。 休憩スペース セッション外の場所では飲み物や軽食を受け取れるスペースがあり、休憩したり他の参加者と交流したりできます。 景品抽選タイム ブースを出展している企業から抽選で景品がもらえるイベントもありました。景品を引き当てた人は大いに喜んでいました。 OSS Awards オープンソースの授賞式もあり、各部門(例:生産性への貢献やもっともコミュニティーに影響与えた)ごとに賞が授与されました。使ったことがないライブラリや拡張機能があってとても参考になりました。特にTypeScriptのエラーを見やすく表示できるVisual Studio Codeの拡張機能のPretty TypeScript Errorsは使ってみたいと思いました。 2023年6月2日 - React Summit 2日目はReact Summitです。JSNationと同じ会場で行われたのですが、雰囲気は一気に変わり全体的に緑を基調とした空間でした。 Reactの誕生日 Reactが10周年を迎えるということでそのお祝いが行われました。会場内でケーキが配布されました。また、JSNationの時と同様にプレゼンテーション、ブースの出展、OS Awardsのイベントも行われていました。 After Party カンファレンスが終わった後にアフターパーティーが行われ、JSで作られたデジタルアートと音楽が流れ、盛り上がっていました。 2023年6月3日 - Boat Tour & Walking Tour 最終日にHangout Dayにてボートと散歩のツアーがありました。どちらのツアーもガイドの案内があり、アムステルダムについて知ることができました。 気になったセッションについて 最後に、特に印象に残ったプレゼンテーションを2つ紹介します。これらのプレゼンテーションはGitNationのウェブサイトでも視聴可能なので、是非確認してみてください。 portal.gitnation.org Modern Web Debugging portal.gitnation.org 最初に注目したのは、Chrome DevToolsのメンバーで、DevToolsの機能を頻繁に動画で紹介してくれるJecelyn Yeenさんの発表でした。設定した条件を満たした時点で停止する 「Conditional Breakpoints」の機能 や、 ネットワークパネルにおけるレスポンスヘッダーを上書きしてデバッグできる機能 などが紹介されました。 私は普段の開発の中でconsole.logを使ったりしますが、それと似た機能である 「Logpoints」を使ったデバッグ などの存在も初めて知りました。これからは開発者ツールのデバッグ機能をもっと活用していきたいと思いました。 いくつかの新たなデバッグ体験が紹介され、観客の間から拍手が湧き上がりました。隣にいたエンジニアと雑談し「新たなデバッグの機能を知ることができてよかった」と感想をシェアしました。 また、質問パートにおいて本番環境でminifyされたコードをデバッグする方法について質問していた方がいて興味深かったです。回答としては、そのminifyされたコードに対するSource Mapsをプライベートな場所に保存するという内容でした。 FAANSのWebはエラーログの収集ツールとしてSentryを使っていますが、ログがminifyされた状態で表示されていて追いづらい課題を抱えていました。 そこでSentryにSource MapsをアップロードするSDKが用意されており、minifyされたコードから開発コードへ復元された状態でログを追えることが分かりました。 FAANSのWebでは開発速度の向上を目的にCreate React AppからViteに移行し、 ViteでSource MapsをアップロードするSDK が用意されておりその一環で対応しました。 対応の内容として、ビルドして生成した本番コードとそれに紐づくSource MapsをSentryへアップロードするが、ユーザーへは公開しない対応を取りました。 これによりセキュリティーリスクを回避しつつログが追いやすい体制を整えることができました。 カンファレンスで得た内容が実プロダクトに活かされてよかったです。 Accessible Component System Through Customization portal.gitnation.org 他社においてコンポーネントライブラリがどのような過程で構築されるのかに興味がありプレゼンテーションを視聴しました。コンポーネントライブラリは全体のデザインシステムを定義した後の実装部分であるとし、それを構築する際は適切なデザインシステム(スタイルガイドやタイポグラフィーなど)の設計が必要であると述べていました。 また全ての状況をカバーできるような万能なコンポーネントライブラリを作ることはできず、作成する際にはその目的や対象を明確にする必要があると指摘していました。そこで、彼女の自身のプロジェクトであるStorefront UIはEコマース専用のコンポーネントライブラリに特化し、それを元にアクセシビリティーなどの要件を満たすように構築していると述べていました。 また、質問パートで既存のライブラリ(例えばRadix UIのようなもの)をカスタマイズするのではなく、ゼロからコンポーネントライブラリを作成する理由について質問していた方がいて興味深かったです。 一概に決められるものではなく状況によると述べていました。Microsoftのような大企業で働いている場合、内部の様々なチームが再利用可能なものを自前で作る必要があるとのことです。また、コードが成長するにつれて外部のライブラリを使用することは、バグ修正、保守性、テストなどの観点から問題を引き起こす可能性があるから自作で構築していると説明していました。 この話を聞いてプロジェクトや状況に応じた技術選定が大事で、改めてFAANSのWebにおけるコンポーネントライブラリの選定を見直すきっかけになりました。 FAANSのWebはChakra UIを使っていて外部のライブラリに依存しております。ただFigmaが提供されていたり、少しのカスタマイズで複雑なUIをスピード感もって実装できることから、少数な弊チームにあった選択肢だと再確認できました。 初めて海外カンファレンスに行ってみての感想 日本から遠いオランダでしかも初めての海外カンファレンスで不安がありましたが、参加してみて結果的には大満足でした。初対面の人々と会話するのに少々緊張していましたが、周りの人は私の話を理解しようとし、それによって技術についての話題で盛り上がることができてとても楽しかったです。その経験により私の不安は消え去り、開発者同士の繋がりを実感できました。また、Source MapsのSentryへのアップロード対応のようにカンファレンスで得た知識を実プロダクトに活かせて良かったです。著名なエンジニアからプレゼンテーションや会話を通して新たな知識や良い影響を受けたり、日々のフロントエンド開発のモチベーションにもなりました。更に深いレベルの情報共有をしたいと思うようになり、今後の英語学習のモチベーションに繋がりました。 さいごに 最後に、ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。DevRelブロックの @wiroha です。7月11日に ZOZO Tech Meetup - iOS/Android を開催しました。ZOZOのiOSエンジニアとAndroidエンジニアがそれぞれの技術領域にフォーカスして紹介するイベントです。オンラインとオフラインのハイブリッドで開催しました。 オープニングの様子 登壇内容まとめ 今回は弊社から9人とたくさんのエンジニアが登壇しました。 コンテンツ 登壇者 [Android] JankStats LibraryでJankを検出しよう 高橋啓太 [Android] Compose for iOS for ZOZOTOWN 井上晃平・ 財部彰太 [iOS] Visionフレームワークを使って非接触でMacを操作しよう 中岡黎 [Android] ComposeでWidgetを実装できるライブラリ「Glance」がbetaになったので触ってみる 野々村樹 [iOS] Pathを活用してSwift Chartsの限界を超えろ 山田楓也 [Android] よく書く同じようなコードはAndroid StudioのLiveTemplateを使ってテンプレート化しよう 山田尚吾 [iOS] FAANS iOSアプリでGitHub Copilotを使い倒してみた 中島頌太 [iOS] 10年以上の歴史を持つプロダクトのRosettaフリー 〜その中で遭遇した問題と解決法を公開します〜 小松悟 当日の発表はYouTubeのアーカイブでご覧下さい。 www.youtube.com [Android] JankStats LibraryでJankを検出しよう 高橋啓太 / ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android2ブロック speakerdeck.com 高橋からは、Jank検出ツールであるJankStats Libraryについて発表しました。Jankとはフレームがスキップされてアプリがスムーズでなくなることを指し、その検出・修正は快適な操作感を維持するために重要です。JankStats Libraryは本番環境で発生しているJankの原因が分析可能になる点が優れているそうです。導入方法・使用方法・実装時に詰まりそうな点が紹介され導入しやすそうでした。 [Android] Compose for iOS for ZOZOTOWN 前半担当:財部彰太 @hamuyatti / ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android1ブロック 後半担当:井上晃平 @nemo-855 / ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android2ブロック speakerdeck.com こちらは2人からの発表です。前半は財部よりCompose for iOSの基礎知識について、後半は井上よりZOZOTOWNの画面を作ってみたというお話をしました。ComposeのコードでiOSの画面も作れるのはAndroidエンジニアにとっては嬉しいですね。まだalpha版とのことですが、カルーセルやGridのスライダーもしっかり動いていました! [iOS] Visionフレームワークを使って非接触でMacを操作しよう 中岡黎 @rei_nakaoka / 計測プラットフォーム開発本部 計測アプリ部 iOSブロック speakerdeck.com 中岡の発表は非接触でMacを操作するとはどういうことか紹介するデモ動画からはじまりました。手を上下に動かして画面のスクロールをしたり、指でつまむような動作をしてマウスポインタを動かしたりと、Macを操作できていました。Visionフレームワークを使って手のポーズを認識し、それに応じてCGEventを発生させることで実現できるそうです。 中岡はiOSDC Japan 2023でも登壇します。こちらも聞きに来ていただけると嬉しいです! fortee.jp [Android] ComposeでWidgetを実装できるライブラリ「Glance」がbetaになったので触ってみる 野々村樹 @nono_develop / ブランドソリューション開発本部 フロントエンド部 WEARAndroidブロック speakerdeck.com 野々村からはComposeでWidgetを実装できるライブラリ「Glance」を触ってみたという発表です。Composable関数を書くことでWidgetを作成できていました。ただしGlance専用のAPIを使用するためまだ多少制限があるそうです。betaでGlanceThemeというThemeが追加されるなど便利になっているので、今後が楽しみですね! [iOS] Pathを活用してSwift Chartsの限界を超えろ 山田楓也 @gamegamega_329 / ブランドソリューション開発本部 フロントエンド部 WEARiOSブロック speakerdeck.com 次は山田楓也からSwift ChartsではサポートされていないグラフもPathを活用すれば描画ができるという発表です。Pathを使ってアニメーション付きの円グラフが綺麗に描画されていました。レーダーチャート、メーター、多重ドーナツ型のグラフも作成できていました。Swift Chartsの限界を超えていました! [Android] よく書く同じようなコードはAndroid StudioのLiveTemplateを使ってテンプレート化しよう 山田尚吾 @yshogo87 / ブランドソリューション開発本部 フロントエンド部 FAANSブロック speakerdeck.com 続いて山田尚吾より、Android StudioのLiveTemplateについて発表しました。略語でテンプレートを呼び出せる機能で、とても便利そうだとTwitterでも盛り上がっていました。Gitでの管理もできるためチームの実装を楽にするためにも、設計を強制するためにも使えます。すぐに取り入れて活用できそうな内容でした! [iOS] FAANS iOSアプリでGitHub Copilotを使い倒してみた 中島頌太 @burita083 / ブランドソリューション開発本部 フロントエンド部 FAANSブロック speakerdeck.com 中島からは、いまみなさまの関心が高いGitHub Copilotの導入事例について発表しました。導入箇所を検討し試したところ、同じようなコードを複数書く場面ではうまく活用できていました。新規にコードを書く場面ではまだ改善の余地があるそうです。 GitHub Copilotの全社導入とその効果については先日堀江( @Horie1024 )が記事を公開し、多くの反響をいただいています。ぜひこちらもご覧ください! techblog.zozo.com [iOS] 10年以上の歴史を持つプロダクトのRosettaフリー 〜その中で遭遇した問題と解決法を公開します〜 小松悟 @tosh_3 / ZOZOTOWN開発本部 ZOZOTOWNアプリ部 iOS1ブロック speakerdeck.com 小松からはRosettaの説明、課題、その解決方法について熱のこもった発表がありました。RosettaはApple silicon上でx86_64用のバイナリを動かす翻訳プロセスです。いつまでサポートがあるかわからないためRosettaフリーを進めました。課題は多く大変な道のりのようでしたがひとつずつやり切り、副次的効果としてビルド時間の高速化・新規メンバーのセットアップ簡易化などのメリットも得られていました。 小松もiOSDC Japan 2023に登壇しますので、ぜひ聞きに来てください! fortee.jp 最後に 司会・登壇者のみなさま LT終了後にオフライン会場では懇親会を行いました。食事と飲み物を片手に交流を楽しんでおり賑やかな雰囲気でした。iOSエンジニアの方が「Androidも開発してみたくなった」と言ってくださったり、私自身も元AndroidエンジニアながらiOSの知識が広がったのを感じたり、相互に関心を持つ良い機会となりました。みなさまご参加ありがとうございました! ZOZOでは今後もiOSやAndroidのイベントを開催していきます。そして一緒にサービスを作り上げてくれるiOS/Androidエンジニアを募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co hrmos.co
はじめに こんにちは、CTO/DevRelブロックの堀江( @Horie1024 )です。ZOZOでは GitHub Copilot を全社へ導入しました。本投稿では、GitHub Copilotの導入に際して検討した課題とその課題の解決策としてどのようなアプローチを取ったのかを紹介します。 目次 はじめに 目次 GitHub Copilotとは何か? GitHub Copilot導入の背景と目的 導入する上での課題 セキュリティ上の懸念 ライセンス侵害のリスク GitHub Copilot for Businessの利用 導入による費用対効果 試験導入による費用対効果の見積もり 試験導入の実施 対象者の選出 アンケートの設計 試験導入の実施 アンケート結果の集計 アンケート結果の考察 費用対効果の見積もり 全社導入の判断 導入決定後のGitHub Copilot利用環境の整備 社内LT会 おまけ まとめ さいごに GitHub Copilotとは何か? GitHub CopilotはGitHub社が提供するAIを活用したコード補完ツールです。開発者はコードを書き始めるか、そのコードに何をさせたいのかをコメントに記述することでGitHub Copilotのサポートを得られ、コードをよりすばやく記述できるようになります 1 。また、GitHub Copilotの能力を強化するGitHub Copilot Xのリリースも予定されています。AIとのチャットによる既存コードの分析や修正提案、Pull Requestの作成など、開発のライフサイクル全体を通してAIがサポートすることでよりスムーズな開発者体験の実現が期待されます 2 。 GitHub Copilot導入の背景と目的 ZOZOでは、 ZOZOTOWN や WEAR 、 FAANS 、 ZOZOFIT といった複数の事業でこれから実現していきたいことが多くあり、ユーザーへより多くの価値を素早く届ける体制作りを進めています。リリースまでのリードタイムのうち開発のプロセスが全てを占める訳ではありませんが、開発効率を向上させることは重要です。そして、開発効率の向上を目的とする施策の一環として、GitHub Copilotを導入することにしました。 GitHub Copilotが開発効率に与える影響について、GitHub社により行われた興味深い調査があります。この調査では、2,000名を超える開発者へのアンケート、GitHub Copilotの使用有無でタスクの完了までの時間に差が発生するかの対照実験を95名の開発者を対象に行なっています。 github.blog この調査では、抜粋すると次のような結果が報告されています。 GitHub Copilotを使用した開発者の60-75%が開発の満足度が向上したと回答 GitHub Copilotを使用した73%の開発者が集中して作業ができ、87%の開発者が繰り返し作業中の精神的疲労を軽減できたと回答 3 GitHub Copilotを使用した開発者群が55%早くタスクを完了 この調査結果を踏まえると、GitHub Copilotを導入することで開発効率を向上させる効果が期待できます。 導入する上での課題 GitHub Copilotを全社へ導入する上での課題として次の3点があげられます。 セキュリティ上の懸念 ライセンス侵害のリスク 導入による費用対効果 セキュリティ上の懸念 セキュリティ上の懸念として次の2点があります。 プロダクトのコードが学習に使用されることでの社外への流出 提案されたコードへの脆弱性の混入 私達が様々なサービスを利用する際に入力したデータは、機械学習モデルの学習データとして活用される場合があり、特に業務利用では機密情報の入力に気をつける必要があります。GitHub Copilotにおいても入力となるプロダクトのコードが学習に使用される可能性が懸念点としてあがりました。加えて、GitHub Copilotが提案するコードに脆弱性が含まれていた場合、それをそのまま受け入れることでプロダクトに脆弱性を作り込んでしまう懸念もあります。 ライセンス侵害のリスク 普段の開発でOSSを利用しない場面は無く、その利用には各OSSが採用するライセンスを遵守する必要があります。弊社でもOSSの利用ガイドラインを策定してライセンスを侵害しないよう努めています。ライセンスの種類によりますが、侵害した場合OSSを利用したプロダクトのコードの公開要求や賠償請求のリスクがあります。 GitHub Copilotが使用するOpenAIのCodexモデルの学習データにはGituHub上で公開されているOSSのソースコードも含まれます。GitHub Copilotが生成するコードは学習元のコードをそのままコピーするわけではありませんが、特定のコードスニペットがライセンス違反を問われる可能性は存在します。 GitHub Copilot for Businessの利用 GitHub Copilotには、個人での利用を目的としたfor Individualsとビジネスでの利用を目的としたfor Businessの2つのプランがあります。そして、セキュリティ上の懸念およびライセンス侵害のリスクは、GitHub Copilot for Businessを利用することで低減できます。 docs.github.com GitHub Copilot for Businessでは、プロダクトのコードはGitHub Copilotの学習に使用されません 4 。また、GitHub Copilot for Businessを利用し、脆弱性を含むコードをチェックし提案から除外するフィルタを利用した場合でもGitHub Copilotが提案するコードに脆弱性が混入する可能性はあります 5 。しかし、脆弱性を作り込まないための対策はGitHub Copilotの使用有無で変わりません。開発者は自身の書いたコードの内容をGitHub Copilotの提案を含め理解した上でPull Requestを作成し、レビュアーによるコードレビューを必ず実施することが脆弱性を防ぐ上で重要です。 また、ライセンス侵害のリスクは、公開されたコードがGitHub Copilotの提案として出力されそれをそのまま受け入れてしまった場合に発生します。GitHub Copilotの設定には「Suggestions matching public code」という項目があります。こちらをBlockedにすることで公開されたコードと一致する提案をブロックし、ライセンス侵害のリスクを低減します。加えてGitHub Copilot for BusinessではOrganization全体にこの設定を強制可能です。 docs.github.com このような対策を講じた上でライセンス侵害が問題になった場合どうすれば良いのでしょうか。この場合、 GitHub Copilot Product Specific Terms の第4項 6 および GitHub Customer Agreement の第6項、第7項が適用されます。それにより第三者からの賠償請求に対してGitHub社から無制限の補償を受けられます 7 。 導入による費用対効果 GitHub Copilotを全社へ導入する上でその費用を賄う予算をどう確保するかは課題となります。GitHub Copilotを導入することで開発効率を向上させる効果が期待できますが、予算を計上するには費用対効果を意識する必要があります。ここで、導入に係る費用はGitHub Copilotの利用料金であり、その効果は開発効率を向上させたことでどの程度コストを削減できたかです。GitHub Copilotの導入にコストメリットがあると示すことは予算の承認を受ける上で重要です。 試験導入による費用対効果の見積もり 弊社では、費用対効果を見積もるために、GitHub Copilotの試験導入を計画しました。試験導入によって実際の開発業務でどの程度コスト削減できたかを可視化し、GitHub Copilotの導入にコストメリットがあると確認することで全社への導入判断に役立てます。 試験導入の実施 試験導入では、社内の様々な開発者を対象者として選出します。対象者には試験導入の終了後にアンケートを実施し、その集計結果から費用対効果を見積もります。そして、その結果を踏まえGitHub Copilotの全社への導入を判断します。 試験導入の開始から終了までのプロセスは次のとおりです。 試験対象者の選出 アンケートの設計 試験導入の実施 アンケート結果の集計 アンケート結果の考察 費用対効果の見積もり 対象者の選出 試験対象者の選出には、様々な属性の対象者に対して満遍なく試験ができるよう次のような観点を設けました。 業務内容や使用するプログラミング言語、開発環境が異なるよう選出すること また、望ましい対象者のパーソナリティとして次の点をあげています。 新しいツールを試すのが得意 導入時にチーム内で有効な活用方法を教示できる これらの条件を元に社内の各マネージャーに対して開発効率の向上を狙いたいチームから各1名を目安に選出を依頼し、最終的に56名を対象者に選出しました。 対象者の一日の仕事の中でコーディングに費やす時間、GitHub Copilotと共に使用したプログラミング言語、開発に使用したエディタ・IDEは次のとおりです。 一日の仕事の中でコーディングに費やす時間 アンケート回答者がGitHub Copilotと共に使用したプログラミング言語 アンケート回答者が開発に使用したエディタ・IDE アンケートの設計 アンケートの設計は、 GitHub社の調査 を参考にZOZO独自の設問を追加する形で行いました。 次は実際のアンケートの設問内容です。 設問 選択肢 開発でGitHub Copilotと共に使用したプログラミング言語を教えてください。 Bash C++ Go Java JavaScript Kotlin Objective-C PHP PowerShell Python Ruby Scala Swift TypeScript VBScript その他 前問でその他を選択されえた方はこちらにプログラミング言語名をご入力ください。 記述式 あなたの職種を教えてください。 SRE バックエンドエンジニア フロントエンドエンジニア iOSエンジニア Androidエンジニア 検索/推薦/機械学習エンジニア データエンジニア QA/テストエンジニア その他 開発に使用したエディタ・IDEを教えてください。 記述式 GitHub Copilotを使用することでより生産的になった。※必須 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う 一日の仕事の中でコーディングに費やす時間はどれくらいですか? 30%以下 30%-60% 60%以上 GitHub Copilotを使用するとコーディング時のイライラが軽減される。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使うことで仕事に充実感を感じられるようになった。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使用することで繰り返し作業をより迅速にこなすことができるようになった。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使うことでより満足度の高い仕事に集中できるようになった。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使用することでより早くタスクを完了できるようになった。※必須 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使用することでフロー状態(作業に没頭し集中できる状態)に入りやすい。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使用することで検索にかかる時間が短縮された。※必須 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う GitHub Copilotを使用すると繰り返し作業に費やす精神的な負担が軽減された。 全くそう思わない そう思わない どちらでもない そう思う とてもそう思う 1日あたり、GitHub Copilotを使用することでおおよそどれくらいの時間を節約できましたか?※必須 0-0.5時間 0.5-1時間 1-2時間 2-3時間 3-4時間 4-5時間 5-6時間 7-8時間 8時間以上 GitHub Copilotについて同僚に共有したいこと(tips等をまとめたページやSlackのコメントなど)があれば教えてください。 記述式 GitHub CopilotについてGitHubへのフィードバックがあれば教えてください。 記述式 テンプレートとの差分ですが、回答者の細かい属性を取得するために次の設問を追加しています。 「開発でGitHub Copilotと共に使用したプログラミング言語を教えてください」 「前問でその他を選択された方はこちらにプログラミング言語名をご入力ください」 「あなたの職種を教えてください」 「開発に使用したエディタ・IDEを教えてください」 加えて、テンプレートに存在した設問「GitHub Copilotでの経験について同僚に知っておいてほしいことはありますか?」については次のように変更しています。 「GitHub Copilotについて同僚に共有したいこと(tips等をまとめたページやSlackのコメントなど)があれば教えてください」 また、「1日あたり、GitHub Copilotを使用することでおおよそどれくらいの時間を節約できましたか?」の設問については、回答の選択肢として選べる時間区分をより細分化しました。 試験導入の実施 試験導入は期間を2週間として実施し、試験対象者への連絡は専用のSlackチャンネルにて行いました。 試験導入の開始アナウンス 試験期間中には、GitHub Copilotを実際に使用して得た知見を試験への参加者同士で共有することを推奨しました。これはGitHub Copilotが活発に利用され、より良い試験導入とすることを意図しています。 試験参加者の間で共有された知見の例 試験導入は予定通り2週間で終了し、参加者にはGoogleフォームで作成したアンケートに回答頂きました。 アンケート終了のアナウンスと回答へのお礼 アンケート結果の集計 アンケート結果の集計結果は次のとおりです 8 。 アンケート結果の考察 設問「GitHub Copilotを使用することでより生産的になった」への回答では、78.9%がGitHub Copilotを使用することでより生産的になったと回答しています。その他の設問でも「とてもそう思う」「そう思う」の合計は、ほとんどの設問で過半数を超えています。また、設問「1日あたり、GitHub Copilotを使用することでおおよそどれくらいの時間を節約できましたか?」への回答では、58%が1日あたり30分以上時間を節約できたと回答しています。 また、アンケートの次の設問は、SPACEフレームワーク 9 の「満足と幸福」に対応しています 10 。 GitHub Copilotを使用するとコーディング時のイライラが軽減される GitHub Copilotを使うことで仕事に充実感を感じられるようになった GitHub Copilotを使うことでより満足度の高い仕事に集中できるようになった 回答結果を見ると、これらどの質問に対しても40%から50%の回答者が「とてもそう思う」「そう思う」と答えています。 加えて、次の設問はSPACEフレームワークの「効率とフロー」に対応しています。 GitHub Copilotを使用することで繰り返し作業をより迅速にこなすことができるようになった GitHub Copilotを使用することでより早くタスクを完了できるようになった GitHub Copilotを使用することでフロー状態(作業に没頭し集中できる状態)に入りやすい GitHub Copilotを使用することで検索にかかる時間が短縮された GitHub Copilotを使用すると繰り返し作業に費やす精神的な負担が軽減された 設問「GitHub Copilotを使用することでフロー状態(作業に没頭し集中できる状態)に入りやすい」への回答は「とてもそう思う」「そう思う」の合計が29.9%です。しかし、それ以外の設問では60%を超えています。 これらの結果から、ZOZOの場合においてもGitHub Copilotを利用することで開発者の「満足度と幸福度」「効率とフロー」に良い影響を与えられると言えるでしょう。 78.9%がGitHub Copilotを使用することでより生産的になったと回答する一方、「そう思わない」「全くそう思わない」という回答も存在します。この原因を探るため「GitHub Copilotを使用することでより生産的になった」への回答を職種別に集計しました。 次は「GitHub Copilotを使用することでより生産的になった」への回答の職種別の集計結果です。iOSエンジニアにおいて「そう思わない」「全くそう思わない」と回答する割合が増えています。 今回の試験ではiOSエンジニアが開発に使用した開発環境はXcodeのみでしたので、プログラミング言語別、開発環境別での集計もSwift、Xcodeが同様の結果となります。 Xcodeは、GitHub Copilotが公式にサポートしているエディタに含まれていません 11 。XcodeからGitHub Copilotを利用するには非公式のプラグインを利用する必要があり、このような結果となった一因であると考えられます。iOSアプリ開発においてもGitHub Copilotを活用しやすい環境を整えることは導入後の課題となります。 費用対効果の見積もり アンケートの集計結果を元に費用対効果を見積もります。アンケート「1日あたり、GitHub Copilotを使っておおよそどれくらいの時間を節約できましたか?」の回答結果を参照し、月あたりのコスト削減金額を計算します。そして、GitHub Copilotの利用料と比較することで費用対効果を見積もります。 まず、人件費、年間休日数、1日あたりの労働時間を次のように仮定します。 人件費 年間休日数 1日あたりの労働時間 800万/年 12 120日 8時間 この場合時給は4,082円となり、1日あたりの節約時間より計算すると平均削減金額は、1日あたり最大で4,917円/人、最小で2,372円/人になります。月(20営業日)で換算すると、月あたりの平均削減金額は次のようになります。 最大 最小 98,340円/人 47,440円/人 また、GitHub Copilotの月あたりの利用料は次のとおりです。 $19/月/人 ≒ 2,736円/人(為替レート144円 13 /ドルで計算) したがって、「GitHub Copilot導入による人件費の平均削減金額」と比較して、次の金額だけコストメリットがあると見積もりました。 最大 最小 95,604円/人 44,704円/人 全社導入の判断 前述の通り、GitHub Copilotの導入には3つの課題がありました。 セキュリティ上の懸念 ライセンス侵害のリスク 導入による費用対効果 セキュリティ上の懸念およびライセンス侵害のリスクについては、GitHub Copilot for Businessを利用することでその懸念とリスクを低減できます。また、導入による費用対効果についても十分な効果とコストメリットがあると判断したことから、GitHub Copilotの全社導入を決定しました。 導入決定後のGitHub Copilot利用環境の整備 GitHub Copilotの導入決定後、全社でGitHub Copilotを利用できる環境の整備を進めました。 まず、全社に対してGitHub Copilot利用希望者のリストアップを依頼しました。その後、コーポレートエンジニアリングチームと連携しながら総利用金額の見積りと予算確認の後、希望者全員に対してGitHub Copilotを有効化しました。また、GitHub Copilotの利用に関する社内承認フローを整備し、運用ルールについても整備しています。 社内LT会 GitHub Copilotを活用していく上で重要なのは、まずどのような使い方ができるかを開発者が知ることです。執行役員CTO瀬尾( @sonots )のアイデアもあり、GitHub Copilotのtipsについて発表するLT大会を開催しました。登壇者は試験導入への参加者の中から8名に依頼しています。 LTのタイトルは次のとおりです。 タイトル GitHub Copilot × 言語 GitHub CopilotとAndroid開発 GitHub Copilot × Kotlin/Java PowerShellでGitHub Copilot利用 GitHub Copilot × PowerShell How to use Copilot Successfully with jQuery GitHub Copilot × jQuery Terraform, YAMLでの利用例 GitHub Copilot × Terraform/YAML GitHub Copilotを使ってみた感想 GitHub Copilot × Java iOSアプリ開発におけるGitHub Copilotの活用方法 GitHub Copilot × Swift Copilot使ってみてLT GitHub Copilot × TypeScript GitHub Copilot × VBScript GitHub Copilot × VBScript LT会には約200名が参加し非常に盛り上がりました。様々な言語・フレームワークとGitHub Copilotについての事例をまとめて聞けたので個人的にも勉強になりました。社外秘の情報が含まれるので登壇資料の全てをお見せすることはできませんが、一部を抜粋して紹介しようと思います。 GitHub Copilotを使ってみた感想やまとめを見ると、当然ながら得意とする作業と苦手な作業があります。ペアプロの体験になぞらえた体験例も面白いですね。また、GitHub Copilot × Swiftの事例では、GitHub CopilotとXcodeとの相性やVSCodeを使ったコーディングについて触れられていました。どのようにすればiOSアプリ開発でGitHub Copilotをより活用できるかを探る必要があります。iOSチームでは全社導入後にもiOSアプリ開発でのGitHub Copilotの活用方法について勉強会を開いており、他の領域でも同様な勉強会が開催できればと考えています。 GitHub CopilotとAndroid開発 :GitHub Copilot × Kotlin/Java Terraform, YAMLでの利用例 : GitHub Copilot × Terraform/YAML GitHub Copilotを使ってみた感想 : GitHub Copilot × Java iOSアプリ開発におけるGitHub Copilotの活用方法 : GitHub Copilot × Swift 型定義をGitHub Copilotに与えること提案の精度が上がることもLTで報告されており、GitHub Copilotを活用していくヒントとなりました。 Copilot使ってみてLT : GitHub Copilot × TypeScript 巨大なjQueryで書かれたコードに対して、GitHub CopilotにJSDocで型定義を書いてもらうことでコードリーディングを容易にする事例もありました。初見のコードに対してそのコードが何をしているかを理解する上でもGitHub Copilotは有効活用できそうです。 How to use Copilot Successfully with jQuery : GitHub Copilot × jQuery ZOZOTOWNのコードベースではJavaへのリプレイスが進んでいますが、今でもシステムの大部分をVBScriptが支えています 14 。今回の試験導入で、VBScriptでの開発についてもGitHub Copilotが有効に活用できる事例を得られたことは、既存機能の改修とリプレイスを行う際にもプラスになると考えています。 GitHub Copilot × VBScript 事例 GitHub Copilot × VBScript まとめ おまけ GitHub Copilotが提案したコメントなのですが、謎のクリスマス事件として盛り上がりました。 まとめ 現在ZOZOでは社内のほぼ全ての開発者がGitHub Copilotを活用して開発業務を行なっています。GitHub CopilotはGitHub Copilot Xとして今後も継続的に機能の追加が予定されています。それにより開発効率を向上させユーザーへより多くの価値を素早く届けるための手助けとなってくれることを期待しています。また、GitHub NextのページではGitHub Copilotに関する興味深いプロジェクトを確認できます。これらのプロジェクトによってどのような開発者体験が実現されるのか楽しみです。 本記事では、ZOZOでのGitHub Copilotの導入にあたって課題となる点や、課題にどのように対応したのかを紹介しました。本記事がGitHub Copilotの導入の一助となれば幸いです。 さいごに ZOZOでは一緒に楽しく働くエンジニアを絶賛募集中です。ご興味のある方は下記リンクからぜひご応募ください。 corp.zozo.com https://docs.github.com/ja/copilot/overview-of-github-copilot/about-github-copilot-for-individuals ↩ https://github.blog/jp/2023-03-23-github-copilot-x-the-ai-powered-developer-experience/ ↩ 原文では精神エネルギーの節約とありましたが、ここでは精神的疲労を軽減と表現しました。 ↩ GitHub Copilot for Business Privacy Statement でGitHub Copilot for Businessが収集するデータを確認しました。その結果、GitHub Copilot for Businessではコードスニペットデータは保持されず、プロダクトのコードは学習に使用されないと判断できました。また、GitHub for Individualsの設定項目に「Allow GitHub to use my code snippets for product improvements」があります。GitHub for Individualsではこの項目の設定値によって学習への使用有無を制御可能です。 ↩ GitHub Copilotには、リアルタイムに安全でないコーディングパターンをブロックするAIベースの脆弱性防止システムが導入されています。詳細はこちらの記事「 GitHub Copilot now has a better AI model and new capabilities 」をご覧ください。 ↩ 「Suggestions matching public code」をBlockedに設定することが契約に基づいて補償する条件として明記されています。 ↩ 「GitHub Copilot Product Specific Terms」および「GitHub Customer Agreement」は2023/6/22時点のものを参照し確認しています。また、弊社が締結した契約では無制限の補償を受けられることをGitHub社に確認済みですが、保証内容は契約しているプラン、契約時期によって異なります。エンタープライズ契約でも主契約次第で補償内容が異なる可能性もありますので、必ずGitHub社にご確認をお願いいたします。 ↩ 記述式での回答結果の集計は除いています。 ↩ https://queue.acm.org/detail.cfm?id=3454124 ↩ Albert Ziegler, et al. "Productivity Assessment of Neural Code Completion" arXiv preprint arXiv: 2205.06537. 2022, p.12. ↩ GitHub Copilotがサポートするエディタは2023年6月時点でVisual Studio Code、Neovim、JetBrains系IDE、Visual Studioです。 ↩ こちらは仮の人件費です。弊社実績とは関係ありません。 ↩ 2023年6月末の為替レートを参照しています。 ↩ https://technote.zozo.com/n/ndf768964d62e ↩
はじめに こんにちは、計測プラットフォーム開発本部SREの纐纈です。 2023年6月23日にFindy社によるオンラインイベント「CI/CD最前線〜今開発現場が直面している課題とは? Lunch LT」が開催されました。このイベントでは、株式会社アンドパッドさん、株式会社サイバーエージェントさん、エムスリー株式会社さんから一人ずつ、弊社からも私がLTをしてきましたので、こちらのブログでも報告させていただきます。 findy.connpass.com 今回のイベントでは、CI/CDを社内で導入・推進されている、もしくはCI/CDの取り組みの具体的な方法や事例を知りたい方が参加者の対象となっていました。そのため、ここ最近CI/CDの改善に努めていた弊チームの取り組みがちょうど良く紹介できると思い、発表者として手を上げさせてもらいました。 今回の発表に使ったスライドはこちらです。 speakerdeck.com 登壇内容まとめ 今回の私の発表は、今までのチームの取り組みまとめということで、そのほとんどはブログ内ですでに公開しているものです。これらをLTの時間内に伝わるようにまとめて、改善効果が伝わりやすくなるように努めました。 要約すると、元は週2〜3時間、2人でやっていた定期リリース作業を、PRをマージするだけでリリースできるように安全性を担保しつつ自動化するまでという内容です。視聴者にとって全てが刺さることはないと思いますが、一部だけでも参考になる箇所があれば良いなと思いました。 これまで公開したブログは次のとおりです。詳しく知りたい方はぜひこちらをご覧下さい。 techblog.zozo.com techblog.zozo.com techblog.zozo.com techblog.zozo.com techblog.zozo.com 登壇後の所感 登壇後のフィードバックやTwitter( #CICD_findy )実況を見たところ、ポジティブな反応が多くあり、安心しました。特に負荷試験まで自動化されていることに関心を持たれた人が多かったです。普段社外からチームでやっていることに対して評価をもらうことはないので、新鮮な気持ちになりました。今後も機会があればこのような発表ができたらと思います。 謝辞 今回のイベントはFindy社の主催でしたので、Findyの方々には様々なサポートをしていただきました。また、登壇までサポートしてくれた社内のDevRelチームや弊チームにもこの場を借りて感謝申し上げたいと思います。イベントに参加して視聴してくださった皆様も、ありがとうございました。 最後に ZOZOでは、プロダクト開発以外にも、今回のようなイベントの開催や登壇など、外部への発信も積極的に取り組んでいます。 一緒にサービスを作り上げてくれる方はもちろん、エンジニアの技術力向上や外部発信にも興味のある方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! corp.zozo.com
はじめに こんにちは、CISO部の 兵藤 です。日々ZOZOの安全のためにSOC対応を行なっています。 本記事ではサイバー脅威インテリジェンス(CTI)のプラットフォームの1つであるOpenCTIをAzure上に構築した事例を紹介します。また、CISO部ではその他にもZOZOを守るための取り組みを行っています。詳細については以下の「フィッシングハントの始め方」をご覧いただければと思います。 techblog.zozo.com 目次 はじめに 目次 背景と概要 OpenCTIとは 構築 概要 Azure VM でのOpenCTI実装 Keyコンテナでの管理 Azure Application Gatewayを用いたHTTPS化 Azure ADと連携しSAML認証を実装 OpenCTIのコンフィグ変更 Azure ADの設定 SAML認証フローの確認 Azure Network Security Group(NSG)でアクセス制限 Azure File Storageを用いたデータのバックアップ 構築結果 まとめ おわりに 背景と概要 そもそもサイバー脅威インテリジェンスとは何かですが、「脅威情報を意思決定のプロセスに必要な状態へ分析、解釈、集約、補強されたもの」で、インテリジェンスのレベル感によって大きく3段階に分かれます。 Strategic level Operational level Tactical level SOCでは悪意のあるドメイン名やマルウェアのハッシュ値など、Tactical levelでのインテリジェンス(IOCなど)を日々扱うことが多いです。詳しくはサイバー脅威インテリジェンスについて調べていただければと思いますが、これらのインテリジェンスは活用スパンが短く、自動運用を進めることが望ましいです。ですが、ZOZO内では上記Tactical levelでのインテリジェンスを各個人が扱い、SOC対応に活かしている状況でした。 SOC対応を実施するにあたって各個人で調査収集していた情報を1つのプラットフォームで自動収集、管理し、ゆくゆくはSIEMと連携したいと考えていました。そこで、CTIのプラットフォームのOSSであるOpenCTIを検討しました。 OpenCTIとは OpenCTIについては先ほど少し紹介した、CTIのプラットフォームのOSSであり、Filigranが提供しているものです。公式サイトは こちら をご参照ください。 ReactやGraphQL、Elasticsearchなどを使用しており、モダンでカッコいい見た目がたまらなくセキュリティエンジニアをしている感が出ているプラットフォームです。 ※以下画像は公式 1 から引用。 日本語での詳しい解説は「にのせき」さんの記事 2 が参考になります。 このOSSはDockerを用いて構築が可能で、初期構築が素早くできます。また、OpenCTIの機能を拡張するための「Connectors」についてもDockerイメージが提供されており、容易に機能拡張が可能です。アプリケーションの構築の経験が少ない方でも、Dockerを用いて簡単に構築できるのでおすすめのOSSです。 OpenCTIの機能を拡張するための「Connectors」の紹介 構築 概要 ZOZOではAzure上にOpenCTIを構築しました。以下が概要図です。 Azure VM上にDockerを用いてOpenCTIを構築 環境変数などはKeyコンテナで管理 Azure Application Gatewayを用いてHTTPS化 Azure ADと連携しSAML認証を実装 Azure Network Security Group(NSG)でアクセス制限 Azure File Storageを用いてデータのバックアップを実施 Azure VM でのOpenCTI実装 OpenCTIは先ほども述べたようにDockerでの構築が可能です。要するにコンテナサービスを提供しているクラウドサービスであれば構築可能です。例えばAzureでのコンテナサービスは以下のようなものがあります。 Azure Container Instances Azure App Service Azure Container Apps Azure Kubernetes Service (AKS) 上記のうちコンテナを複数起動する場合は後半の2つ、Container AppsまたはAKSを検討することになると思います。筆者も最初はこの2つを検討し、Kubernetesで構築するほどでもなく簡易的に複数コンテナを起動できれば良かったのでContainer Appsでの構築を目指しました。では何故Azure VMでの構築になったのでしょうか? 答えは簡単です。Container Appsでの構築を進めていると、インスタンスの起動の段階で壁にぶち当たります。Elasticsearchなどのコンテナがスペック不足で落ちてしまうのです。Microsoft公式の ドキュメント によると従量課金プランだと記事執筆の時点(2023年6月15日)ではメモリ4Gがリミットとなってます。ですので、これ以上スペックを上げることがContainer Appsではできそうもありませんでした。ここら辺のお話や知見はSlackのコミュニティでも議論されていて、今回の私の知見も共有しています。このようにSlackではOpenCTIに関する情報が多く共有されているので、構築する際には参考になると思います。 SlackコミュニティにおけるAzure Containerの見解 これらの背景により、今回はAzure VM上にDockerを用いてOpenCTIを構築しました。 ここまでAzureにこだわっている理由は後ほど説明します。 Keyコンテナでの管理 OpenCTIを構築する際には、環境変数を設定しなければなりません。Connectorsの種類によっては、APIキーなどの秘匿情報を設定する必要があります。このような秘匿情報をハードコーディングすることはよろしくないので、Keyコンテナを用意し、そこで環境変数を管理するようにしました。 この変数についてですが、コンテナを起動する際に必要なので、Keyコンテナから値を読み込むOSSツールの vaultenv を利用しています。 github.com 以下のフォーマットのように記述すれば環境変数を置き換えてくれるので便利です。 APIKEY={{ kv "https://keyvault-name.vault.azure.net/secrets/example-key" }} Azure Application Gatewayを用いたHTTPS化 Application GatewayでSSL/TLSオフロードをしています。OpenCTIはデフォルトでは8080のportで起動するのでこのportへ443からの通信をマッピングすることでHTTPS化を実現しています。特段変わったことはしていません。 Azure ADと連携しSAML認証を実装 OpenCTIはデフォルトでID、Password認証です。これをAzure ADと連携しSAML認証に変更しました。この実装については文章化されているものが少ないので詳細を記載しようと思います。公式のドキュメントは こちら のリンクを参照してください。 OpenCTIのコンフィグ変更 OpenCTIのデフォルトのdocker-compose.ymlは以下のようになっています。 opencti: image: opencti/platform:5.7.4 environment: - NODE_OPTIONS=--max-old-space-size=8096 - APP__PORT=8080 - APP__BASE_URL=${OPENCTI_BASE_URL} - APP__ADMIN__EMAIL=${OPENCTI_ADMIN_EMAIL} - APP__ADMIN__PASSWORD=${OPENCTI_ADMIN_PASSWORD} - APP__ADMIN__TOKEN=${OPENCTI_ADMIN_TOKEN} - APP__APP_LOGS__LOGS_LEVEL=error - REDIS__HOSTNAME=redis - REDIS__PORT=6379 - ELASTICSEARCH__URL=http://elasticsearch:9200 - MINIO__ENDPOINT=minio - MINIO__PORT=9000 - MINIO__USE_SSL=false - MINIO__ACCESS_KEY=${MINIO_ROOT_USER} - MINIO__SECRET_KEY=${MINIO_ROOT_PASSWORD} - RABBITMQ__HOSTNAME=rabbitmq - RABBITMQ__PORT=5672 - RABBITMQ__PORT_MANAGEMENT=15672 - RABBITMQ__MANAGEMENT_SSL=false - RABBITMQ__USERNAME=${RABBITMQ_DEFAULT_USER} - RABBITMQ__PASSWORD=${RABBITMQ_DEFAULT_PASS} - SMTP__HOSTNAME=${SMTP_HOSTNAME} - SMTP__PORT=25 - PROVIDERS__LOCAL__STRATEGY=LocalStrategy ports: - "8080:8080" depends_on: - redis - elasticsearch - minio - rabbitmq restart: always ローカル認証に関する環境変数は PROVIDERS__LOCAL__STRATEGY=LocalStrategy です。ここに追加で環境変数を設定していき、SAML認証のみを行うようにコンフィグを変更できます。以下のように追加します。 # docker-compose.yml ...省略 - SMTP__PORT=25 - PROVIDERS__LOCAL__STRATEGY=LocalStrategy - PROVIDERS__LOCAL__CONFIG__DISABLED= true # ここから追加した環境変数 - PROVIDERS__SAML__STRATEGY=SamlStrategy - "PROVIDERS__SAML__CONFIG__LABEL=Login with SAML" # ログイン画面のボタン名 - PROVIDERS__SAML__CONFIG__ISSUER=${PROVIDERS_SAML_CONFIG_ISSUER} # Issuer名 - PROVIDERS__SAML__CONFIG__ENTRY_POINT=${PROVIDERS_SAML_CONFIG_ENTRY_POINT} #IdPのエンドポイント - PROVIDERS__SAML__CONFIG__SAML_CALLBACK_URL=${PROVIDERS_SAML_CONFIG_SAML_CALLBACK_URL} # SAML認証後のリダイレクト先 - PROVIDERS__SAML__CONFIG__CERT=${PROVIDERS_SAML_CONFIG_SAML_CERT} # IdPの証明書 ports : - "8080:8080" ...省略 これらの設定でSAML認証のみを行うようになります。実際に認証画面を覗きにいくと以下のようにSAML認証のみ可能になっています。各種環境変数の詳細な設定値については後ほどADの設定の際に確認します。 SAML認証のみ有効な様子 Azure ADの設定 OpenCTIのSAML認証を有効化するためにAzure ADでアプリを登録し、SAML認証の各種設定をしなければなりません。まずはAzure ADでアプリを登録します。アプリの登録は公式ドキュメントの「 独自のアプリケーションの作成 」を参考にしてください。 続いて、SAML認証を設定します。Azure ADのアプリの設定画面から「シングル サインオン」を選択し、「SAML」を選択します。 ここから具体的なSAMLの設定に移っていくのですが、その前にSAML認証でAzure AD、OpenCTIで設定が必要な各種項目について整理しておきます。 項目 設定箇所 説明 識別子(Entity ID) Azure AD SPを一意に識別するためのID。URL形式をとり、IdPとSPで一致させる必要がある。別名「Issuer」。 応答URL(Assertion Consumer Service URL) Azure AD SAML Responseの宛先URL。 Issuer OpenCTI IdPを一意に識別するためのID。URL形式をとり、IdPとSPで一致させる必要がある。別名「Entity ID」。 Entry Point OpenCTI IdPのエンドポイント。SAML Requestの宛先URL。 SAML Callback URL OpenCTI SAML Responseの受信URL。 CERT OpenCTI SAML Responseに対して署名を検証するための証明書。 上記「識別子(Entity ID)」と「Issuer」はIdP(Azure AD)とSP(OpenCTI)で一致させないといけない点に注意して設定します。「応答URL(Assertion Consumer Service URL)」については「SAML Callback URL」で設定したURL(ここでは「https[:]//opencti-domain/autth/saml/callback」とします)を記載します。このURLの階層はそのまま変えずにドメインを変更すればいいでしょう。「CERT」については以下の赤枠の「ダウンロード」をクリックすると証明書がダウンロードできます。 最後に「Entry Point」については以下の「ログインURL」の項目をOpenCTIに設定すれば良いです。 以上でAzure AD(とOpenCTI)の設定は完了です。 SAML認証フローの確認 OpenCTIのSAML認証がどのように動作しているか確認をしていこうと思います。SAML認証のフローには大きく「SP-initiated」と「IdP-initiated」の2つがあります。OpenCTIの認証前の緑色のボタンは「SP-initiated」のフローを開始するボタンなので、今回はこちらの方を確認していきます。 通信のフローを確認するためにChromeの拡張機能「 SAML-tracer 」をインストールしておきます。この拡張機能を用いるとSAML認証の通信のフロー(SAML Request, SAML Response)を簡単に確認できます。以下が「SP-initiated」のSAML Requestです。 < samlp : AuthnRequest xmlns : samlp = "urn:oasis:names:tc:SAML:2.0:protocol" ID = "_80a2e36b3cc742a5b5b7" Version = "2.0" IssueInstant = "2023-06-06T04:29:02.704Z" ProtocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Destination = "https://login.microsoftonline.com/XXXXXXX-XXXXXX-XXXXXX-XXXXXX/saml2" AssertionConsumerServiceURL = "https://opencti-domain/auth/saml/callback" > < saml : Issuer xmlns : saml = "urn:oasis:names:tc:SAML:2.0:assertion" > http://opencti/entity-id </ saml : Issuer> < samlp : NameIDPolicy xmlns : samlp = "urn:oasis:names:tc:SAML:2.0:protocol" Format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate = "true" /> < samlp : RequestedAuthnContext xmlns : samlp = "urn:oasis:names:tc:SAML:2.0:protocol" Comparison = "exact" > < saml : AuthnContextClassRef xmlns : saml = "urn:oasis:names:tc:SAML:2.0:assertion" > urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport </ saml : AuthnContextClassRef> </ samlp : RequestedAuthnContext> </ samlp : AuthnRequest> Requestの中身を確認すると、 NameIDPolicy の Format 項目で emailAddress を要求しているのでメールアドレスの単位でOpenCTIのユーザを一意に認識していると考えられます。また、 AllowCreate の項目が true となっているのでOpenCTI側でユーザを作成していなくてもIdP(Azure AD)がOpenCTIにユーザを作成し初期化してくれます。ProtocolとしてはHTTP-POSTバインディングで返信するように要求しています。認証が成功すると <NameID> の項目で emailAddress がSAML Responseで返ってきます。 HTTPベースでこれらの情報をやり取りするのでHTTPSでの実装をしていることが前提になります。 Azure Network Security Group(NSG)でアクセス制限 SAML認証でもアクセス制御を行っていますが、それ以上にNSGで接続元を制限していたりと多層防御を実施しています。このように設計していた場合、Azure ADを用いたSAML認証の認証フローを許可するのが面倒かと思われるかもしれません。 ですが、Azureで実装していればその点の面倒さは解消されます。Azureにこだわっていたのはこのためです。以下のようにソースを「Service Tag」、ソースサービスタグを「AzureActiveDirectory」に設定しておけば煩わしい設定が一気に簡単になります。 受信セキュリティ規則で設定を行う一例 Azure File Storageを用いたデータのバックアップ コンテナの難点にデータの永続化が挙げられると思います。OpenCTIではDocker volumeを利用して永続化を行なっていますが、本設計だとVMを丸ごとスナップショットとして保存しておかないといけなくなります(それでもいいのですけど)。ですので、VM内のコンテナのファイルをAzure File Storageにマウントし、OpenCTIのMinIOのデータを外部記憶しています。 docker-compose.ymlのvolumesの部分を以下のように変更することで実現しています。 ...省略 volumes : esdata : s3data :  # ここから変更 driver : local driver_opts : type : cifs o : "mfsymlinks,vers=3.0,username=${AFS_NAME},password=${AFS_KEY},addr=${AFS_NAME}.file.core.windows.net" device : "//${AFS_NAME}.file.core.windows.net/${AFS_CONTAINER}" 正常にマウントされていれば以下のようにAzure File Storageにデータが保存されていると思います。 基本的にこのFile StorageはOpenCTIからしかアクセスしないのでNW制御でVnetからしかアクセスできないようにしておくのがおすすめです。また、アクセスキーについては定期的に更新しておきましょう。 構築結果 上記画像のようにAzure上にOpenCTIを構築できました。これにより自動でインテリジェンスを取り込み、管理できるようになりました。Search欄から気になるIOCなど検索できて便利です。 また、日本語対応もしていて設定がとてもしやすいと感じました。開発してくださったNFLabs 3 の皆様ありがとうございます。 まとめ サイバー脅威インテリジェンスのプラットフォームの1つであるOpenCTIをAzure上に構築する取り組みについて紹介しました。意外とAzureでのOpenCTI構築やSAML認証の実装事例が少ないので、OpenCTIをこれから構築する方達のご参考になれば幸いです。 ZOZOではこれからも脅威情報を逐次収集し、意思決定プロセスに必要なインテリジェンスの活用に努めていき、ZOZOの安全性の向上を図っていきたいと考えています。続いてのフェーズではIOCのトリアージを詳細に詰めていこうと目論んでいます。 おわりに ZOZOでは、一緒に安全なサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクから是非ご応募ください! corp.zozo.com OpenCTIの Github ↩ OpenCTI入門 ↩ OpenCTIの日本語化にあたって ↩
はじめに こんにちは。DevRelブロックの @wiroha です。6月27日に「 WWDC23 報告会 at LINE, ZOZO, ヤフー 」を開催しました。WWDCに参加した各社のエンジニアが新しく発表された技術や得た知見・情報などを共有するイベントです。今年はオンラインと一部オフラインのハイブリッドで開催しました。 登壇内容まとめ 3社の社員によるLTとパネルディスカッションを行い、その後は交流会で盛り上がりました。なお本イベントはAppleがNDAを締結した開発者にのみ公表している情報を取り扱っており、参加はApple Developer Programに加入している方に限定させていただきました。本レポートもLTの詳細は割愛し、雰囲気をお伝えできればと思います。 コンテンツ 登壇者 What’s new in image processing たなたつ (田中 達也) ◆ヤフー Hello Object Capture for iOS! 森口 友也 ◆ZOZO Animate SF Symbols 羽柴 彩月 ◆LINE Whatʼs new in privacy 2023 大塚 達也 ◆ヤフー The New Potential of Widgets 山田 楓也 ◆ZOZO What’s new in Swift 5.9 Hiraki ◆LINE Panel Discussion 早石 明浩 ◆LINE 冨田 悠斗 ◆ヤフー 永井 崇大 ◆ZOZO まつじ (松本淳之介) ◆LINE 交流会 発表風景 ヤフーの田中さま ZOZOの森口 LINEの羽柴さま ヤフーの大塚さま ZOZOの山田 LINEのHirakiさま Panel Discussion 挙手をしながらパネルディスカッションを進行 乾杯をしてカジュアルな雰囲気でパネルディスカッションがはじまりました。随時質問を挟み会場の皆さんに手をあげていただき、双方向のコミュニケーションを取りながら楽しく進行しました。 最も印象に残った発表はApple Vision Pro 最も印象に残った発表は「Apple Vision Pro」で満場一致でした。久しぶりの「One more thing...」に現地会場は大興奮で一体感があったそうです。 興味があったセッションはUIKitやARKitなど各々の好きな分野を熱く語っていました。試したい機能やプロジェクトで実装したいことについては、普段の業務知識や関心があるからこその発想があげられていました。 前日の現地での様子を紹介 パネリストの3人より前日・当日のタイムラインを写真で共有いただきました。現地に参加した方は前日からノベルティの受取や著名な方との写真撮影をしており、イベントを楽しむ様子が伝わってきます。日本にいた方はヤフー紀尾井町オフィスのLODGEにて行われたリアルタイム視聴イベント「 Extended Tokyo - WWDC 2023 」に参加していました。こちらの様子はレポート記事がありますのでこちらもぜひご覧ください! techblog.zozo.com 交流会 パネルディスカッションの後、オフライン会場にて交流会を実施しました。Apple JapanからTechnology Evangelistの豊田さまにご参加いただき、参加者からのさまざまな質問に回答いただきました。登壇したみなさまもリラックスした様子で交流を楽しんでいました。 Apple Japanの豊田さま 最後に みなさまご参加ありがとうございました。WWDCの報告会は毎年の恒例イベントです。また来年も開催できることを楽しみにしています! ZOZOでは一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co
はじめに こんにちは、ECプラットフォーム基盤SREブロックの織田と、カート決済SREブロックの遠藤です。 本記事では、Istio Rate Limitの概要とZOZOTOWNでの導入事例を2つご紹介します。 目次 はじめに 目次 現在のZOZOTOWN Istio Rate Limitの概要 Istio Rate Limitとは? 仕組み どのような制限ができるのか? Circuit Breakerとの違い 導入事例 事例1. マイクロサービスから外部APIへのリクエスト制限 構成 設定と計測のポイント 1. レイテンシの計測 2. 1つのAPIに対して2重の制限を設定 事例2. オンプレミス環境のリクエスト制限 構成 設定と計測のポイント 1. 商品別の注文リクエスト数制限 2. 閾値決定のための負荷試験 今後の展望 感想 最後に 現在のZOZOTOWN ZOZOTOWNではオンプレミス環境からクラウドへの大規模なリプレイスを行っており、クラウド移行と共にモノリシックな開発からマイクロサービス開発への移行を推進しています。クラウド、マイクロサービスへの移行では、ストアドプロシージャからの脱却やマイクロサービスごとにDBを分離するなどの対応を実施しています。 また、サービスメッシュの導入も並行して進めており、マイクロサービスの基盤にはKubernetes、サービスメッシュにはIstioを採用しています。 マイクロサービス化への取り組みについては、以前イベントを行っており、そのまとめとしてTECH BLOGを投稿しています。ご興味のある方はご覧ください。 techblog.zozo.com Istioに関する取り組みについてもTECH BLOGを投稿しています。 techblog.zozo.com techblog.zozo.com Istio Rate Limitの概要 Istio Rate Limitとは? Istio Rate Limitは、Istioの機能の一部で特定のエンドポイントやAPIなどに対するリクエスト数を制限できる機能です。 リクエスト数を制限することで急激なトラフィックの増加によるアプリケーションやバックエンドサービスの過負荷を防ぎ、サービスの安定性の向上が実現できます。Rate LimitはIstio独自のものではなく、ネットワークトラフィックを制限することを意味する単語として利用されていることが多いです。 仕組み Envoyのネイティブレートリミットを利用し、EnvoyFilterで動的にトラフィックを制限します。ここでは、外部リクエストと内部リクエストに対するRate Limitの仕組みを簡単に説明します。 まずは、外部リクエストについてです。外部からのリクエストに対してIstio Rate Limitがどのような通信をしてトラフィックを制御するか説明します。 外部からのリクエストがIngress Gateways(istio-proxy Container)に届く ingressgatewayからRate Limitにリクエストを行う Rate LimitがRedisに書き込みを行い、ingressgatewayにレスポンスを返す 設定ファイル(ConfigMap)に定義されている制限に引っかかっていればingressgatewayが429を返し、制限に引っかかっていない場合はアップストリームにトラフィックが流れる 次に内部リクエストについて説明します。外部リクエストと基本的に同じですが、リクエスト元やRate Limitに問い合わせするリソースが異なります。 Service Pod内のService Containerからサイドカーであるistio-proxy Containerにリクエストが届く istio-proxyからRate Limit Podにリクエストを行う Rate Limit PodがRedisに書き込みを行い、istio-proxyにレスポンスを返す 設定ファイル(ConfigMap)に定義されている制限に引っかかっていればistio-proxyが429を返し、制限に引っかかっていない場合はアップストリームにトラフィックが流れる Redisに書き込まれるkeyとvalueは、 <domain>_<key>_<value>_<unix-time> とリクエストカウントの組み合わせとなります。 domain、key、valueは、Rate Limitの設定ファイルに定義している値が使われます。Redisに書き込まれるkeyは、 GenerateCacheKey で生成され、以下のように設定ファイルを定義している場合、 global-ratelimit_generic_key_zozotown_1687238460 となります。 domain : global-ratelimit descriptors : - key : generic_key value : zozotown rate_limit : unit : second requests_per_unit : 1 どのような制限ができるのか? メッシュ内の全トラフィックに対して制限可能です。例えば、内部サービスAから内部サービスBへのリクエストや内部サービスCから外部サービスDへのトラフィックなど様々なケースに対して制限を設定できます。 ただ、外部へのリクエストに対して制限を設ける場合は、 Service Entry を作成する必要があります。Service Entryを追加することで外部サービスを論理的にメッシュ内に取り込み、メッシュ内サービス同様にアクセスやルーティングできるようになります。 制限を設定できる範囲としては、 Global Rate Limit と Local Rate Limit があります。両者の違いは、以下のようになります。 Global Rate Limitは、メッシュ全体へ制限を適用します。外部のインメモリーデータストアにデータを格納し、各ワークロード(Pod)から書き込みや参照が行われます。 Local Rate Limitは、ワークロード(Pod)毎に制限を適用します。サイドカーとしてinjectされたistio-proxyごとにインメモリーでデータを持ちます。ワークロードごとに制限がされているため、ワークロードが増える度にアップストリームへの制限は緩くなります。 Rate Limitの評価は、Local Rate Limit、Global Rate Limitの順で実行されます。そのため、両者を組み合わせて利用することでGlobal Rate Limitの負荷を軽減できます。 制限可能な単位は、 Rate limit definition に明記されていて1秒、1分、1時間、1日です。例えば、1分間に100リクエストまで通常通り処理し、101リクエスト目以降は固定のレスポンスコードを返すというようなことができます。 1秒間で5リクエストの制限を設定できますが、2秒間で10リクエスト、3秒間で15リクエストなどの制限は設定できません。1秒間で5リクエストの設定にしておけば、2秒間で10リクエスト、3秒間で15リクエストの制限になりそうですが、実際の挙動は異なるので細かな設定が必要な場合は注意が必要です。 ただ、設定できる単位などについてはユーザの需要に基づいて機能が追加されていくとのことなので、issueを立てたりPRを作成してみると良いかもしれません。 このように柔軟性が高いため、マイクロサービス環境でのトラフィック制限を簡単かつ効果的に実現可能です。 Circuit Breakerとの違い Rate Limitはトラフィック制限、Circuit Breakerは障害回避とタイムアウト制御のためのもので、両者ともに異なる問題に対するアプローチです。 Circuit Breakerは、あるサービスの障害を検知した場合に通信を遮断し、サービスの復旧を検知すると通信を復旧させる仕組みです。特定マイクロサービスの障害を検知した場合にそのマイクロサービスへの通信を遮断することによって、1つの障害が連鎖的な障害となるカスケード障害を避けることができます。 ZOZOTOWNでは、Istio Circuit Breakerについても採用しています。 techblog.zozo.com 導入事例 ここからはシステムへの導入事例を2つ紹介します。両事例ではメッシュ全体に制限を適用させ、ワークロードの増減に影響されずアップストリームへのリクエスト制限を一定にしたかったため、Global Rate Limitを採用しました。 マイクロサービスから外部APIへのリクエスト制限 オンプレミス環境のリクエスト制限 事例1. マイクロサービスから外部APIへのリクエスト制限 事例1では、マイクロサービスから外部APIへのリクエスト制限を導入した事例について紹介します。 2023年5月頃に利用者の年齢に応じたマーケティング施策を実施可能にするため、会員情報を扱うマイクロサービス(以降、会員基盤)で年齢認証の機能をリリースしました。実際に2023年6月1日から6月30日の間で、年齢認証を利用したマーケティング施策を実施しています。 年齢認証の機能では、本人確認を実施するために外部サービス LIQUID eKYC を利用しており、LIQUID社からプレスリリースも出されています。 liquidinc.asia LIQUID eKYCは、AI審査で本人確認業務を自動化するサービスで、撮影画像の品質をチェックする画像処理技術や本人確認書類の文字を読み取るOCR技術などの精度の高さが特徴です。 会員基盤からLIQUID eKYCへのリクエスト数に制限を設けるため、Rate Limitを導入しました。制限を設けずに年齢認証が必要な現在行われている施策などを実施した場合、LIQUIDへのリクエストがスパイクし過負荷になってしまう可能性があるためです。 構成 会員基盤周辺の構成図は、以下のようになっています。 会員基盤からLIQUIDへは、Service Entryでメッシュ内のサービスがLIQUIDにアクセスやルーティングできるようにし、 Virtual Service でルーティングの設定を行い、 Destination Rule でルーティングが発生した際にトラフィックに適用されるポリシーを設定します。EnvoyfilterとRate Limit ConfigMapは、どのようなリクエストに対してどのような制限をするか設定します。 Rate Limitサービスはマイクロサービスと同じEKSにデプロイし、RedisはAWS ElastiCache for Redisを利用しています。現在、Rate Limitを利用しているマイクロサービスは多くないため、Rate LimitサービスとRedisはKubernetes Clusterごとに共通のものを使用しています。 設定と計測のポイント 会員基盤へ導入するにあたって実施したことを2つほど紹介します。 1. レイテンシの計測 Rate Limitを導入することでレイテンシが悪化してしまうというような懸念も考えられます。私達は普段からAPIの性質に応じて目標レイテンシを設定しているため、今回のような外部APIをコールするAPIに対しても目標を設定しRate Limitの有無でどのような変化があるか確認しました。 実際に計測した方法は、Rate Limitの有無がAPIのレイテンシにどのように影響を与えるかというシンプルな方法です。EnvoyFilterの作成、削除でRate Limitの有無を設定できるため、とても容易に計測できました。 計測結果としては、Rate Limitを設定していた場合に約30msほどレイテンシが高くなるという結果となりました。マイクロサービスやインフラの構成によって差異が発生するため参考程度にしていただければと思います。 2. 1つのAPIに対して2重の制限を設定 LIQUID APIへのリクエストに対して1秒と1分単位の制限を2重で設定しています。ここでは、EnvoyFilterとRate Limit configの設定例を紹介します。 まずはEnvoyFilterです。特定のマイクロサービスからZOZOTOWNへのリクエストに対してRate Limitで制限するための設定になります。 apiVersion : networking.istio.io/v1alpha3 kind : EnvoyFilter metadata : name : zozotown spec : configPatches : ... - applyTo : HTTP_ROUTE match : context : SIDECAR_OUTBOUND routeConfiguration : vhost : name : zozo.jp:443 route : name : zozotown patch : operation : MERGE value : route : rate_limits : - actions : - generic_key : descriptor_value : zozotown-per-second - actions : - generic_key : descriptor_value : zozotown-per-minute 設定項目の説明をすると ApplyTo は、パッチを適用する対象を指定します。 Match(EnvoyConfigObjectMatch) は、パッチを適用する条件を指定します。SIDECARからHTTP_ROUTEの vhost(zozo.jp:443) に定義されている route(zozotown) に対するOUTBOUNDトラフィックに適用されます。SIDECARに設定されているルートコンフィグの確認は、 istioctl proxy-config route <pod name> で確認可能です。 Patch は、対象の扱い方を指定します。マッチしたリクエストに対してRate Limitで制御するためにdescriptorを付与しますが、どのような値を付与するかは descriptor_value に定義します。1秒間の制御に利用する zozotown-per-second と1分間の制御に利用する zozotown-per-minute を付与する設定となっています。 次にRate Limitの設定ファイルです。 domain : global-ratelimit descriptors : - key : generic_key value : zozotown-per-second rate_limit : unit : second requests_per_unit : 10 - key : generic_key value : zozotown-per-minute rate_limit : unit : minute requests_per_unit : 100 Rate Limitが制御する際に参照する設定ファイルの descriptors は、上記のように定義します。この例ではEnvoyFilterで定義した descriptor_value (設定ファイル内では、value)を利用し、1秒間に10回、1分間に100回という制限を設定しています。 このようにすることで特定のマイクロサービスからZOZOTOWNへのリクエストに対して1秒間に10回、1分間に100回という制限を設けることができます。 事例2. オンプレミス環境のリクエスト制限 次に、オンプレミス環境の注文処理に対してRate Limitを導入した事例をご紹介します。 2023年6月現在、注文処理はオンプレミス環境上のレガシーシステム(Classic ASP)で稼働していますが、高負荷時のボトルネックとして顕著になってきたため、リプレイスを待たずに流量制限の仕組みを導入することになりました。 構成 この事例の特徴は、Istio Rate Limitを使って、サービスメッシュ外のオンプレミス環境で稼働するアプリケーションの処理を制御している点です。 オンプレミス環境の注文処理アプリケーションにリクエストがあると、Rate Limit用エンドポイントにリクエストを送ります。 そのエンドポイントに対してRate Limitが設定されており、閾値を超えた場合はHTTPステータス429を返し、閾値を超えない場合はDummy Web ServerがHTTPステータス200を返します。 注文処理アプリケーションはそのHTTPステータスに従い、429の場合はカート画面にリダイレクトします。それ以外の場合は後続処理へ進み、SQLServerに接続します。 Rate Limit用エンドポイントであるDummy Web Serverは、サービスメッシュ内の空のAPIであり、常にHTTPステータス200を返します。 設定と計測のポイント 1. 商品別の注文リクエスト数制限 ZOZOTOWNの注文確定時の処理に対して、2段階の制限をかけています。 注文処理の全リクエスト数に対する制限 商品ID単位のリクエスト数に対する制限 同一商品へのリクエストが集中することで、データベースの同一ページに対するラッチ競合が発生します。 これを防ぐために商品ID単位での制限値も設ける必要がありました。 仕組みとしては、クライアント側でリクエストヘッダーに商品ID(以下goods-id)を付与し、Istio Rate Limitでヘッダーの値ごとにカウントすることで実現しました。基本的な設定内容は事例1と同じですが、 request_headers でヘッダー名を指定することで、ヘッダーの値ごとにカウントできます。 apiVersion : networking.istio.io/v1alpha3 kind : EnvoyFilter metadata : name : zozotown spec : configPatches : ... - applyTo : HTTP_ROUTE match : context : ANY routeConfiguration : vhost : name : <FQDN>:80 route : name : zozotown patch : operation : MERGE value : route : rate_limits : - actions : - generic_key : descriptor_value : zozotown-order-count-by-goodsid - request_headers : header_name : goods-id descriptor_key : goods-id 2. 閾値決定のための負荷試験 以下のように設定することで、全体数の閾値とgoods-idごとの閾値を設定できました。 descriptors : - key : generic_key value : zozotown-order-count rate_limit : unit : second requests_per_unit : 200 - key : generic_key value : zozotown-order-count-by-goodsid descriptors : - key : goods-id rate_limit : unit : second requests_per_unit : 100 これらの閾値を決めるために負荷試験を実施しました。 まずは単体負荷試験として、Rate Limit Podの処理性能を確認しました。Rate Limit Podは今後複数の機能で共通利用していく想定のため、キャパシティ計画を立てやすいように1Podあたりが処理できるリクエスト数を明確にしました。 次に、Rate Limitが発動しないように閾値を非常に大きくした状態で、注文処理の結合負荷試験を実施しました。 徐々に注文処理のリクエスト数を増やし、データベースでページラッチが発生してパフォーマンス影響が出たところを限界値とし、それよりも小さい閾値でRate Limitの閾値を設定しました。 余談ですが、Rate Limitを発動させたくない場合は、 shadow_mode を有効化することで透過モードとなり、Rate Limitの閾値を超えても常にOKと判定されるようになります。 パフォーマンスについても、Rate Limit Pod単体でのレイテンシは5ms程度で、上記の構成によるオンプレミス環境側へのレスポンスも20ms~30ms程度と短く、注文機能として許容できるものでした。 今後の展望 リプレイスが進む中でシステムのキャパシティは変わっていくため、今後もRate Limitの閾値を調整していくことが考えられます。 現時点で基本的な監視は導入済みで、各Podの負荷状況、Rate Limit用エンドポイントへの総リクエスト数、Limitを超過したリクエスト数を監視しています。 課題として、ヘッダーのgoods-id別でカウント数を可視化する必要があり、監視の改善を進めています。 envoy proxyのドキュメントによると、 detailed_metric を有効化することで、今回のようにヘッダー値を指定しない場合でもヘッダー値をmetricsに含めることができるそうです。 最終的には他のIstioのメトリクスと同じようにDatadogで可視化できないかを検証中です。 また、現在カート投入のリクエストに対しても同じ対応を進めており、Istio Rate Limitの機能を横展開する予定です。 感想 EnvoyFilterの利用が初めてで普段読み慣れているIstio公式ドキュメントの EnvoyFilter には詳細な情報があまりなかったため、 Envoy のドキュメントを読みながら試行錯誤するのが大変でとても苦労しました。 Istio Rate Limitは柔軟で詳細な設定ができ、様々なケースに対応できそうだと感じました。また、パフォーマンスや安定性は非常に高く、満足のいく構成となりました。 最後に 現在、私たちと共にサービスを支える方を募集しています。少しでもご興味のある方は、以下のリンクからぜひご応募ください。 hrmos.co
こんにちは、WEARバックエンドエンジニアの 三浦 です。WEARのバックエンドの開発、保守運用に携わっています。個人ではおよそ2年ぶりのテックブログ執筆となります。 さて、今回はWEAR上のコンテンツを運用チームが自由にカスタマイズできるようモジュール化した話をご紹介します。 目次 目次 モジュール概要 導入背景 モジュールの設計 要件と課題 リプレイスとの競合 負荷対策 管理ツール API まとめ さいごに モジュール概要 まず最初にWEARのモジュール化はどのようなものか説明します。 今回導入したモジュール化は、WEARのアプリ上の一部コンテンツをUIモジュールとして扱えるようにすることです。1つ目の画像に表示されている シアーシャツ や 雨の日コーデ といったテーマ別のコンテンツ1つ1つがモジュールとなっており、テーマに合わせて絞られたコーディネート画像が表示されます。モジュール右上の すべてを見る を押下するとそのテーマに沿ったコーディネートの一覧画面へ遷移します。それが2つ目の画像になります。モジュールは運用チームが管理ツールから自由に作成できます。 導入背景 以前ビジネスサイドから、WEARのTOPページのコンテンツを定期的に変更してユーザーに様々な切り口でコーディネートを提案し、効果検証にも活用したいという要望があがりました。しかし、当時は新しいコンテンツを導入する際には、毎回デザイン検討、開発、QAチームによるテストと一連のプロセスを踏む必要があり、かなりの工数がかかりました。そのため、気軽に変更することが難しい状況でした。 そこでTOPページの一部のコンテンツをモジュール化し、大規模な開発をせず自由にコンテンツを変更できる環境を整えようということで今回の施策が動き始めました。 モジュールの設計 要件と課題 ビジネスサイドからの要件は以下のとおりです。この要件をベースに設計を考えていきます。 初回リリース時点ではコーディネートに関するモジュールを作成できること 将来的に動画やフリマ出品アイテムといったコーディネート以外のコンテンツもモジュール化できる汎用的な作りになっていること モジュールは複数表示でき、個別に掲載期間も指定できること モジュールの作成自体は管理ツールから運用チームが自由に作成できること 要件を満たすための設計を検討していく上でいくつかの課題も浮かび上がってきました。 リプレイスとの競合 WEARでは別チームが同時並行でリプレイスを行っており、コーディネートの検索基盤も旧版(Amazon CloudSearch)と新版(Elasticsearch)が同時稼働している状態です。モジュールの すべてを見る のリンクから遷移するコーディネート一覧画面は、開発工数を抑えるため既存の画面をそのまま使用します。こちらは旧版の検索基盤で検索が行われています。一方、TOP画面のモジュールで表示されるコーディネートには、ユーザーが最初に目にする部分であることから、柔軟にロジックを調節できる新版の検索基盤を使用します。 リプレイス前後の検索基盤でインデクシングされている項目に差異があり、リプレイス後の方が検索の絞り込み条件が増えています。ただし、TOP画面と一覧画面で検索結果が変わってしまっては困るので、その差異を考慮しながら検索条件を組み立てる必要があります。 負荷対策 モジュールはTOP画面で表示され、かつモジュールの数に比例してAPIのリクエスト数も増えるので、検索基盤に大きな負荷を掛けてしまう恐れがあります。 これらの要件や課題を踏まえて管理ツール、APIそれぞれの設計をしていきます。 管理ツール 運用チームは、まず管理ツール上のコーディネート検索画面からモジュールに使用したい条件で検索し、検索結果を確認します。ここでの検索には新版の検索基盤を使用しているため、TOP画面のモジュールで表示されるコーディネートになります。検索条件に使用できる条件は、リプレイス前後のパラメータの差分を確認して旧版の検索基盤でも検索可能な条件のみに限定しました。問題なければ同画面に用意しているモジュール作成ボタンからモジュール作成へ進みます。 以下は、上記のフローをシーケンス図にしたものです。 モジュール作成画面では主に以下の値を設定できます。 タイトル 掲載開始日時 掲載終了日時 表示優先度:複数のモジュールを表示した際の優先順位 コンテンツ種別:コーディネート、動画、etc... 検索パラメータ:モジュールに表示するデータの検索条件 ディープリンク:絞り込み条件付きの一覧画面のアプリディープリンク 検索パラメータとディープリンクに関しては、シーケンス図の 1.コーディネート検索画面:検索 で指定した条件を基にバックエンド側で登録します。画面で指定した条件と各検索基盤で使用するキー名のマッピングをモデル内で保持し、検索パラメータとディープリンクを組み立てていきます。ディープリンクのパラメータに関する資料があまり残っておらず、どのようなキー名が使われているかをアプリ上で実際に試しながら実装したため時間がかかりました。マッピングの実装部分は若干複雑になってしまいましたが、運用チームがリプレイスの環境差分やパラメータ名などシステムの仕様を意識せず、直感的にGUI上で登録できるようになりました。 また、同時に掲載できるモジュールの数には制限を設けることにしました。無制限に作成するとシステム負荷が上がるため、運用チームから作成したいモジュール数をヒアリングし、最大4つに制限しました。 API モジュールの一覧APIと詳細APIの2つに分けて作成しました。一覧APIで現在日時が掲載期間内のモジュールのIDを全て取得し、詳細APIではそれぞれのモジュールの内容を1件ずつ取得します。 以下は、上記のフローをシーケンス図にしたものです。 モジュールの内容は全ユーザーに対して同じであるため、負荷対策として詳細APIの内容を一定時間キャッシュする仕組みを導入しました。具体的にはRailsの低レベルキャッシュを使用して、 #{controller_path}/#{モジュールのID} をキーにレスポンスのJSONをキャッシュします。キャッシュ有効期間は、検索基盤のインデクシングが更新されるタイミングに合わせて設定しました。同じモジュールの内容を再利用することで、頻繁な検索基盤へのアクセスを回避し、検索基盤への負荷を軽減しました。 coordinate_module = CoordinateModule .find(params[ :id ]) coordinate_module_json = Rails .cache.fetch( "#{ controller_path } / #{ params[ :id ] }" , expires_in : xx.minutes) do render_to_string json : CoordinateModuleSerializer .serialize( coordinate_module : coordinate_module, coordinates : coordinates_searched_by(coordinate_module)) end render json : coordinate_module_json 詳細APIのレスポンスは、 管理ツールで設定したモジュールの内容 + モジュールと一緒に表示するデータの配列 のJSONになっています。配列部分はコンテンツ種別で設定した内容によってコーディネートの配列や動画の配列といったように変わってきます。クライアント側はコンテンツ種別を見てどんなデータが返ってくるかを判別します。 これによりコンテンツによって大きくレスポンス形式を変えることなく汎用的に利用できるようになりました。 まとめ モジュール化を行なったことで、エンジニアを介さず運用チーム側で定期的にTOP画面のコンテンツを更新できるようになりました。また、システム面で汎用的に作成したので他のページでもこの機能を応用できました。現在はフリマページのおすすめの出品アイテムも一部モジュール化されています。 モジュールを複数個出し始めてから、ユーザーのコーディネートの一覧ページへの回遊率が上昇しました。年齢別やTPOに合わせたテーマが人気のようです。 今後は、検証をした結果を活かして、コンテンツの精度の向上やパーソナライズなどもしていきたいです。 さいごに WEARでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
2022年6月に、Androidテックリードになった いわたん です。最近、某モンスターを育てたり図鑑を埋めたりするゲームで社内大会をやったらフルボッコにされて涙目でした。悔しくて最近は不思議な力でクラフトしたり空飛んだりして王国を救うゲームやってます。 今回はAndroidテックリードとして1年間やってみた施策の紹介と、それぞれの成果や反省点を紹介したいと思います。これからテックリードになろうとしている方やテックリードをしている方の参考になったり、こんな施策もいいよというアドバイスをもらえたら幸いです。 ZOZOのテックリードの役割と責任 実施した施策 テックリード1on1 読書会 歴史的経緯があるアプリのアーキテクチャ整理へのアプローチ ネーミングセンスを鍛える会の取り組み 案件への関わり方 横断的なコードレビュー 横断的に使う機能の実装 まとめ 最後に ZOZOのテックリードの役割と責任 Qiita で弊社VPoEの瀬尾が紹介しているように会社としてのテックリードの役割定義があります。 Qiitaより抜粋したテックリードの役割です。 チームの技術的な方向性、設計や開発手法、実装や品質などプロダクトの技術面での定義に対して責任を持つ 全社横断で、技術施策の策定と普及、スキルアップ支援、採用活動への協力など技術面での貢献を行う 具体的にチームに対してどういった活動をするかはテックリードに任されている状態でした。 そこで、 エンジニアのためのマネジメントキャリアパス という書籍を参考に、活動内容を考えました。 また、ZOZOの組織体制として、チームのマネジメントを行うブロック長がいます。ZOZOTOWN Androidチームでのブロック長の活動は 過去の記事 があるので、そちらも興味があれば読んでいただければ幸いです。 チーム内にテックリードとブロック長と似ているような似ていないような役職が存在することで、役割が重なりチームの二重管理とならないよう、役割の棲み分けを行いました。 メンバーのサポート テックリードは個人、チームの技術面の成長をサポートする ブロック長は、 企業理念 に上げている ZOZOらしさ の成長をサポートする プロダクト開発 テックリードは技術的負債の返還や新たに発生させないよう責務を負う ブロック長はアサイン管理を実施する テックリードが目指すべき具体的なタスクの方向性が見えてきたので、次のような施策を実施しました。 実施した施策 チームとして成長を続けるため、さまざまな施策を試行しました。具体的にはテックリード1on1、読書会、アーキテクチャ座談会、ネーミングセンスを鍛える会など、様々な形の活動を通じて、チーム全体の技術力の向上を目指しました。 これら以外にも試した施策は多数ありますが、本記事では記憶に残っていたり、特に効果的だったものをいくつかピックアップし、それらの施策について詳しく紹介します。それぞれの施策は、テックリードとしてチームの成長を支援するために重要だと考えています。またチームとして、常に新たな取り組みに挑戦することは大切だと考えており、新たな試みから学んだことを積極的にフィードバックに活かすことで、より良いチーム作りを目指しています。 テックリード1on1 前述の『エンジニアのためのマネジメントキャリアパス』にも書かれていたのですが、メンバー個別の成長をサポートするためにテックリード1on1を実施しました。テックリード1on1は、個々のメンバーと直接対話し、具体的な技術的課題や目標に対するサポートを提供するための時間として設定しました。ZOZOでは定期的に上長との1on1を実施していますが、その場とは異なり、より技術的な内容に焦点を当てる時間としました。 テックリード1on1の主な目的は次のとおりです。 技術的な要素が絡む目標で手伝えることは何かを明確にする。 伸ばしたい技術が何か、個々のメンバーと相談する場を提供する。 チーム内で技術知識を横展開し、共有する。 出しているPull Requestのレビューや、一緒にタスク整理をする。 テックリード1on1は最低月1回開催し、半期ごとの目標設定で設定した技術的な目標に対して手伝えることがないかを確認します。また、評価時には、これまでのテックリード1on1で話し合った内容をもとに、個々のメンバーが自己アピールに使えるポイントを明示しました。 ZOZOTOWN Androidのメンバーは10人以上おり、テックリード1on1は全体で毎週半日ほどの時間をかけるようにしました。テックリードになってからの負担としては一番大きい施策ですが、テックリード1on1の取り組みにより、メンバーそれぞれから様々な課題感を聞くことができました。 出てきた課題感として、次のような技術的な課題が出ました。 コードメトリクスの計測 を行いコードの品質を測定したい Android Vitalsを活用 してアプリのパフォーマンスを計測したい 積読を消化したい プログラミングに関する知識をもう一度きちんと勉強したい コードメトリクスの計測やAndroid Vitalsの活用など、メンバーが抱えている課題に対応する時間を、テックリード1on1や個別の対応時間で取り組むことができました。それにより、メンバーが得た知識や解決経験をテックブログへの投稿やDroidKaigiでの登壇に繋げることが出来ました。積読の消化やプログラミング知識に関する相談は次に紹介をする、読書会を開催するようにしていきました。 読書会 私たちのチームは多様なバックグラウンドを持つメンバーで構成されています。未経験からスタートしたエンジニア、他業種から転職してきたエンジニア、そして経験豊富なエンジニアと、そのレベルと経験は様々です。その一方で、リモート環境下では自然と得られるメンバー間での学びの機会が減少し、特に未経験者や他業種から来たエンジニアは基礎的な知識や技術を学ぶのに苦労していました。 ZOZOでは 書籍購入支援 の制度があり電子書籍での購入も認められています。制度を利用し皆で本を購入し読書会を行うことにしました。 読書会で取り上げる書籍は、参加者全員が理解を深められるような基礎的な内容から、より高度で技術的な話題まで幅広く選んでいます。この読書会の目的は、メンバー全員の技術力向上と知識の共有です。未経験者や他業種からの転職者は基礎を固め、経験者は新たな視点を得ることができます。また、それぞれの学びを共有することで、チーム全体としての知識も向上します。 これまでに、『Clean Architecture』や『読みやすいコードのガイドライン』、そしてUML関連の本を取り上げてきました。特に『Clean Architecture』は、アーキテクチャに興味のあるメンバーには好評でしたが、SOLIDの原則以降は難易度が高く、チーム全員が内容を把握するにはハードルが高いと感じました。 一方で、『読みやすいコードのガイドライン』はコスパがよく、コードの品質改善に大きく貢献しました。読書会後にPull Requestを見ていると、メンバーがコードにコメントを書く量が全体的に増えたことからもその影響が見て取れました。 技術的な議論においても明確な根拠を示せるようになりました。具体的には、Pull Requestのレビューの際に、『読みやすいコードのガイドライン』や『Clean Architecture』など、読書会で取り上げた本から出典を示すことが容易になりました。このことにより、指摘の際の説明負荷が軽減し、コミュニケーションがより円滑になりました。 歴史的経緯があるアプリのアーキテクチャ整理へのアプローチ ZOZOTOWNのAndroidアプリは、リリースが2012年5月、現在のコードベースの初回コミットが2015年2月2日と、かなりの歴史があります。長い間に渡って開発が続けられてきた結果、異なるアーキテクチャの思想や実装上の思想が混ざり合い、保守コストが高まり、現在採用しているアーキテクチャや実装方針が見えにくくなるという課題が生じました。 ZOZOTOWN Androidチームはメンバーも多く、同時並行で稼働している案件もある程度の数があります。その為、自分一人でアーキテクチャの選定、メンバーへの普及や教育等を実施するのは難しいと考えました。そこで、将来テックリードになる可能性のある候補者と現テックリードでアーキテクチャについて議論を深めるアーキテクチャ座談会を週に1時間開催しています。 座談会の結果、新しいアーキテクチャとしてGoogleの Modern Android App Architecture (以下、MAD)に従うことを決めました。さらに、その中で、ZOZOTOWNのAndroidアプリがMADに対して持つ独自の部分は何かという問いについて明確化できました。 具体的には、MADではRepository間の依存を認めていますが、ZOZOTOWN Androidではこれを認めずUseCaseを使用すると決めました。他にも決めたことに関してはガイドライン化してドキュメントとしてメンバーが確認出来るようにしました。 また、アーキテクチャ座談会でメンバーを増やしたことで、新しいアーキテクチャの導入に伴う問題点を早期に把握できました。座談会の参加者がそれぞれの担当案件ごとに新しく決めたアーキテクチャを採用することで、その問題点や運用上の問題が明確になりました。 具体的な問題点としては、新旧の実装方針が混在してしまうケースや、同様の責務を持っているが別の名前を付けられているような命名規則の問題が発生しました。原因分析もアーキテクチャ座談会の中で実施できました。結果として原因は、新しい実装方針がチームに十分に浸透しておらず、「なぜ新しい方針に変えるのか」という理由が共有されていなかったと結論づけました。 この記事の執筆時点ではまだ実施が出来ていない状況なのですが、対策として各案件にアーキテクチャの責任者を割り当て、設計やレビューを通じて案件メンバーにアーキテクチャを浸透させる計画をしています。 ネーミングセンスを鍛える会の取り組み ネーミングは必ずしも1つの正解があるわけではなく、各メンバーの感性や背景、経験が反映されます。そのため、同じチーム内でもネーミングやインタフェース定義にはばらつきが生じてしまうことがありました。 この課題を解決するために、「ネーミングセンスを鍛える会」を実施してみました。この会の運営は、AIベースの言語モデルであるChatGPTを用いて行いました。具体的な手順は次の通りです。 ChatGPTを用いて様々なお題を作成します。具体的なプロンプト例は後述します。 そのお題をSlackに投稿し、チームメンバー全員に共有します。 メンバーはそれぞれの解答をSlackに投稿します。 投稿された解答に対して、ChatGPTを用いて添削をします。 実際のSlackでのやり取りはこんな感じでした。 この活動は初めての試みでしたが、参加者からは「面白い」という声が上がり、また「ハードルが低くて参加しやすい」との評価も得ることができました。しかし、この活動はSlack上で自由に参加できる形式を取っていたため、積極的に回答を投稿するメンバーと、そうでないメンバーとの間に差が生じてしまいました。その結果、活動はいつしか自然消滅してしまいました。 実際に使用したプロンプトは下記のとおりです。 制約事項を元に関数の仕様を決め、説明文を生成し出力の書式に沿って生成してください # 制約事項 - 関数は1件とする - 出力は要約と説明文のみにする - 説明文は処理内容の重要な点のみを記述する - 説明文は箇条書きで出力すること - 関数名は出力しない - 説明文はですます調とする - 関数は循環複雑度が3〜5程度の内容とする # 出力 ## 要約 {機能の要約を出力する} ## 説明文 {箇条書きで機能の説明文を出力する} ## 使用例 入力:{ここに関数への入力値が入る} 出力:{ここに関数の出力値が入る} 案件への関わり方 『エンジニアのためのマネジメントキャリアパス』ではテックリードの定義は次のように紹介されていました。 (ソフトウェアの)開発チームに対する責任を担い、最低でも自身の職務時間の3割はチームと共にコードを書く作業に充てているリーダーのこと。 ZOZOTOWN Androidは複数案件が同時並行で走り、かつメンバーも全体で10人以上の規模です。チームとともにコードを書くためには自分は案件に専任で入るのではなくチーム全体に横断的な関わり方をするようにしました。具体的には次のような関わり方をしました。 横断的なコードレビュー 横断的に使う機能の実装 横断的なコードレビュー ZOZOTOWN Androidチームでは、各メンバーがアサインされた案件内でPull Requestを相互でレビューするという体制をとっています。私自身はテックリードになってからは特定の案件にはアサインされない体制にし、時間が許す限りほぼ全てのPull Requestを確認していました。しかし、成長するチームとともにPull Requestの量も増え、全てを見るのが難しくなってきました。 そこで、私の役割を再定義し、重要案件の立ち上がり時のPull Requestや自分の興味があるPull Requestなどで優先順位を付けて確認するように変更しました。これにより、効率的に重要な箇所を見ることができるようになりました。 また、過去のアーキテクチャでの実装箇所やリファクタリング対象で今後は新規実装しない方針のモジュールに変更があった場合、その確認を促すようなBotを作成し導入しました。具体的には、Pull RequestのDiffに該当するファイルがあった際にBotが自分にメンションを投げ、該当のPull Requestを確認するように促します。これにより、重要なモジュールの変更確認が漏れることなく、品質を確保できています。 1年間のPull RequestをGitHubの活動を見える化する Findy Team+ で確認してみたところ、ほぼ全員のPull Requestを確認していました。 横断的に使う機能の実装 横断的な機能の開発と整備を実施しました。具体的には、Loggerの統一、Feature Flagの導入、そしてGitHub Actionsの整備などを行いました。 テックリードが横断的な機能開発に関与する理由は、各案件で横断的に使う機能を実装すると、その実装が特定の用途に過度に特化してしまう可能性があると考えたからです。この特化は、その機能の汎用性が低下する可能性を生み出します。そのため、テックリードがチーム全体を見渡して汎用性の高い実装をすることで、チーム全体の効率と品質を向上させることを目指しました。 今回は具体的な例としてLoggerの統一を紹介します。 ZOZOTOWN Androidではデータを集めたい複数の部署や開発の効率化のためなどの理由で複数のLoggerが使われています。そのため、ログを仕込む箇所での実装も煩雑になっていました。この問題を解決するために、用途ごとに違う色々なLoggerを統一的に使用できる新しいLoggerを設計、実装しました。 結果として次のような実装をしました。 // Logのパラメータ interface LogEvent { // Log送信を行なった画面の情報 val screenName: String ? // interfaceのデフォルト実装を利用することで未実装時はnullを返し、nullを返した場合はZozoLoggerは対応するLoggerに対して送信は行わない val parameterA: ParameterA? // LoggerA用のパラメータ get () = null val parameterB: ParameterB? // LoggerB用のパラメータ get () = null val parameterC: ParameterC? // LoggerC用のパラメータ get () = null } // 統一的に使えるLogger object ZozoLogger { // 各種ロガー private var loggerA: LoggerA? = null private var loggerB: LoggerB? = null private var loggerC: LoggerC? = null // App.kt等でロガーをBindする fun bind( loggerA: LoggerA, loggerB: LoggerB, loggerC: LoggerC, ) { this .loggerA = loggerA this .loggerB = loggerB this .loggerC = loggerC } // ログの送信処理 @JvmStatic fun log(event: LogEvent) { event.parameterA?.let { param -> loggerA.send(param) } event.parameterB?.let { param -> loggerB.push(param) } event.parameterC?.let { param -> loggerC.post(param) } Log.d(event.screenName, event.toString()) } } ZozoLoggerはSingletonにしてHiltでDIする方法も考えました。しかし、ログ送信箇所が古い実装であったりDIが難しい場合に、使えない可能性を考慮しobjectで実装することを選択しました。 次のようにLogEventインタフェースの実装で各Logger用のパラメータを生成することで、各種Logger毎にパラメータを用意して別々にLogを送信する必要性をなくしました。 data class MyLogEvent( override val screenName: String , private val data1: Data, private val data2: Data, ) : LogEvent { // 送信を行いたいLogger用のパラメータだけを実装する override fun parameterA() = ParameterA(data1.value) override fun parameterB() = ParameterB(data1.value, data2.value) // parameterCについては実装していないのでLoggerCには送信されない } 実際にログを送信する際の実装は以下のような形になります。 class MyViewModel() : ViewModel() { fun logic() { // 何か処理をする // ログの送信処理 ZozoLogger.log( MyLogEvent( screenName = "my_screen" , data1 = Data1(...), data2 = Data2(...), ) ) } } このような実装により、新たなLoggerの追加や削除、さらには1つのLoggerで送られていなかったイベントを別のLoggerで送るといった要件変更が容易になります。この変更は送信処理の修正を必要とせず、各LogEventの修正だけで対応可能となり変更コストを下げることが出来ました。 まとめ 私たちのチームでは、チームの成長を実現するために、様々な改善活動を積極的に試行しています。私たちは皆で提案を歓迎し、改善を推進する文化を育ててきました。 その結果、私たちは常にチームの改善に向けて努力を続けることができています。しかし、まだ改善できる点はたくさんあり、私たちは常に更なる成長を目指しています。 また、私たちのチームでは、マネジメントラインとは別にテックリードが存在しています。これにより、テックリードはチームへの技術的なサポートに専念でき、チームの技術力や生産性の向上に集中できています。 一方で、会社としてテックリードに期待されている全社横断の活動については、まだ十分に取り組めていないと感じています。今後は、チーム内で培った知識や経験を、会社全体で共有し、全社の成長に寄与することを目指していきます。 以上の取り組みを通じて、私たちはチームとしての成長を促進し、より良いアプリを提供することを目指しています。これからも私たちは、新たな試みを恐れず、チーム全体での技術力の向上に向けて邁進していきます。 最後に ZOZOではAndroidエンジニアを募集しています。ご興味のある方は下記リンクからぜひご応募ください。 hrmos.co
はじめに こんにちは、ZOZOTOWN開発本部リユースシステムブロックの西山です。最近の癒やしは飼い猫のお腹に吸いつくことです。普段は買い替え割サービスにおけるバックエンドの開発や運用保守を担当しています。 買い替え割サービスのデータベースはRDS for MySQL 5.7を利用していますが、2023年10月にサポートが終了するため次期バージョンへのアップデートが不可欠となっておりました。 また、サービスの成長に伴い、運用効率、可用性、耐障害性をさらに向上していくために、データベースそのものを見直す必要もありました。 この両面の課題に対応するため、RDS for MySQLからAurora MySQLへの移行をすることになりました。 本記事ではRDS for MySQL 5.7からAurora MySQL v3へ移行時と運用をしてみて気づいたことを紹介していきます。 はじめに 買い替え割サービスとは 抱えている課題 Auroraの選定理由 RDSとAuroraの違い Aurora独自の機能 課題解決 移行方法の検討 移行前後のシステム構成図 移行方法 移行手順 移行した際の気づき 移行による確認と変更 ケース1:Aurora v2を作成時デフォルトパラメータグループが存在しないエラーになる ケース2:Aurora v3の作成で失敗する(その1) ケース3:Aurora v3の作成で失敗する(その2) ケース4:別のAWSアカウントで作業時間が想定よりも長くかかった 運用した際の気づき ケース1:一時領域の不足エラー ケース2:CFnとAWS管理コンソールでAuroraの設定変更による再起動の挙動の違い ケース3:プラグインに関するエラーメッセージ ケース4:断続的に発生するコネクション関連のエラーログ ケース5:ブルーグリーンデプロイ まとめ 最後に 買い替え割サービスとは 買い替え割サービスはZOZOTOWNが提供する「買い替え割」「いつでも買い替え割」サービスの総称です。 「買い替え割」はZOZOTOWNで購入したアイテムを下取りすることで、欲しいアイテムを割引価格で購入できるサービスです。 また、「いつでも買い替え割」はZOZOTOWNで購入したアイテムを好きなタイミングでZOZOポイントに交換できるサービスです。 図1:買い替え割サービス 抱えている課題 冒頭で軽く触れましたが現状抱えている課題を再度整理しました。 RDS for MySQL 5.7.39のサポート期限が2023年10月に迫っている。 サービス成長に伴いユーザーアクセスが増えている中で、メンテナンス作業時間の短縮を求められている。 DBの肥大化により、インデックス設定などのテーブル更新作業で1時間以上かかることもありメンテナンス作業が長時間化している。 Auroraの選定理由 Auroraを選定した理由について説明します。RDSとAuroraの違い、Aurora独自の機能を調査し、課題に対してどのように対応できるかを検討しました。 RDSとAuroraの違い RDS for MySQL Aurora (MySQL) SLA 99.0%以上、99.95%未満 99.0%以上、99.99%未満 処理性能 ※1 100,000 SELECT/秒 500,000 SELECT/秒 Failover完了までの時間 60~120 秒 30 秒以内 Failover時のDB接続先エンドポイント変更 あり なし ストレージ EBS (Elastic Block Store) 仮想クラスターボリューム 最大ストレージ容量 16TiB 128TiB ストレージ領域の自動拡張 なし あり (※1)参考: 「MySQL の 5 倍のパフォーマンス」とはどんな意味ですか? 特にRDSとAuroraではストレージが大きく異なります。RDSではEBSを利用しているのに対し、Auroraでは仮想Clusterボリュームを利用しています。 そのため、RDSでストレージを拡張する場合は手動で行う必要があり、EBSを1台のインスタンス毎にアタッチするためすべてのEBSに対してインスタンスタイプの変更が必要となります。 Auroraでは仮想クラスターボリュームで3つのAZにまたがって6つのストレージノードにレプリケートされ、高可用性を実現しながら自動で拡張するためストレージ領域の管理が不要となります。 図2:RDSとAuroraのストレージの違い 参考: Is Amazon RDS for PostgreSQL or Amazon Aurora PostgreSQL a better choice for me? 参考: Introducing the Aurora Storage Engine Aurora独自の機能 Aurora独自の機能も見ていきます。 ZDP(ゼロダウンタイムパッチ)を利用することで、クライアントの接続を維持しながら5秒程度の遅延でパッチ適用が可能になる。 ストレージサイズを最大128TiBまで自動で拡張するため、ストレージサイズの管理が不要になる。 Auroraのフェイルオーバーではエンドポイントの書き換えが無いため、アプリケーション側のエンドポイントの書き換えが不要となりダウンタイムを短縮できる。 課題解決 現状抱えている課題がどのように解決出来るか見ていきます。 RDS for MySQL 5.7.39のサポート期限が2023年10月に迫っている。 次期8.0系のv3にバージョンアップすることで、サポート期限の延長及びv2からv3への移行の手間がなくなります。 v2は5.7系と互換性がありますが、今後メジャーバージョンのサポート切れになる可能性を考慮し、よりライフサイクルの長いv3が良いと判断しました。 サービス成長に伴いユーザーアクセスが増えている中で、メンテナンス作業時間の短縮を求められている。 Auroraマイナーバージョンはリリースから少なくとも12か月間利用が可能となっており、メンテナンス頻度の削減につながります。 RDSはベンダーによるコミュニティリリースに合わせてマイナーバージョンがリリースされます。2022年のマイナーバージョンアップは4,5回行われていました。 最新バージョンを利用するには、ベンダーのリリースに合わせてマイナーバージョンをアップデートする必要があります。また緊急性の高いセキュリティパッチは期限を過ぎると、強制的に再起動して適用されるため注意が必要です。 参考: Amazon RDS での MySQL のバージョン DBの肥大化により、インデックス設定などのテーブル更新作業で1時間以上かかることもありメンテナンス作業が長時間化している。 ブルーグリーンデプロイを利用することで、運用しながらテーブル更新作業を事前に行えるためメンテナンス時間が短縮可能になります。 前述の内容を踏まえてよりクラウドネイティブに設計されメリットが多く、現状抱えている課題をまとめて解決が出来るAurora v3への移行を決断しました。 移行方法の検討 移行前後のシステム構成図 移行前のRDSと移行後のAuroraの簡略化したシステム構成です。移行前からMultiAZ構成になっていますが、移行後では更に別のAZにリードレプリカを1台増やして可用性を高めています。 図3:移行前後の構成図 移行方法 今回で検討した移行方法は大きく分けて3つありました。RDSからAurora v3へ直接アップデートは出来ないため、RDS→Aurora v2→Aurora v3の順でアップデートしました。 図4:移行方法の比較 移行方法 総評 データの移行速度 作業時間 データの整合性 クラウド環境への適正 1.スナップショットから昇格して移行する ◎ ◎ ◎ ◎ ◎ 2.リードレプリカから昇格して移行する ◯ ◎ △ ◎ ◎ 3.ダンプ+リストアして移行する △ × △ ◎ ◎ 今回はデータの移行速度が早く、作業手順もシンプルでAWSが提供している1のスナップショットから昇格して移行する方針で対応しました。 参考: DB スナップショットを使用した MySQL DB インスタンスから Amazon Aurora MySQL DB クラスターへのデータ移行 移行手順 移行当日に行った移行作業の流れについて説明します。また移行作業の際にハマった点や気づいた点は「 移行した際の気づき 」に後述します。 RDSのスナップショットを取得する 当日作業に問題が発生した場合すぐに切り戻しが出来るようにRDSは残しておきます。 スナップショットからAurora v2のクローンを作成する 作成したAurora v2は作業後不要になります。 Aurora v2のスナップショットを取得する CloudFormation(以後CFnとする)のスタック更新でAurora v2のスナップショットを元にAurora v3のクラスターを作成する チームの運用ルールとしてAWSのリソースをCFnで管理しています。AuroraもCFnで管理するため論理IDを新規に用意しました。 図5:移行作業の流れ 移行した際の気づき 移行による確認と変更 移行時に行った主な確認や変更点をまとめました。 MySQL 5.7からMySQL 8.0で認証プラグインのデフォルト変更による影響の確認 MySQL 5.7では認証プラグインのデフォルトがmysql_native_passwordでしたが、MySQL 8.0ではcaching_sha2_passwordとなります アプリケーション側はPHPを利用しておりDB接続に利用するPDOは現状caching_sha2_passwordに対応してません Aurora v3では認証プラグインのデフォルトがmysql_native_passwordになっているため、移行による影響はありませんでした パラメータグループの設定値の変更と確認 Aurora専用のパラメータ追加、一部パラメータのデフォルト値変更があるためシステムに合わせた設定の調整が必要です 文字コード、タイムゾーンはアプリケーション側と合っているか確認し必要に応じて設定しました ログ周りの設定も必要かどうかを見直しました general_logに関してはAuroraのパフォーマンスに影響をあたえるため本番稼働する環境では無効にすることを推奨していました 一時領域に関するパラメータはシステムに合わせた設定が必要となります 「 ケース1:一時領域の不足エラー 」で後述します 上記以外のパラメータについては、実際のワークロードで性能が発揮できるようにチューニングしているためほとんどのパラメータ設定はデフォルト値で問題ありませんでした 参考: Amazon Aurora MySQL データベース設定のベストプラクティス Auroraではオプショングループのパラメータ設定が変更できなくなる 移行前にパラメータグループの設定を確認し、移行後で設定変更が必要なパラメータはないか確認する必要がありました チームでは監査ログの取得にMariaDB Audit Pluginを利用していました Aurora v3では監査ログを取得する機能が備わっているため必要に応じて設定を変更する必要があります 参考: Amazon Aurora MySQL DB クラスターでのアドバンストな監査の使用 監視メトリクスが追加になる Aurora v3では監視メトリクスが追加になります デッドロックなどのメトリクスの追加があるため、アラート設定を見直しました AuroraはRDSと比較しCPUやメモリ使用率が上昇するため、アラート設定を確認する必要があります ケース1:Aurora v2を作成時デフォルトパラメータグループが存在しないエラーになる 事象 「図5:移行作業の流れ」のステップ2でAurora v2のクラスターを作成する際に以下のようなエラーが発生しました。 DBClusterParameterGroup not found: 'default.aurora-mysql' 原因 Auroraを構築したことがなくデフォルトパラメータグループも存在しないため発生していると考えられます。 原因に関して調査しましたが詳細は不明でした。 対応 対処法についても調査しましたがこれといった解決策は見つかりませんでした。 公式の方法ではないですが、スナップショット復元した後リードレプリカからAurora v3を作成することでデフォルトパラメータグループが作成された状態となります。 以降はスナップショットからAurora v2のクラスターを作成可能となりました。 ケース2:Aurora v3の作成で失敗する(その1) 事象 「図5:移行作業の流れ」のステップ4でAurora v3のクラスターを作成開始後、6時間経過しても作成が完了しない。 原因 テーブルのコメントで utf8mb3 非対応の文字列が含まれているため、Aurora v3のクラスター作成時にエラーが発生していました。 エラーが出なかったため、原因調査ではMySQLバージョンなのかAuroraによる挙動の違いが原因なのかを切り分けていきました。 RDSのみでMySQL 5.7から8.0へのアップデートを行ったところ特に問題は発生せず、Aurora v2からAurora v3へのアップデートを行ったところエラーが発生しました。 Auroraのerrorログには以下のようなエラーが出力されていました。 Comment for table 'データベース名.テーブル名' contains an invalid utf8mb3 character string: '16進数の文字列'. 対応 ALTER文でテーブルのコメントを再度設定することでエラーが発生しなくなり、Aurora v3のクラスター作成が完了しました。 ケース3:Aurora v3の作成で失敗する(その2) 事象 「図5:移行作業の流れ」のステップ4でCFnからAurora v3のクラスターを作成する際に以下のようなエラーが発生しました。 The following resource(s) failed to create: [AuroraCluster]. 原因 RDSとAuroraではCFnのプロパティが異なるため、エラーが出ていました。 対応 Auroraに対応したCFnのプロパティを設定することでAurora v3のクラスター作成が完了しました。 ケース4:別のAWSアカウントで作業時間が想定よりも長くかかった 事象 チームでは検証と本番でAWSのアカウントを分けて運用しています。 本番アカウントにて本番を想定したリハーサルの実施時、予定していた時間よりも長くかかってしまい当日の作業スケジュールに影響する可能性がありました。 移行当日にテーブルの更新作業も合わせて実施予定で、この更新作業が検証よりも1.4倍もの時間がかかっていました。 原因 考えられる原因としては、今回予定していたテーブルの更新作業は型の変換であるため、テーブルのフルスキャンが発生することです。 検証アカウントでは3か月前の本番相当データ量で実施しており、3か月分のデータ差分によって作業時間へ影響した可能性がありました。 対応 時間を短縮するためインスタンスタイプの一時的な変更や、パラメータの調整などを試みましたが、フルスキャンになる型の変更の場合は時間を短縮出来ないことが分かりました。 今回はRDSからAuroraへ移行がメインでテーブルの更新作業は別日に実施する判断となりました。 現状抱えている課題にもなるので、今後はブルーグリーンデプロイを活用することでメンテナンス時間の短縮を目指して行きたいと考えています。 運用した際の気づき ケース1:一時領域の不足エラー 事象 リーダーインスタンスに対してクエリを実行した際に以下のようなエラーが発生しました。 [ERROR] [MY-013132] [Server] The table '/rdsdbdata/tmp/#sql294_800_1' is full! (handler.cc:4388) 原因 リーダーインスタンスとライターインスタンスでメモリ枯渇の挙動が異なり、今回はリーダーインスタンスのメモリ枯渇でエラーとなりました。 下記、参考URLのAppendix(Aurora MySQL 3.0)フロー図を参照するとリーダーインスタンス/ライターインスタンスでエラーになるまでのフローが確認できます。 参考 : Use the TempTable storage engine on Amazon RDS for MySQL and Amazon Aurora MySQL 対応 クエリ実行後にTempTableの合計サイズを確認し、 temptable_max_ram か temptable_max_mmap の値を調整する必要があります。 今回は temptable_max_mmap をTempTableの合計サイズより大きい値に設定することで解決しました。memory-mapped fileは作りすぎたとしても実体はメモリではないのでOOMのリスクはなく、あるとすればストレージ枯渇が考えられます。 temptable_max_ram に関しては値を誤ると メモリを使い切りライターのプロセスが落ちる 可能性もあるため設定を慎重に行う必要があります。 ケース2:CFnとAWS管理コンソールでAuroraの設定変更による再起動の挙動の違い 事象 Auroraのリネームや動的パラメータ変更などAWS管理コンソールから変更した時、Auroraを再起動せずに変更した値の反映が可能です。 しかし、再起動が不要な変更をCFnからスタック更新すると再起動される事象を確認しました。 原因 原因について調査してみたところCFnによる仕様になっておりました。 参考: AWS::RDS::DBClusterParameterGroup 対応 CFnからスタック更新する際は、Auroraが再起動される可能性を想定し事前に確認する。 または、AWS管理コンソールから変更を行い後追いでスタック更新すると再起動なしで更新が可能になります。 パッチ適用で検証した際は再起動されずにスタック更新が終了することを確認しました。この時、スタック更新の挙動はパッチ適用済を確認してスタック更新を終了しました。 ケース3:プラグインに関するエラーメッセージ 事象 移行後にAuroraのerrorログで監査のプラグインに関するエラーログが出力されていました。 [ERROR] [MY-010901] [Server] Can't open shared library '/rdsdbbin/oscar-8.0.mysql_aurora.3.xx.0/lib/plugin/server_audit.so' (errno: 0 /rdsdbbin/oscar-8.0.mysql_aurora.3.xx.0/lib/plugin/server_audit.so: cannot open shared object file: No such file or di). 原因 こちらに関して調査しましたが原因に関する詳細は不明でした。現行の運用に影響が出ていないため、早急な対応は必要ないと判断しました。 対応 エラーとなっているプラグインを削除することで解消できそうだと考えましたがrootでは権限がないため実行できませんでした。 AWSで管理する権限でのみ操作が可能なため、今後のアップデートで解消することを期待しています。 ケース4:断続的に発生するコネクション関連のエラーログ 事象 Auroraのerrorログで以下のようなエラーログが断続的に出力されていました。 Aborted connection 1234 to db: 'unconnected' user: 'rdsadmin' host: 'localhost' (Got an error reading communication packets) 原因 こちらに関して調査しましたが原因に関する詳細は不明でした。 対応 運用に影響はないので、今後のアップデートで解消することを期待しています。 ケース5:ブルーグリーンデプロイ Auroraではブルーグリーンデプロイの機能を提供しています。 ブルーグリーンデプロイは、クラスターを2つ用意し、1つのクラスターで運用しながらもう1つのクラスターで新しいバージョンのAuroraを構築します。テストを行い問題がなければ切り替えるというものです。 オンラインDDLでロックを取得せず運用中にスキーマ変更する機能もありますが、型変換などロックが取得されるものに関しては、一度メンテナンスしてからスキーマ変更する必要があります。 またDBが肥大化しているため実行時間が1時間以上かかるテーブルもありメンテナンス時間が長時間化してしまいます。 そこで、ブルーグリーンデプロイを利用することでメンテナンス時間の短縮が期待できます。まだ検証段階で事例がないので今後ナレッジが蓄積してきたらまた記事にしたいと思います。 まとめ RDS for MySQL 5.7からAuroraへの移行に関する事例について、移行の流れや、移行作業、運用中のケーススタディを紹介しました。 検証やトライアンドエラーを繰り返し、リハーサルテストを重ね当日は無事に本番導入でき、リリース後も特に大きな問題なく稼働しています。今後も買い替え割サービスをはじめとしたリユースシステムのサービスをより良くしていくためにも、リプレイスや機能開発など進めていきたいと考えています。 最後に リユースブロックは「買い替え割」や「いつでも買い替え割」など、買取した中古商品を販売しているZOZOUSED事業に関するサービスの開発チームです。 現在はリプレイスプロジェクトが進んでおり、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
こんにちは。FAANSブロックiOSチームの加藤です。 日本時間の6月6日から10日にかけて WWDC23 が開催されました。 WWDC23では、空間コンピュータ「Apple Vision Pro」を始め、iOS 17、SwiftDataなどワクワクする発表が目白押しでした。 今年は去年と同様に、抽選に当選すれば現地で開催されるApple Parkのパブリックビューイングにも参加できました。ZOZOからは2名が当選して、現地に赴きました! 本記事では、WWDC23におけるZOZOのiOSアプリ開発メンバーの取り組みについてご紹介します。また、オフライン参加メンバーによる現地レポートや、ラボ、セッションに参加して得られた知識も可能な範囲で公開します。ぜひ最後までご覧ください。 WWDCについて 現地で楽しむWWDC23 6月4日 - イベント前日 6月5日 - イベント当日 パブリックビューイング Meet the Teams ZOZO×WWDC23オンライン Activities Labs & Sessions Swift open hours lab & Xcode open hours lab 今年のDemystifyセッション Apple Vision Proの発表に寄せて まとめ さいごに WWDCについて WWDC(Worldwide Developers Conference)は、Appleが年に1度開催している開発者向けのカンファレンスです。OSのアップデートをはじめ、開発環境周りの新機能が発表されます。今年は昨年に続いてハイブリッド開催であり、ZOZOの当選したメンバーは業務の一環として現地参加しました。また、オンラインで参加したメンバーは、セッションやラボ、アクティビティに参加しました。 現地で楽しむWWDC23 こんにちは、計測アプリ部の永井とフロントエンド部でWEARのiOSを担当している山田です。昨年に引き続き、今年もオフライン/オンライン同時開催となり、私たちは現地で参加してきました! 現地参加のスケジュールは以下の通りです。 日付 時間(Pacific Time) コンテンツ 場所 6月4日 3:00 PM Early Check-in Infinite Loop 6月5日 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 3:00 PM Meet the Teams Apple Park - Caffè Macs 5:30 PM Apple Design Awards Apple Park 6月4日 - イベント前日 まず、私たちはEarly Check-inを済ませるため、Infinite Loopへ向かいました。Early Check-inを行うことで、イベント前日にネームカードや参加記念品をもらうことができます。列に並んで待っていると、Appleのスタッフが「ダブダブ!」「ディーシー!」のコールで盛り上げ、イベント前日から既に気合が入っていました。 Early Check-inを済ませた後、参加記念品をいただきました。今年は、トートバック、帽子、水筒、ピンバッジでした。ピンバッジの種類は、Mac OSのレインボーカーソル、アップルのロゴ、絵文字など様々でした。絵文字は人によって異なっていました。 Infinite Loopは多くの人で賑わっており、参加者同士がドリンクや軽食を楽しみながら交流していました。広場の芝生には椅子とパラソルが配置され、くつろぎながらおしゃべりできる雰囲気が漂っていました。 会場では、DJが高いBPMの曲で盛り上げたり、ビッグスケールのジェンガやパズルゲームが行われるなど、非日常的な雰囲気が広がっていました。さらに、そこでAppleの関係者と写真を撮るなど、WWDCのムービーでしか見たことのないような楽しい時間を過ごすことができました。 イベント前日はここで終了です。さあ、明日はどんな日になるのか。 6月5日 - イベント当日 イベント当日。私たちは、チェックインが始まる時間より1時間も前にVisitor Centerへと向かいました。それは、Keynoteを最前列で視聴するため、そして歴史的瞬間を誰よりも前で目撃するためです。しかし、驚くべきことに、そんな私たちよりもさらに早くに来て、チェックインの待機列に並んでいる猛者が20名ほどいました。 待機列に並び、重いまぶたを擦りながら待っているとスタッフがコーヒーと朝食を配ってくれました。前日に引き続き、デベロッパーに対する素晴らしいホスピタリティに感動しました。 待機列に並ぶデベロッパーたちと今年はどんな発表がありそうかと話しているうちに、チェックインの時間となりました。 はやる気持ちを抑えながらチェックインを済ませ、道を進んでいくと、そこには象徴的なドーナツ型のオフィスがありました。近未来的なデザインのオフィスと、それを取り囲む大自然が、不思議と調和していて芸術的でした。 おっと、気を奪われてはいけません。そこからは脇目も振らず、パブリックビューイング会場を目指し、無事に最前列の席を確保できました。早起きしたかいがありました。 今年のパブリックビューイング会場には、巨大な屋根が建て付けられていました。去年の参加メンバーからは日差しが照りつける会場だったと聞いていましたが、今年は去年を踏まえた改善がしっかりとされていました。 パブリックビューイング 席を確保したのち、Caffè Macsで朝食を食べていると、あっという間に時間は過ぎて、Keynoteの時間となりました。 去年と同じく、Keynoteの幕開けとともにティム・クックとクレイグ・フェデリギが壇上に上がりました。最前列だからこそ、ティムとクレイグの存在がすぐそこに感じられました。 Keynote本編では、NameDropやStandByの発表、小島秀夫氏の登場など様々な驚きがあり、その度に会場の聴衆からは歓声が上がりました。その中でも、ひときわ聴衆を熱狂させたのはやはり、「One more thing …」のお馴染みのフレーズとともに発表されたvisionOSの登場でした。 Keynoteに続くPlatforms State of the UnionはCaffè Macsで、WWDCのTシャツを着たAppleのスタッフに交じって視聴しました。Swift macrosやSwiftDataなどデベロッパーとしてワクワクするアップデートが多く、会場からも喜びの声が上がったり、拍手が起きたりしていました。 Meet the Teams パブリックビューイングの時間が終わると、現地ではMeet the Teamsという、Appleのエンジニアやデザイナーと直接話せるイベントが催されました。 会場のスクリーンには、どのエリアに何のチームがいるのかのマップが映し出されており、興味のあるエリアに行って自由に話しかけることができました。 私たちはSpatial Computing、SwiftUI、Design、Developer Toolsなどのエリアに行って、日頃気になっていたことや今年の発表について根掘り葉掘り聞きました。その中でも特に印象的だったのはDesignのエリアです。Appleのデザイナーに自分たちのアプリを見てもらい、目から鱗が落ちるフィードバックを受けられました。そして、そのフィードバックをチームに共有して、実際に改善する動きまで繋げられました。 ZOZO×WWDC23オンライン 現地参加した2名以外のほとんどのメンバーがWWDC23にオンラインで参加しました。ZOZOでは、WWDCの開催期間中、現地に合わせて日本時間2:00〜11:00で勤務するメンバーや、通常の勤務時間で公開されているセッションの映像を視聴するメンバーもいました。 キャッチアップした情報を共有するために、毎日1回、ビデオ通話によるミーティングを行っていました。また、視聴したセッションのサマリや、ラボやアクティビティで得た情報はMiroで管理していました。WWDC終了後には、多くの情報がMiroにまとめられて以下のようになっていました。 Miroを用いたやり方はWWDC21よりZOZOで実施しているもので、下記のWWDC21参加レポートに詳しく公開しているので、よろしければこちらもご覧ください。 techblog.zozo.com Activities WWDC23では、オンラインのActivitiesとして、Q&AやMeet the presenterが実施されました。Q&Aでは、対象のトピックについて気になったことをAppleのエンジニアやデザイナーにSlackを通して質問できます。またMeet the presenterでは、セッション後にセッションの担当者にSlackで質問できます。質問に対して担当者が丁寧に回答してくださるので、セッションについての理解が深まります。 私も「Machine learning open forum」のQ&Aや「Mix Swift and C++」のMeet the presenterで質問をさせていただきました! また、オンラインのActivitiesとして「Trivia Time」が開催されました。Trivia Timeでは、参加者がWWDC23のセッション、開発ツール、Appleの歴史に関するトリビアクイズに挑戦しました。トリビアクイズでは、Xcodeの新機能に関することや、Appleの歴史に関するクイズが出題されました。クイズの参加者はSlackのスレッドで大いに盛り上がっていました! Labs & Sessions 去年に引き続き、ZOZOメンバーがラボでAppleのスタッフにお聞きしたことやフィードバックを一部紹介します。また、セッション動画の中からZOZOメンバーとして気になったものも紹介します。 Swift open hours lab & Xcode open hours lab ZOZOTOWN開発本部iOSブロックの小松( @tosh_3 )です。自分はWWDCではLabに参加して、Appleのエンジニアと話すのが好きで、今年も2つのLabに参加してきました。今年参加したLabはSwift open hours labとXcode open hours labです。 Swift open hours labでは、CombineとAsync & Awaitの連携について相談しました。Combine内でasyncMapのようなその内部でawaitできるような高階関数を作成できないだろうかというのを既存のアイデアともに持っていきました。また、そこに付随しながら、iOS 17で追加されたAPIやそれらのバックポートに対する姿勢などについてAppleのエンジニアと話しました。 Xcode open hours labでは、XcodeのPreviewの機能について質問しました。ZOZOTOWNではメインターゲットとは別に、Preview専用のターゲットを作っています。というのも、メインターゲットでPreviewを行おうとすると必ず失敗するという問題があったためです。 Appleのエンジニアにこの問題について聞いたところ、どうもlinking周りに問題があるらしく、custom linker flagを設定しているか確認されました。これは、CocoaPods側で設定されるもののようで、これが多くなることでPreviewに対して悪影響を与えている可能性があるとのことでした。こういった問題に対して直接Appleのエンジニア回答をもらえるのもLabの魅力です。 今年のDemystifyセッション こんにちは、ZOZOTOWNブロックiOSチームの森口です。 私は毎年密かにDemystifyシリーズのセッションを楽しみにしています。 古くは2015年のMysteries of Auto Layout, Part 1に始まり、2021年に Demystify SwiftUI が発表され、2022年には Demystify parallelization in Xcode builds とDemystifyから始まるセッションが続いています。 これらのセッションはAppleの技術の仕組みをより深く理解するために視聴は欠かせません。 今年のWWDCでは Demystify SwiftUI performance というセッションが発表されました。SwiftUIはシンプルなレイアウトから複雑なレイアウトまで実装できますが、プロダクトに本番導入してみるとパフォーマンスの観点で無視できない問題に遭遇する場合があります。このセッションではSwiftUIにおけるパフォーマンス問題のいくつかの原因と対応について解説しています。 ZOZOTOWNは歴史が長く続く、多くのお客様に利用されているプロダクトです。コードのモダン化を安全に進めていくことが常に課題となる私たちにとって、このセッション内容から得た知見は今後の開発に大いに役立ちそうです。 Apple Vision Proの発表に寄せて ARやVRといったXR領域のリサーチや検証などを担当している創造開発ブロックの @ikkou です。頭はひとつしかないのにVRヘッドマウントディスプレイやARグラスはたくさん持っています。さて、世界で初めて家庭用として販売されたVRヘッドマウントディスプレイの「Oculus Rift DK1」がリリースされたのは10年前の2013年でした。それから10年が経ち、ついにAppleから最初のSpatial computerである「 Apple Vision Pro 」が発表されました。 WWDC20頃から“Apple Glass”なるものが出るぞ出るぞとまことしやかに噂されていましたが、WWDC21、WWDC22と“One more thing”もないまま年月を重ねていました。その一方で他社からは続々とVRヘッドマウントディスプレイやARグラスが登場し、期待ばかりが膨らむ状況が続いていました。 そんな中で満を持して発表されたのがApple Vision Proです。今年こそ発表される確度が高いということを意識してか、競合にあたるとも考えられるMeta社は Meta Quest 3 をWWDC23の直前に急に発表しました。ARグラスのXREAL社(旧Nreal社)も 自社製品との違いを存分にアピール しています。 今回の発表を受けて、iOS開発者界隈に限らず、XR開発者界隈も非常に沸いています。3,499ドルという価格(日本円にして約50万円)はMicrosoft社の複合現実HMDである「 HoloLens 2 」の¥422,180よりも高いです。3,299ドルで販売されている「 Magic Leap 2 」に近い価格帯です。決してお安いお買い物ではありませんが、アーリーアダプター気質のある開発者は間違いなく買うでしょう。もちろん私も買います。 開発者視点では、 Unityの公式対応 が発表されたことも大きな意味を持っています。Unityの公式対応により、生粋のiOSエンジニアだけではなく、Unityを使ったXRエンジニアもこれまでの資産を生かせることになります。早速 ベータプログラム に申し込みました。 ところでAppleはSpatial computer、日本語では「空間コンピュータ」という言葉を用いていて、ARヘッドマウントディスプレイやVRヘッドマウントディスプレイといった言葉を用いていません。系譜としてはSpatial Computingという言葉を用いているMagic Leapに近い印象です。また、日本語では「没入」と訳されることの多いimmersiveというフレーズも用いています。ここには強い意思が感じられます。 そんなApple Vision Proに関連するセッションが複数用意されていたWWDC23でしたが、まず「 Principles of spatial design 」は必見です。あわせてアイトラッキングやハンドトラッキングに触れている「 Design for spatial input 」も欠かせません。空間コンピュータの名の通り、描画するのはiPhoneやiPadといった平面ではなく目の前にある空間そのものです。画面の絵作りや入力方法も大きく変わることを十分に理解する必要があります。 Apple Vision ProはUSでは来年初旬に、その他の国や地域では来年の後半より販売を開始とアナウンスされていますが、その対象に日本が入るかどうかは明示されていません。しかし、開発者向けのテスト施設である「 Apple Vision Pro Developer Labs 」をクパチーノ・ロンドン・ミュンヘン・上海・シンガポール、そして東京に開設することを発表しています。これは間違いなく日本でも発売されると言っても良いのではないでしょうか。 これからApple Vision ProのOSである visionOS の詳細が続々と発表されていくはずです。それらのリソースを頼りに日々“素振り”を続けていきたいところです。現場からは以上です! まとめ 本記事では、WWDC23の参加レポートをお伝えしました。 今年のWWDCも現地・オンラインで楽しむことができ、参加したメンバーとしては充実した5日間だったと思います。また、Apple Vision Pro、iOS 17など数多くの発表があり、アプリ開発者に限らず、たくさんの人が進化や未来を感じたのではないでしょうか。ZOZOは、WWDC23に参加して得られた知見を業務に反映して、サービスの向上に努めていきます! さいごに ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。ZOZO DevRelブロックの @wiroha です。6月5日の深夜から6月6日にかけて Extended Tokyo - WWDC 2023 を開催しました。 Extended Tokyoは、WWDCのメインセッション(Keynote)をさらに楽しむためのイベントです。今年もLINE株式会社、株式会社ZOZO、ヤフー株式会社の3社で主催しました。オフライン会場は2019年以来のヤフー紀尾井町オフィスにあるLODGEです。またオンライン会場は2021年ぶりにclusterのVR LODGEとハイブリッドで開催しました! イベント内容まとめ WWDCのKeynoteは日本時間で深夜2時からです。それに合わせて本イベントも23時30分と遅い時間からはじまりました。クイズ大会、LT大会で気分を高めた後、リアルタイムでKeynoteを視聴しました。 コンテンツ 登壇者 クイズ大会 LT1:Appleの進化を楽しむための歴史の授業 新妻 広康◆ヤフー LT2:あなたの知らないWWDC現地参加の世界 〜Apple Parkへ行った僕が見た、新しいWWDC〜 荻野 隼◆ZOZO LT3:WWDC「間」を復習しよう 平井 亨武◆LINE LT4:メタバースプラットフォーム開発におけるSwiftUIの活用とTips 董 亜飛◆cluster 交流会 Keynote視聴 クイズ大会 WWDCや各社にちなんだクイズ大会でイベントスタートです! 正解した方にはノベルティが贈られました。 じゃんけんのようにクイズに回答 正解者へのプレゼント 現地中継 現地からは歓声も聞こえてきます イベント中、何度か現地参加者とビデオ通話をつないで様子を伝えていただきました。とても明るく良い天気で、日本との気候の違いを感じますね。話しているとちょうど開場がはじまり、人がドッと動き出しました! 臨場感が伝わってきます! Appleの進化を楽しむための歴史の授業 ヤフー株式会社 新妻さま www.docswell.com LT大会へと移り、新妻さまからはXcodeが生まれる前に遡って開発の歴史を紹介いただきました。AutoLayout、Swift、SwiftUIはアプリ開発の問題を解決する大きなソリューションですね。 あなたの知らないWWDC現地参加の世界 〜Apple Parkへ行った僕が見た、新しいWWDC〜 株式会社ZOZO 荻野 speakerdeck.com ZOZOの荻野からは2022年のWWDCに現地参加した体験を時系列で発表しました。会議室やミニコンテンツ、現地で盛り上がった場面やトイレまで知れるのは面白かったです。今年もZOZOから現地に参加しているメンバーがおり、写真とメッセージを共有させていただきました! WWDC「間」を復習しよう LINE株式会社 平井さま speakerdeck.com 平井さまからは1年でWWDCまでの間にあった出来事をご紹介いただきました。間に起きた出来事の中で、App Storeの価格の設定方法のアップデートと UIViewController.ViewLoading について詳細を解説いただきました。価格設定は悩ましいと共感の声が出ていました。 メタバースプラットフォーム開発におけるSwiftUIの活用とTips クラスター株式会社 董さま speakerdeck.com 董さまからはclusterでのSwiftUIの知見を発表いただきました。マルチプラットフォーム対応で毎週リリースしているのはすごいですね。タブインジケータや画像のズーム、Truncated Textの詳細な実装を解説いただきました。ARデバイスの発表に期待する声は他の発表でも出ていました。 交流会 Apple Park内の様子が気になるみなさま Keynoteがはじまるまでは交流会を行いました。登壇者も発表が終わってホッとした様子でみなさんとお話を楽しんでいました。現地とも通話をつないで今年は何があるのか聞いたりしました。 Keynote視聴 ついにスタート!! 交流を楽しんでいるとあっという間にKeynoteの開始時刻となりました! 新しい情報には「おぉー」と声が上がったり笑いやどよめきが起きたり、みなさんと気持ちを共有できる楽しさを感じました。15インチMacBook Air、M2 Ultra、iOS 17など新しい情報が盛りだくさんでしたね。何よりApple Vision Proにはオフライン会場が沸きました! すごい、使ってみたいと早速感想を分かち合いました。 最後に みなさま夜遅い時間にもかかわらずご参加ありがとうございました。WWDCの詳細をもっと知りたいと思った方はぜひ6月27日の「 WWDC23 報告会 at LINE, ZOZO, ヤフー 」にご参加ください。WWDCに参加した各社のエンジニアが、新しく発表された技術や得た知見、情報などを共有します。 line.connpass.com ZOZOでは一緒にサービスを作り上げてくれるiOSエンジニアを募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co
はじめに こんにちは。DevRelブロックの @wiroha です。6月8日に ZOZO Tech Meetup〜ZOZOTOWNフロントエンドリプレイスの事例紹介〜 を開催しました。ZOZOTOWNを支える開発において「フロントエンドリプレイス」にフォーカスした技術選定や設計手法、設計時の考え方などを具体的な事例を交えながら紹介するオフラインイベントです。 登壇内容まとめ ZOZOのエンジニアと技術顧問の古川さんがZOZOTOWNのフロントエンドリプレイスの事例をLTとパネルディスカッション形式でご紹介しました。 コンテンツ 登壇者 Next.js を選定した ZOZOTOWN のフロントエンドリプレイス、その全体像 ZOZOTOWN開発本部 ZOZOTOWNWEB部 / 武井 勇也 オンプレミスの運用からクラウドの運用へ 〜SREのフロントエンドリプレイス裏側〜 技術本部 SRE部 / 秋田 海人 フロントエンドリアーキテクト2023 技術顧問 / 古川 陽介 パネルディスカッション 懇親会 会場の様子 Next.js を選定した ZOZOTOWN のフロントエンドリプレイス、その全体像 ZOZOTOWN開発本部 武井による発表 speakerdeck.com 武井からはリプレイスの経緯や課題点、その解決法などを発表しました。レンダリングについて詳細を熱く語り、参加者のみなさまも熱心にメモをとって聞いてくださっていました。質問も非常に多くいただきNext.jsの知見への需要を感じました。 オンプレミスの運用からクラウドの運用へ 〜SREのフロントエンドリプレイス裏側〜 技術本部 秋田による発表 speakerdeck.com 秋田からはSRE部のプロジェクト体制、リプレイスプロジェクトの進め方、インフラ構成について発表しました。ZOZOのようにセールなどで急激に負荷が高まるサービスの場合、負荷試験は重要です。自社で開発したGatling OperatorといったOSSツールを利用しているそうです。Gatling Operatorの詳細は次の記事で紹介しています。 techblog.zozo.com フロントエンドリアーキテクト2023 技術顧問 古川さんによる発表 speakerdeck.com 古川さんからはフロントエンドリアーキテクトの知見を発表していただきました。リアーキテクトとはどういったものか、うまくいくかどうかの違いはとても学びになりました。アンチパターンはとてもやりがちなことが紹介されていたので、ぜひ資料をご覧ください! パネルディスカッション みなさんが気になりそうなテーマでディスカッション LTを行った3人で各テーマのディスカッションを行いました。途中「リアーキテクトを完遂するにはエンジニアだけではなく経営判断が関わってくる」といった話になります。そのタイミングでなんと弊社VPoEの瀬尾が飛び入り参加! 経営層との対話などVPoEならではの話をし、来場者からの質問にも回答しました。 今後の予定など気になる質問にも回答 最後に 登壇者の集合写真 みなさまご参加ありがとうございました。今回はオフラインで懇親会の時間を設け、たくさん交流ができたかと思います。今後もさまざまなイベントを開催していきますのでよろしくお願いいたします! ZOZOでは一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。DevRelブロックの @wiroha です。6月1日に Recap: Google I/O 2023 を開催しました。 Google I/O 2023 で発表されたAndroidのセッションを振り返るイベントです。LINE株式会社、株式会社ZOZO、ヤフー株式会社の3社合同でオフラインとオンラインのハイブリッドで開催しました。 登壇内容まとめ 3社の社員によるLTとパネルディスカッションを行い、その後オフライン会場では交流会を行いました。 コンテンツ 登壇者 Introduction of new in Kotlin for Android 菅野 祐馬 / ヤフー How does the Relay connect Android app development and Design? 堀江 亮介 / ZOZO Android 14’s predictive back gesture 千北 一期 / LINE Jetpack Compose Debugging to fix performance problems 谷川 悠 / ヤフー Panel Discussion 森 洋之 / ヤフー 玉木 英嗣 / LINE 堀江 亮介 / ZOZO 鈴木 航 / ヤフー 当日の発表をYouTubeで公開しましたのでぜひご覧ください。 www.youtube.com Introduction of new in Kotlin for Android ヤフー株式会社 菅野さま speakerdeck.com 菅野さまからはKotlinに関するニュースを紹介いただきました。中でもKotlinがbuild scriptのデフォルト言語になったこと、kaptからKSPへ移行が推奨されていることは重要なトピックだと思いました。Kotlin 2.0 Compilerでのパフォーマンス改善は実際に試してみたいところですね。 How does the Relay connect Android app development and Design? 株式会社ZOZO 堀江 speakerdeck.com 弊社の堀江からはRelayを使ってFigmaからComposableを生成する話が発表されました。とても便利そうです。UIに更新があった場合や、Figmaで複数のVariantを扱う場合、クリックなどのインタラクションも対応されていました。Relayは現在α版だそうで今後に期待が高まります! Android 14’s predictive back gesture LINE株式会社 千北さま speakerdeck.com 千北さまからはpredictive back(予測型「戻る」)の発表がされました。predictive backを使うと画面が少しずつスケールしてアニメーションしながら遷移するため、ユーザはジェスチャー後の移動先を理解しやすくなります。 API level 33で非推奨となった OnBackPressed を新しいAPIに置き換える必要はありますが、Activityごとに部分的な導入もできるそうで試してみてはいかがでしょうか? Jetpack Compose Debugging to fix performance problems ヤフー株式会社 谷川さまの質疑応答タイム speakerdeck.com 谷川さまからはJetpack Composeのデバッグについて発表いただきました。Android Studio Hedgehog以降、Recomposition Stateを確認できるようになりデバッグがしやすくなったそうです。Layout Inspectorを使うとComposableが再描画された回数、スキップされた回数をデバッグできていました。改善の前後のデモが非常にわかりやすかったです。 Panel Discussion パネリストのみなさま 現地の写真 パネルディスカッションは事前にmiroでまとめた資料を基に進行しました。本日司会を務めた鈴木さまは現地参加ができたとのことで、写真や動画を共有いただきました。現地に行きたくなりますね! パネリストの気になる技術やセッション 印象的だった発表や今後期待することはみなさま会話型AIの Studio Bot をあげており大人気でした。まだ日本では使えないため、対応が待ち遠しいです。 面白かったセッションや試したい機能はFlutter、Large Screen、開発ツール、アクセシビリティなどさまざまな話題があがりました。もう一度Google I/Oのページを見て気になるセッションをチェックしてみようと思いました。 io.google 最後に 登壇者の集合写真 登壇者のみなさま、発表ありがとうございました。新しいニュースでワクワクする時間を過ごせました。お越しくださった皆さま、オンラインで視聴してくださった皆さまもありがとうございました! ZOZOでは一緒にサービスを作り上げてくれるAndroidエンジニアを募集中です。ご興味のある方は以下のリンクからぜひご応募ください。 hrmos.co hrmos.co hrmos.co
はじめに こんにちは、計測プラットフォーム開発本部SREブロックの髙木( @TAKAyuki_atkwsk )です。普段は ZOZOMAT や ZOZOGLASS 、 ZOZOFIT などの計測システムの開発・運用に携わっています。およそ2年ぶりのテックブログ執筆となりました。 さて、今回はCI/CD環境やKubernetesエコシステムのバージョン更新についてRenovateを使って楽しようという話をご紹介します。 CI/CDのワークフローや実行環境、Kubernetesを運用する上で導入するエコシステム 1 の多くはコード管理されています。そして、これらについてどのバージョンを使うかをコード上で指定することが多いです。しかし、コード化はされているもののバージョン更新まではなかなか手が回らなくなっており、どうにか解消したく取り組んだ話になります。 目次 はじめに 目次 背景や課題 バージョン更新のステップ 解決に向けて ツールの導入 Renovateの導入 試験的な運用 Renovateの設定 JSON5 kustomizeのリモートファイル参照 詳細なバージョン更新を行わない 本運用を行ってみて おわりに 背景や課題 計測プラットフォーム開発本部SREブロックでは計測システムにおける実行基盤(AWSやKubernetesを利用することが多い)やCI/CD環境、監視の仕組み、その他運用で必要なツールがあります。これらのほとんどはCloudFormationのテンプレートやKubernetesマニフェスト、ビルドパイプラインの設定ファイルなどコード化して管理されています。コード化を進めていくとパッケージを利用することが少なくないと思います。具体例を挙げると、Dockerイメージ、Helmチャート、CIの実行環境、プログラミング言語のライブラリなどになります。また、パッケージを利用する際にはその名前とバージョンをコード上で指定します。 これらのパッケージは継続的に更新されていて、脆弱性の対応やバグ修正、新機能追加、パフォーマンス向上などの内容が含まれています。したがって、更新された内容を取り込むには指定するバージョンを上げていく必要があります。latestタグなどを使い最新のバージョンを使う方法もありますが、予期しない変更が入ってしまったり、別のバージョンが意図せず同時に稼動したりするため限定的に利用される方法だと思います。 このため、パッケージのバージョン更新に付いていくのが理想ですが、私たちのチームではパッケージのバージョンは一度設定したままになっているか気づいたタイミングで更新するという状態に陥っていました。この状態の問題点は、新しいバージョンを利用したいと思ってもパッケージの変更の差分が大きくなり過ぎるため、更新を適用して問題ないか判断するのに時間が掛かってしまうことです。特にパッケージの特定バージョンのサポートが切れてしまったり脆弱性が見つかったりする場合にこの問題の影響を受けてしまいます。 バージョン更新のステップ あるパッケージのバージョンを更新するには以下のステップが必要になると考えています。 更新されたことに気づく 更新して問題ないか判断する 更新を適用する バージョン更新作業はなんとなく大変だなと思われている方は私以外にもいるかもしれません。この大変さというのを考えてみると、「更新されたことに気づく」「更新して問題ないか判断する」ステップにそれぞれ大変さの要素が存在すると考えが到りました。「更新を適用する」ステップに関しては、私たちは既にデプロイメントパイプラインを整えているため変更のPRをマージさえすれば適用される状態になっています。 更新されたことに気づく そもそも更新されたことに気づかない 全てのパッケージの情報を追っていられない 気づいた人がやることになる 作業の負担が大きく属人化しやすい 更新して問題ないか判断する どう影響するか判断するのが大変 変更差分を探してきて理解する 自動テストが無いものは開発環境に反映して動作確認が必要 これらの要素が組み合わさると以下のような悪循環が発生してしまいます。図の中で示した特に大変さを感じるポイントは差分が溜まった上での更新して問題ないかの判断です。これを解消するには判断する作業自体の負担を減らすこと、更新が溜まらないようにすることが必要だと考えました。 解決に向けて ツールの導入 このような大変さに対してツールを使うことで緩和できるのではと考えました。フロントエンドやバックエンドの開発においては既に浸透しているものだと思います。ツールの具体例としては、 Dependabot や Renovate 、 Scala Steward (Scalaに特化したものですが)があります。これらのツールを用いる場合のツールと人間の作業の棲み分けを以下のようにまとめました。いくつか人間のやることは残っていますが、上記で挙げた大変さの半分以上は解消されそうです。 ステップ ツールのやること 人間のやること 更新されたことに気づく プルリクエスト(以下PRと表記)の作成、レビュアやassigneeの割り当て PRの存在を把握 更新して問題ないか判断する 変更差分のまとめ 変更差分の理解、開発環境での動作確認、更新の適用を見送る場合はPRのクローズ 更新を適用する PRのマージ さて、私たちはこのツールとしてRenovateを使うことにしました。他の選択肢としてはDependabotも候補に挙がりました。Renovateを使う判断の決め手となったのは更新検知される対象パッケージの豊富さでした。特に、CI/CDやKubernetesエコシステムに関してはRenovateに優位性が見られ、私たちの管理するパッケージがその対象範囲に多く含まれると考えました。また、社内で他の部署がRenovateを利用していたので先人からアドバイスをもらえるという点も良かったです。 docs.renovatebot.com ただ、どちらのツールもサポートされる対象が決まっているので、対象外のパッケージについては私たち自身で更新に気づいて作業する必要があります。 Renovateの導入 Renovateは GitHub App が提供されているのでリポジトリに対して設定することで簡単に導入できます。その他、GitHub Actionsを利用する方法などさまざまな導入方法が用意されていますので詳しくは以下のページを参考にしてください。 docs.renovatebot.com GitHub Appを設定すると対象のリポジトリにRenovateの設定ファイルを追加するPRが自動的に作成されます(以下画像を参照)。必要であれば設定ファイルを編集します(設定ファイルの編集はマージ後でも可能です)。このPRをマージすると導入は完了です。 もし、リポジトリ内のパッケージに更新があればRenovateによって以下のようなPRが作成されます。PRの概要には対象のパッケージが何から何に更新されるかの情報とバージョン毎のリリースノートが記載されます。PRに含まれるソースコードの差分としては、以下の例ではArgoCDアプリケーションのソースとして指定するHelmチャートのバージョンを書き換えるものとなります。 リリースノートを探しに行くのは手間になるので、このように概要欄に展開してくれるのは非常に助かります。パッケージによっては記載されないものもあるので、その場合は自分でコミットを追うなりする必要があります。 試験的な運用 1つのリポジトリに導入するのであれば先ほど紹介したような形になりますので比較的容易だと思います。しかし、私たち計測プラットフォームSREが主に管理するリポジトリは現在8つあります。その内訳はインフラ用(CloudFormationテンプレートやTerraformのtfファイルの管理)、共通ツール用、各サービスのKubernetesマニフェスト用(6つある)となっています。Kubernetesマニフェスト用のリポジトリについては以下の記事で説明されていますので良ければ参考にしてみてください。 techblog.zozo.com 最終的にはこれらのリポジトリ全てにRenovateを導入しますが、一度に行うと今まで溜まっていたバージョン更新についてのPRが大量に来てメンバーが捌ききれなくなる懸念を持ちました。ツールの導入が目的ではなく、バージョン更新を楽して継続的にやっていくことが大事と考えたのでまずは試験的に運用することにしました。具体的には、対象のリポジトリを1つとし、対応するメンバーをチームの5人中3人に絞って上手く運用できるか試しました。 また、このように更新作業を進めましょうという簡単なフローを考えて試してみました。 簡単に説明すると、まずRenovateによってパッケージの更新が検知されると新しくPRが作成され自動的にレビュアが割り当てられます。対応メンバー内で最初に確認するメンバーを決めてPRのassigneeに割り当てます。assigneeは更新内容を確認し開発環境に反映して問題ないことを確認します。 確認方法はパッケージによって異なりますが、KubernetesエコシステムであればコントローラーのPodが起動しているか、エラーログが出力されていないかを主に確認します。CIの実行環境であれば、実際にジョブを動かしてみて成功するかを確認します。 確認して問題なければ他のメンバーをレビュアに割り当ててレビューを依頼します。開発環境に反映して問題がある場合、修正等で対応するか対応できないと判断してPRをクローズする、つまりバージョンを更新しないことになります。 このような形で試験運用を行う中で、誰が一番最初に見るかという問題が残っていることに気がつきました。Renovateによって対応するメンバー全員に対してレビュアが割り当てられていることで「誰かが最初の確認をやってくれるだろう」という状態になっていました。チーム内で相談した結果、明示的に担当を決めようということになりました。当初は週に一度PRを眺めて担当を決めていましたが、これも手間だと感じたためRenovateによってランダムアサインする設定を以下のように行いました。 { " assignees ": [ " Alice ", " Bob ", " Carol " ] , " assigneesSampleSize ": 1 } また、開発環境に反映しないと確認できないパッケージについては、確認方法を社内のコンフルエンスページにまとめることでどのメンバーがアサインされても作業が進められるようにしました。試験運用を経てチームメンバーが概ね問題なくバージョン更新の運用をできるようになったので残るリポジトリにもRenovateを導入し本格的に運用を始めました。 以上の改善点を踏まえた上で、以下のフローに沿って運用することにしました。「通常のレビューフロー」について補足すると、PR作成者がレビュアを指定し、レビュア2人からapproveされればマージ可能となっています。RenovateによるPRの場合assigneeがレビュアを指定するという点のみ異なります。また、CIで変更に問題ないことを担保できれば通常のレビューフローとほぼ変わらない形になります。 Renovateの設定 「Renovateの導入」のセクションでGitHub Appを設定後に設定ファイルを追加するPRが作られると紹介しました。初期設定から追加した設定のいくつかをここでは紹介します。 JSON5 初期設定では renovate.json というファイルが作成されますが以下のドキュメントにある通り JSON5 フォーマットにも対応されています。主にコメントを書くことができるという理由でJSON5フォーマットを利用することにしました。 docs.renovatebot.com kustomizeのリモートファイル参照 私たちはKubernetesマニフェストを複数環境に対応させるため kustomize を利用しています。kustomizeではリモートに存在するディレクトリやファイルを参照できます。例えば以下のように書いて kustomize build するとURLを参照しArgoCDインストールに必要なマニフェストを展開してくれます。 apiVersion : kustomize.config.k8s.io/v1beta1 kind : Kustomization resources : - https://github.com/argoproj/argo-rollouts/releases/download/v1.5.0/install.yaml これは remote targets と呼ばれる機能の1つであるremote filesを利用しています。しかしRenovateではこちらの機能はサポートされていません。以下リンク先のremote resourcesがkustomizeのremote directoriesに対応しています。 docs.renovatebot.com 調べたところ RenovateのIssue #18986 に同じようなトピックが存在し、ワークアラウンドが提示されているのを発見しました。これを利用して以下のような設定を追加しています。 { " regexManagers ": [ // GitHub上に存在するマニフェストファイルをkustomizationで利用する部分のバージョン管理 { // kustomization.yml or kustomization.yaml " fileMatch ": [ " kustomization \\ .ya?ml$ " ] , // 例: https://github.com/argoproj/argo-rollouts/releases/download/v1.5.0/install.yaml " matchStrings ": [ " https: \/\/ github\.com \/ (?<depName>.* \/ .*?) \/ releases \/ download \/ (?<currentValue>.*?) \/ " ] , " datasourceTemplate ": " github-releases " , } , { " fileMatch ": [ " kustomization \\ .ya?ml$ " ] , // 例: https://raw.githubusercontent.com/argoproj/argo-cd/v2.7.2/manifests/install.yaml " matchStrings ": [ " https: \/\/ raw.githubusercontent.com \/ (?<depName>[^ \/ ]* \/ [^ \/ ]*) \/ (?<currentValue>.*?) \/ " ] , " datasourceTemplate ": " github-tags " , } ] } この設定によって以下のようなPRが作成されるようになりました。 詳細なバージョン更新を行わない 私たちはCIの基盤としてCircleCIを一部で利用しています。CircleCIのジョブで CircleCI Orb を利用する際に circleci/aws-cli@3 のようにメジャーバージョンのみを指定する箇所がありました。すると以下のようにバージョンを詳細化するPRが作成されました。 この変更は意図しないものだったのでPRをクローズしました。そして新しい設定を追加しました。 rangeStrategy という設定でバージョンの対象範囲を制御しています。 OrbのrangeStrategyはデフォルトでpinになっている ため明示的にreplaceを指定するようにしました。 { " packageRules ": [ { " matchDatasources ": [ " orb " ] , // NOTE: orbの場合はpinになっていて、これだとメジャーバージョンのみ指定していても詳細なバージョンに更新してしまう // これを回避するためにreplaceに設定する " rangeStrategy ": " replace " , }, ] } 本運用を行ってみて 試験運用を含めて約5か月間で77件中55件のPRをマージもしくはクローズしました。PRには例示したように、KubernetesエコシステムやCI/CDの環境に関するものが含まれています。以下のグラフはバージョン更新に関するPRの対応件数を示しています。このように、Renovate導入前はバージョン更新がほぼ手付かずになっていましたが、導入後は継続的に更新できるようになっています。 Renovateによる恩恵によって大変さの悪循環としては以下のように変化しました。まだ循環の要素としては残っていますが、更新差分が溜まりにくくなったことで作業の負担は減っています。 Renovate導入前 Renovate導入後 実際に運用してみて気づいた点を2つ紹介します。1つ目はバージョン更新に必要な情報が均一化されて確認しやすくなったことです。人間によって作成されたPRでは情報の粒度が異なる傾向にあります。RenovateによってPRが作られることで、パッケージへのリンクやリリースノートといった情報が確実に含まれるのでレビューしやすくなると感じました。 気づいた点の2つ目は、長期休暇などで一時的にチームメンバーが減ると、PRがアサインされても確認に取り掛かれないことがありました。この点については、1週間の中でバージョン更新する時間をタイムボックスとして確保することや、パッチバージョンのみの更新であれば自動的にマージすることを対策として考えています。 おわりに 計測プラットフォーム開発本部ではこの記事で紹介したような既存サービスに関わる部分での改善、新規事業の開発など、幅広い業務を担当しています。現在、私たちと共にサービスを支える方を募集しています。少しでもご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com この記事では例えば、 External Secrets Operator や AWS Load Balancer Controller 、 ArgoCD のようなミドルウェアを指します。 ↩
こんにちは、ZOZO NEXTでウェブエンジニアを担当している 木下 です。先日、弊社が運営するオウンドメディアのFashion Tech Newsにおいて、記事リストのパーソナライズを行いました。本記事ではパーソナライズ導入における、要件定義、レコメンドエンジンの比較、実装での知見や注意点についてまとめます。 fashiontechnews.zozo.com 背景 解決方法の検討 課題の分析 パーソナライズ手法の検討 レコメンド方式について サービスの比較 Amazon Personalizeの実装 実装の流れ アーキテクチャ 実装での工夫点 採用したアルゴリズム アイテムデータの更新頻度 ユーザーの識別 注意点 AWS Personalizeのサンプルリポジトリが古い データの収集には時間がかかる まとまった料金が発生する まとめ 背景 「Fashion Tech News」とは、2018年に運用を開始したZOZO NEXTのオウンドメディアです。ファッションテック領域へ挑戦を続けるZOZO NEXTが、独自の視点でファッション×テクノロジーのニュースを提供しています。 記事本数の増加や継続的なサイトの改善などを進めていましたが、以下の課題がありました。 直帰率が高い 単独の記事を目当てにした新規ユーザーが多い そのためPV数の増加には、回遊率を増やしたり、再訪問率を上げたりすることが必要でした。 解決方法の検討 上記の解決方法として、記事下部にある関連記事のパーソナライズを検討しました。 ©️Fashion Tech News 課題の分析 ユーザーの行動を分析した結果、利用するユーザーの大半は新規ユーザーであり、検索やTwitterから各記事に直接訪問することがわかりました。そこで回遊率向上のために、記事を読み終わった後にある関連記事をパーソナライズすることにしました。ユーザーに合った記事を表示することで、好みの記事が見つかるサイトであるという体験も提供でき、再訪問率の向上も期待できます。 パーソナライズ手法の検討 レコメンド方式について まずはレコメンド方式について調査し、主に以下の方式があることを把握しました。 コンテンツベースフィルタリング 協調フィルタリング それぞれ長所と短所があり、状況に応じて使い分けることが必要です。 コンテンツベースフィルタリングは、アイテムの属性情報をもとに類似度を計算することで、ユーザーの好みに合わせたレコメンドを作成します。一方協調フィルタリングは、ユーザー同士の評価値の相関関係を分析することで、ユーザーの行動履歴から類似ユーザーを見つけ、そのユーザーが好んだアイテムをレコメンドするアルゴリズムです。 今回は新規ユーザーが多いため、コンテンツベースフィルタリングを採用する方向で進めていました。一方でより様々なアイテムのレコメンドを行うために、協調フィルタリングも併せて活用できるのではと考えました。結果的に、協調フィルタリングとコンテンツベースフィルタリングを組み合わせたレコメンドアルゴリズムを採用することとなりました。 サービスの比較 3つのサービスを項目ごとに比較しました。今回比較したサービスはどれも前項で説明したレコメンド方式の両方を提供していました。 比較項目 Amazon Personalize Google Recommendations AI Algolia Recommend レコメンドアルゴリズム - 一緒に購入される - 関連 - 類似 - あなたへのおすす - トレンド - 人気 - 最近見た - ユーザーセグメント - パーソナライズランキング - 新しいアイテムのレコメンド - 一緒に購入される - 関連 - 類似 - あなたへのおすすめ - 最近見た - もう一度購入 - セール中 - 一緒に購入される - 関連 - 類似 - トレンド ハイパーパラメータの調整 ○ ○ × トレーニング頻度の調整 ○ ○ × 月間20万リクエストでの概算料金 300USD 400USD 120USD 特徴 - AWSの他のサービスと連携しやすい - モデルの最初のトレーニングに2-5日必要 - 検索機能も同時に導入可能 - UIライブラリがある - 検索機能も同時に導入可能 比較検討の結果、次の理由でAmazon Personalizeを選択しました。まずは様々な ユースケース に対応していることです。ウェブメディアでも様々なアルゴリズムが試せると考えました。2つ目はMLモデルの調整ができることです。パーソナライズを導入するのが初めてのことであり、効果検証をする中でパーソナライズの精度を調整する可能性も考えたためです。それぞれのサービスに特徴があるので、実装したい機能に応じて使い分けると良いと思います。 Amazon Personalizeの実装 実装の流れ 上記の通りAmazon Personalizeを採用しました。理解しやすいように、大まかな実装の流れを挙げます。 データセットグループ の作成 インタラクションデータ の収集 アイテムデータ のアップロード ソリューションとソリューションバージョン の作成 キャンペーン の作成 レコメンド の取得 ※個人的に理解につまづいた用語の説明を加えます。 用語 説明 ソリューション アルゴリズムやパラメータの管理 ソリューションバージョン トレーニング済みのMLモデル キャンペーン APIでレコメンドを取得できるようにデプロイ 実装の結果、以下のようにレコメンドが取得できました。左が従来通りで、右がレコメンドです。この結果は、「 「ゲーム業界が考えるメタバースとは全く異なる」日本でも話題の「AGLET」が描く戦略とビジョン 」という記事の関連記事であり、記事内容に沿ったレコメンドが確認できます。 ©️Fashion Tech News アーキテクチャ 実装に当たって主に以下のサービスを採用しました。 Amazon API Gateway AWS Lambda Amazon Personalize Amazon PersonalizeのSDKをJavaScriptで直接呼ぶことも可能ですが、AWS LambdaやAPI Gatewayを用いたアーキテクチャを採用した理由は、以下の通りです。 PersonalizeのイベントトラッカーやキャンペーンのARNを隠蔽できる API GatewayのCORSの設定によりブラウザからの不要なリクエストを除ける 実装での工夫点 採用したアルゴリズム Amazon Personalizeでは レシピ と呼ばれる、ユースケースごとのアルゴリズムが用意されています。コールドスタートの新規ユーザーを考慮し、インタラクションデータに加えてアイテムデータを利用したレコメンドを行う、 Similar-Items のレシピを選択しました。これは先述の、協調フィルタリングとコンテンツベースフィルタリングのハイブリッドを意味します。 アイテムデータの更新頻度 新規ユーザーが多いことを踏まえると、アイテムデータの類似度によるレコメンドが役立ちます。そのため新規で公開された記事もレコメンドへ反映されるよう、記事の公開に合わせて毎日再トレーニングが実行されるよう設定しました。処理は定時にGitHub Actionsで実行されます。 ユーザーの識別 ユーザーのインタラクションを記録するには、IDなどで区別する必要があります。しかしサイトにはログイン機能がないため、ユーザーを識別する手段としてGoogle Analytics 4 (GA4)のClient IDを利用しました。一方でGA4が無効になっているユーザーに対しては、従来通りの記事リストが表示されるようにします。 注意点 AWS Personalizeのサンプルリポジトリが古い AWS LambdaやAPI Gatewayの実装には、CFnの拡張機能で利便性が高い AWS SAM を活用しました。AWS SAMはCLIから操作をするのですが、 AWS Quick Start Templates というコマンドがあります。このコマンドにより様々な実装例が確認でき大いに参考になりました。 当初はAmazon Personalizeのサンプルリポジトリにある、 streaming_events というコードを参考にしていました。しかしこのコードはAPI Gatewayの書き方などが古く、大部分を書き直す必要がありました。アーキテクチャを考える上で参考になりますが、お気をつけください。 データの収集には時間がかかる ソリューションを作成するために 必要なデータ量 は、2回以上のインタラクションがあるユニークな25人以上のユーザーによる1,000回のインタラクションで、推奨は50,000回です。そのため、計画を立てる上でデータの収集期間をしっかりと見積もることが大切です。もしくは既にあるアナリティクスのデータなどを用いることができれば、早く済ませることができます。 まとまった料金が発生する Amazon Personalizeを利用する上で主に発生する 料金 は、(1)MLのトレーニングと(2)レコメンドがデプロイされている期間です。 トレーニングにはトレーニング時間(4v CPUと8GiBメモリを使用する1時間のコンピューティング性能)という単位で料金が発生します。トレーニングをする頻度は、料金も踏まえて検討する必要があります。仮に毎日10トレーニング時間を利用した場合、1か月で72USDかかることになります。 レコメンドがデプロイされている期間、つまりキャンペーンが存在する期間は料金が発生します。TPS時間という単位で料金が発生しますが、これが最低でも1時間あたり0.20USD、1か月で144USD程度かかります。 まとめ Amazon Personalizeを採用することで、関連記事リストのパーソナライズを実現しました。Amazon Personalizeは、様々なアルゴリズムを利用でき、一部の設定は微調整もできます。上記注意点に挙げたデータの収集や、サンプルリポジトリが古いことに気づくまでに時間がかかったこともあり、運用開始までに2人で約1.5か月かかりました。ただAWSに日常的に携わって詳しい方なら、1-2週間で運用開始できるのではないでしょうか。 MLの実装知識がなくても利用できるのも魅力の1つです。もちろんどのレシピを選択すべきかなどはMLの仕組みを踏まえて検討するため、その知識は必要になります。現在は結果を踏まえ分析をし、より良いレコメンドが行えるように調整しています。レコメンドの効果までお伝えできなかったのは残念ですが、実装の参考になれば幸いです。 ZOZO NEXTでは、様々な技術を取り入れUXを最大化しながらプロダクト開発に取り組んでいます。絶賛仲間を募集しておりますので、興味を持ってくださった方は以下をご確認ください。 カジュアル面談はこちらからご応募ください。 hrmos.co 募集している職種はこちらからご確認ください。 hrmos.co hrmos.co hrmos.co
はじめに こんにちは、SRE部カート決済SREブロックの伊藤です。普段はZOZOTOWNのカート決済機能のリプレイス・運用・保守に携わっています。また、チームを跨いだ横断活動としてデータベース(以下DB)周りの運用・保守・構築に関わっています。 ZOZOTOWNではSQL Serverを中心とした各種DBMSが稼働しています。本記事はSQL Serverのパフォーマンスを調査する上で進めた可視化についての取り組みをご紹介します。 はじめに 従来の方法 DMV運用の課題 Splunkによるダッシュボード化 DMVの可視化例 インストールされているServerのログ情報の送信 DatadogのDatabase Monitoringについて Database Monitoringを使用して改善した例 CPU使用率の高いクエリの検出と改善 パフォーマンスが急に悪化した場合の原因調査 今後の展望 最後に 従来の方法 以前下記のテックブログで紹介させていただきましたが、弊社では 動的管理ビュー (Dynamic Management View:以下、DMV)や拡張イベントの情報をロギングしています。 techblog.zozo.com これらの情報を用いることで何かトラブルが起こった際には後追いできる状況を整えています。 例えば特定の時間帯にクエリが滞留した際には次のクエリを実行することで、滞留していたクエリの詳細な情報を調べることができます。 SELECT collect_date, count (*) AS [クエリの滞留数(全体)] FROM [dbo].[dm_exec_requests_dump_per_several_seconds_20230502] WHERE collect_date between @start_date and @end_date GROUP BY collect_date ORDER BY collect_date ; SELECT collect_date, wait_type, count (*) AS [クエリの滞留数(wait毎)] FROM [dbo].[dm_exec_requests_dump_per_several_seconds_20230502] WHERE collect_date between @start_date and @end_date GROUP BY collect_date, wait_type ORDER BY collect_date, wait_type ; SELECT collect_date, current_running_stmt, count (*) AS [クエリの滞留数(statement毎)] FROM [dbo].[dm_exec_requests_dump_per_several_seconds_20230502] WHERE collect_date between @start_date and @end_date GROUP BY collect_date, current_running_stmt ORDER BY collect_date, current_running_stmt ; 他にも、ストアドプロシージャ(以下、ストアド)の実行統計 sys.dm_exec_procedure_stats もDumpしています。 ストアドに修正を行なった際の監視や、特定のストアドが突然パフォーマンス劣化した際などはこちらを確認することで具体的なパフォーマンスを調べることができます。 オリジナル情報は累積値となっているため、LAG関数を使用して1分前の情報と差分を取ることで1分間の実行回数や実行時間を出力しています。 SELECT object_name, collect_date, execution_count AS ' 実行回数(累積値) ' , -- リセットされていたらキャッシュアウトされた可能性あり CASE WHEN cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date) THEN CONVERT (nvarchar, execution_count - LAG(execution_count, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' 実行回数(1分間の合計) ' , CASE WHEN cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date) THEN CONVERT (nvarchar, total_worker_time - LAG(total_worker_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' CPU時間(1分間の合計) ' , CASE WHEN cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date) THEN CONVERT (nvarchar, total_elapsed_time - LAG(total_elapsed_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' 実行時間(1分間の合計) ' , CASE WHEN cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date) THEN CONVERT (nvarchar, total_logical_reads - LAG(total_logical_reads, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' 論理読み込み量(1分間の合計) ' , CASE WHEN cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date) THEN CONVERT (nvarchar, total_logical_writes - LAG(total_logical_writes, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' 論理書き込み量(1分間の合計) ' FROM dbo.dm_exec_procedure_stats_dump WITH (NOLOCK) WHERE collect_date BETWEEN @start_date AND @end_date AND object_name = ' <絞り込みたいストアドの名前> ' ORDER BY object_name, collect_date DMV運用の課題 ロギングしたDMVは上記のようにトラブルシューティング時に役立てることができますが、周りのメンバーに対応してもらうにあたってハードルの高さを課題として感じていました。 まず、上記のSQLを作るにはDMVについて理解を深める必要があります。SREの全員がDBに精通しているわけではなく、普段SQLを書かないメンバーもいるので学習コストが必要となります。 また、DBに接続するためにはプライベートなネットワークを経由する必要があるなど一手間かかります。本番環境で稼働しているDBサーバーに対してSQLを実行する必要があるので、日常的な運用には向いていません。 SQL Server 2016からはクエリの実行履歴を保存して可視化できる クエリストア の機能も追加されました。トラブルシュートする上で有用ではありますが、上記と同様の課題や表示速度なども含めて、運用の利便性が高いものとは言えませんでした。 そのためパフォーマンス悪化の徴候があったとしても後手に周り、問題が発生してからでないと気付きにくい側面がありました。 Splunkによるダッシュボード化 DMVの課題を解決するために取り組んだのがまずSplunkによるダッシュボード化です。 弊社ではさまざまな場所でSplunkを活用しています。過去のテックブログにもSplunkに関する記事がありますので、興味のある方は是非ご覧ください。 techblog.zozo.com 今回使用したのは Splunk DB Connect というアドオンです。Splunk DB Connectではデータベースの情報を直接インポートできる他に、カスタムクエリを定期的に実行して結果をSplunkに送信できます。 Splunk DB Connect自体のインストール方法やDBとの接続方法に関しましては 公式ドキュメント をご参照ください。 DMVの可視化例 冒頭でDMVの活用例としてストアドのパフォーマンス調査を挙げましたが、まずはこちらをSplunk DB Connectで毎分実行し、Splunk側へ蓄積されるようにしました。作成したのが下記クエリです。 ストアド毎に、3分前から1分前の間にdumpとして保存された2つのレコードを取得し、差分を出力しています。SQL内のコメント文はSplunk DB Connectで設定する際に動作影響が出るため削除しています。 DECLARE @start_date DATETIME2 = dateadd(mi, -3 , GETDATE()); DECLARE @end_date DATETIME2 = dateadd(mi, -1 , GETDATE()); SELECT object_name AS ' stored_procedure ' , collect_date, exec_count_sum_1m, cpu_time_sum_1m, exec_time_sum_1m, logical_read_sum_1m, logical_write_sum_1m FROM ( SELECT object_name, collect_date, CASE WHEN (object_id = LAG(object_id, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) and (cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) THEN CONVERT (nvarchar, execution_count - LAG(execution_count, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' exec_count_sum_1m ' , CASE WHEN (object_id = LAG(object_id, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) and (cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) THEN CONVERT (nvarchar, total_worker_time - LAG(total_worker_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' cpu_time_sum_1m ' , CASE WHEN (object_id = LAG(object_id, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) and (cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) THEN CONVERT (nvarchar, total_elapsed_time - LAG(total_elapsed_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' exec_time_sum_1m ' , CASE WHEN (object_id = LAG(object_id, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) and (cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) THEN CONVERT (nvarchar, total_logical_reads - LAG(total_logical_reads, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' logical_read_sum_1m ' , CASE WHEN (object_id = LAG(object_id, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) and (cached_time = LAG(cached_time, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) THEN CONVERT (nvarchar, total_logical_writes - LAG(total_logical_writes, 1 , 0 ) OVER ( ORDER BY object_name, collect_date)) ELSE ' - ' END AS ' logical_write_sum_1m ' FROM dbo.dm_exec_procedure_stats_dump WITH (NOLOCK) WHERE collect_date BETWEEN @start_date AND @end_date AND object_name not like ' sp[_]% ' AND exists ( select * from sys.objects ob with (nolock) where ob.object_id = object_id(object_name) and is_ms_shipped = 0 ) ) AS sample WHERE NOT exec_count_sum_1m= ' - ' ORDER by collect_date, object_name 上記のクエリで収集した情報を次のようなサーチ文で可視化できます。 index = " heavy_forwarder_db " sourcetype= " dbconnect " host=XXX source= " XXX-procedure-stats " | timechart span=1m useother= false limit= 20 sum (exec_count_sum_1m) by stored_procedure 収集に使用した時間とSplunkが受信する時間に差があるので表示上少しのずれは発生してしまいますが、許容範囲としています。 また、上記のサーチ文では上位20件の情報を表示させていますが、別途テキスト入力欄を設けて特定のストアドを追えるダッシュボードも提供しています。 インストールされているServerのログ情報の送信 Splunk DB Connectとは別に、サーバーにSplunk Universal Forwarderをインストールすることでパフォーマンスモニタやイベントログを送信できます。 同一ダッシュボード内でイベントログとクエリのパフォーマンス情報を表示することで両者の相関関係を結びつけることができ、以下の切り分けが容易になります。 サーバー自体の問題なのか サーバー上で動いているSQL Serverの問題なのか SQL Server上で実行された特定のクエリの問題なのか Splunkによるダッシュボード化を行うことで、DMVやSQLに精通していないメンバーがトラブルシュートのために必要な情報を容易に取得できるようになりました。またプライベートなネットワークを経由して本番環境のDBサーバーにSQLを実行する必要がなくなり、運用の利便性と安全性の向上を実現しました。 DatadogのDatabase Monitoringについて 弊社ではDatadogも活用しています。 techblog.zozo.com 2022年8月、DatadogのDatabase Monitoring機能がSQL Serverに対してもサポートされるようになったため、オンプレミス環境の主要DBへの導入を進めました。 Datadog Agentのインストール方法については公式サイトをご参照ください。 docs.datadoghq.com データ収集は以下の理由からKubernetesクラスタ上にDatadog Agentのpodを立ててDBにアクセスする方法を採用しました。 元々RDSに接続してメトリクスを取得するための雛形を用意していたこと 何か問題があった場合にAgentのアンインストールが不要であること オンプレの場合は直接Agentをインストールした方がOSのメトリクスなど取得できる情報は増えるが別手段で収集済みであり、そこまで重要視しないこと Database Monitoringを使用して改善した例 CPU使用率の高いクエリの検出と改善 Database Monitoringを有効化することで使用できるようになる クエリメトリクスビュー ではクエリ毎のリソース使用率を見ることができます。 次の画像はWORKER TIME(CPU)順で並び替えた画像であり、特定のクエリでCPUを多く使っていることがわかります。 クエリの詳細を確認した結果が次の画像となりますが、グラフから実行時間が安定していないことがわかりました。それぞれの時間帯で記録されていた実行計画を見たところ、遅い時間帯のみ特定のテーブルでスキャンが発生していました。 問題のクエリの平均レイテンシ パフォーマンスが悪い時の実行計画 パフォーマンスが良い時の実行計画 スキャンするプランは望んでいないためFORCESEEKヒントを追加したところクエリパフォーマンスが改善し、DB全体のCPU使用率の改善も確認できました。 Database Monitoringの導入により、視野が広がったことで今まで問題視していなかった部分に対しても先回りして修正できるようになりました。手動で実施していたパフォーマンス情報の収集や キャッシュからの実行計画の収集 もクエリメトリクスビューから閲覧可能なので不要となり、作業の効率化へと繋げられました。 パフォーマンスが急に悪化した場合の原因調査 実行計画の変化や特定の負荷がかかった場合などパフォーマンスが急激に悪化することが度々ありました。その場合はDMVを利用して深掘りを行なっていましたが、最初のアクションとしてDatabase Monitoringを確認するという手段が取れるようになりました。 画像はハードウェア起因のトラブルが発生し、エラーが多発してしまった際のものです。 WriteLogのWaitが大量に発生してしまっており、何らかの要因でトランザクション書き込みが待たされていることがわかります。 緊急時には一刻も早い原因特定が求められるため、簡単に確認ができ、また対応できるメンバーを増やせることは非常に嬉しいポイントです。 今までだと問題に応じて様々なDMVを使い分ける必要があり、対応メンバーにはDMVに対する知見が必要でした。Database Monitoringを活用することで様々な角度から初期調査ができ、属人化の削減に繋げられました。 今後の展望 以上のように、SplunkとDatadogを用いてDBのパフォーマンスを可視化する取り組みを進めました。 現状の両者の使い分けとしては、Datadogによって自動でパフォーマンスを取得し、カバーしきれていない範囲をSplunkのダッシュボードにまとめています。 ただしその理由は時系列的な側面が強く、例えばDatadogでもカスタムクエリを使用したメトリクス化は実現可能であるためそれらをDatadog側に寄せていくことも可能です。 一方でSplunkは自社ではDBに関連する様々なリソースのログも蓄積されてきているため、相関的に情報を得やすいというメリットが存在します。 両者のメリットを活かしつつ、さらに最適なDBパフォーマンスの可視化戦略を今後考えていきたいと思います。 また、現状一部のDBにしか対応できていないため、他のDBに対しても同様の可視化を進めていきたいです。 最後に ZOZOでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! corp.zozo.com