Slide 1

Slide 1 text

FastlyとTypeScriptで 実現する カナリアリリースと A / Bテスト Sho Miyamoto (日本経済新聞社)

Slide 2

Slide 2 text

- 名前 Sho Miyamoto - 所属 日本経済新聞社 - 領域 - web - front-end - {client,server,edge}-side - GitHub https://github.com/shqld 自己紹介 2

Slide 3

Slide 3 text

⚠ 今日の発表では実装の詳細に (なるべく)深入りしないようにします 3 代わりに、発表後に公開されるこの資料をご覧いただけると嬉しいです

Slide 4

Slide 4 text

(ダーク)カナリアリリースを ご存知でしょうか? 4

Slide 5

Slide 5 text

カナリアリリース 5 - 一部のユーザだけに新機 能を見せる - ランダムに絞ることが多い - 問題がなければ、その後 100%のユーザに展開

Slide 6

Slide 6 text

- 開発者・社内のユーザな ど、特定の条件に合う ユーザだけに新機能を見 せる - 本番環境での確認やQA を通した上でユーザへ展 開 ダークカナリアリリース 6

Slide 7

Slide 7 text

- 絞った数のユーザだけで、画面やシステムへの影響度を見ることができる - 問題があったときに全体に波及させない - ダークカナリアリリースの場合、そもそもユーザには見えないのでより安全 - 公開前の機能・ページを予め本番に出しておいて社内で確認、指定日時に自動リ リース、ということも可能 カナリアリリースの利点 7

Slide 8

Slide 8 text

A / B テスト 8 - いわずとしれた 分析手法

Slide 9

Slide 9 text

Feature Toggles で 実現できる 9

Slide 10

Slide 10 text

Feature Toggles 10

Slide 11

Slide 11 text

Feature Toggles 11 https://laptrinhx.com/1-minute-feature-toggle-1686812105/ - 機能のオンオフ状態を示す - イメージはスイッチボタン

Slide 12

Slide 12 text

- 実際にコード内で行っているのは分 岐を作ることだけ - コードを書き換えるのではなく、分 岐させる - 分岐先はランタイムで判定 Feature Toggles 12

Slide 13

Slide 13 text

Feature Toggles - ここに全てが記されている - Feature Toggles (aka Feature Flags) by Martin Fowler 13

Slide 14

Slide 14 text

14 ポイント リリースとデプロイの タイミングをずらす → ユーザからの見え方や挙動を より柔軟にコントロールできる

Slide 15

Slide 15 text

- 機能リリースが安全になる - 一度本番で確認してから全体へリリー ス、ということができる - QAテストが容易になる - 他部署・非エンジニアでも、スイッチを 切り替えるだけで検証できる 15 Feature Toggles の利点 - 非常時の対応が素早い - 何かあればスイッチを切り戻すだけで デプロイせずに対応できる - 非機能要件の検証がしやすい - パフォーマンス施策の効果検証など

Slide 16

Slide 16 text

16 問題があったときに、 いかに素早く切り戻せるかも重要 Toggle経由ならデプロイも待たずに即反映できる

Slide 17

Slide 17 text

電子版と Feature Toggles 17

Slide 18

Slide 18 text

- 新しい機能やページは基本的に ダークカナリアリリース - 実際に開発者が本番で確認してから 全てのユーザに展開 - 指定の日時に自動リリース - e.g. 大統領選の始まる時間に合わせ て特設ページをリリース 18 Feature Toggles の用途 - マーケティング施策のA/Bテストを 行う - e.g. ペイウォール(有料登録動線)の 文言変更 - 技術的な施策の効果検証を行う - e.g. Dynamic Critical CSSを本番に反 映しても問題ないか検証、確認が取れ るまでは反映しない

Slide 19

Slide 19 text

- JSONで管理 - defaultValue を決めて おく - A / B テストを行うときは candidates を定義 実際の Toggle 定義 19

Slide 20

Slide 20 text

- 基本的に boolean なの で、ただの分岐として書く だけ ダーク カナリアリリース 20

Slide 21

Slide 21 text

- カナリアリリースのときと 特に変わらない - この例ではコンポーネント ごと出し分けている - これらのTogglesは全て 計測システムに送ってい るので、この機能を見た ユーザを判別できる A / B テスト 21

Slide 22

Slide 22 text

22 社内の非開発者でも簡単に オンオフできるUIもある

Slide 23

Slide 23 text

Feature Toggles の 実装 23

Slide 24

Slide 24 text

Default / Static - 既定値 - Toggle の定義時に人間が決定 - リリース用 - 例 - 「このToggleはもうリリースできるのでオン」 Override / Dynamic - 上書き値 - 定義された条件から機械的に決定 - スケジュール用、セグメンテーション用 - 例 - 「11/25になったからオン」 - 「このユーザのセグメントが10%内だからオ ン」 24 Toggle の2つの側面

Slide 25

Slide 25 text

25 Toggle の2つの側面

Slide 26

Slide 26 text

26 Feature Toggles の材料

Slide 27

Slide 27 text

基盤の構成 27

Slide 28

Slide 28 text

- dynamic toggles の計算のため に別のサーバを必要としている - 運用・ケアが必要 - Toggles サーバにリクエストする ために VCL内で restart している - restart - static toggles の同期のために サービス側で5分おきにAPIへ ポーリングしている - メモリに暗黙的な状態が存 在する 28

Slide 29

Slide 29 text

- HTTPヘッダに Toggles を詰めて オリジンへリクエスト - キャッシュが混らないように`Vary` ヘッダを付けてレスポンス メッセージの やりとり 29

Slide 30

Slide 30 text

旧基盤 (Nikkei-Flags) についての詳しい解説はFastlyの記事で How to solve anything in VCL, part 3: authentication and feature flags at the edge https://www.fastly.com/blog/how-solve-anything-vcl-part-3-authentication-and- feature-flags-edge 30

Slide 31

Slide 31 text

Feature Toggles システムの リプレース 31

Slide 32

Slide 32 text

リプレースの要件・ゴール (1) 32 - インフラ - 状態や計算も全てVCLで完結 - 完全サーバーレス - 転送量 - 各リクエスト毎に送られるデータはなるべく小さく - できるものは全て静的に解決しておく - 複雑性 - 暗黙的・非透過的な状態はなるべく排除 - 「いま何がメモリにあって何がどうなっているのか」という懸念がない

Slide 33

Slide 33 text

- 運用性 - 更新反映が速い - 終了した・不要な toggle の分岐がコードに残らない - Toggleの定義 - 定義が簡潔 - 個々の存在目的が明確 リプレースの要件・ゴール (2) 33

Slide 34

Slide 34 text

34 まずToggleの概念を再考した

Slide 35

Slide 35 text

35 https://martinfowler.com/articles/feature-toggles.html

Slide 36

Slide 36 text

- Toggleの種類は4つ - Release: (ダーク)カナリーリリース - Experiment: ダークローンチやA/Bテスト - Ops: 状況に応じてインフラなどを切り替える - Permission 権限別に処理をスイッチ - → 本当に必要なのは Release と Experiment - それ以外はどちらも Longevity(寿命)が長く消しづらい - Opsはユースケースが限定的 で頻度が少ない - Permissionはアプリケーションの一部として 依存されてしまう可能性が高い Toggle 再考 36

Slide 37

Slide 37 text

- 存在意義・目的が明確 - ある機能をスイッチ(オン /オフ)するだけ - → 値は True / False の2値のみ - 1つのToggleに複数の用途を持たせない - 寿命が短く疎結合 - 短い役目を終えたら すぐに消せる - コードから剥がしやすい - 存在が透過的 - Toggleの存在を前提としない - Toggleにアプリケーションの 実装が依存しない あるべき理想のToggle 37

Slide 38

Slide 38 text

38 色々な議論・紆余曲折あったが... 最終的にできたもの (設計に半年以上かかった)

Slide 39

Slide 39 text

39 Feature Toggles の材料

Slide 40

Slide 40 text

新基盤の構成 40

Slide 41

Slide 41 text

- エッジで dynamic toggles の計 算を行う - サーバレス、VCLで完結 - restart / polling も消えた - 最初から bereq に toggles が入っている - 各bereqごとに CDN へリクエスト して static toggles を取得 - 常に最新の toggles を同 期できる - 暗黙的な状態を持たない 41

Slide 42

Slide 42 text

- 誰にでも書ける簡単・簡潔な インターフェース - 名前と概要、各タイプのオプション を記述 - JSONとは異なり、オプションの チェックや補完が効く トグルの定義 42

Slide 43

Slide 43 text

- Fastify の `req` オブジェクトに APIを生やす - TypeScriptで実装 - できるだけ型で縛れるように型推 論多め - 返る値は必ずBoolean アプリケーション コード 43 (文字列の位置が下がっているのはVSCodeのプラグインの関係)

Slide 44

Slide 44 text

デプロイフロー - Toggles を別のリポジトリの切り 出した - npmの`prepublishOnly` フックでデプロイが実行さ れる - npm publishとVCLのdeployが終 わったあとに Fastly API でソフト パージ - toggles 周りのVCLは VCL Snippets として作っており、本体 のVCLとは別軸・別レポでデプロ イが可能 44

Slide 45

Slide 45 text

45 VCL Snippets - メインとなるCustom VCLと は別サイクルで更新ができ る - サブルーチン単位でデプロ イ可能 - https://docs.fastly.com/en/g uides/using-dynamic-vcl-sn ippets

Slide 46

Slide 46 text

46 実装上の各ポイント

Slide 47

Slide 47 text

✏ Toggles API の処理を VCLで実装 47

Slide 48

Slide 48 text

- JSで定義されたToggleを読んで、 dynamic toggles 用のVCLを生 成している - Scheduling - リクエストされた時間と定 義ルールの時間を比較し てオンオフを決める - Segmentation - 何かしらのリクエストIDを 使いユーザのセグメントを 決定し定義ルールの範囲 と比較してオンオフを決め る 48

Slide 49

Slide 49 text

- 上の定義から、下の dynamic toggles 用の条件分岐を生成し ている - セグメンテーションで同じユーザが 複数の Toggle で似たような判定 にならないように、条件範囲に random offset を含めている 49

Slide 50

Slide 50 text

50 ✏ 転送量を抑えた

Slide 51

Slide 51 text

51 Toggleの数や名前によって数が重くなる

Slide 52

Slide 52 text

52 長いキーを4 bytesのハッシュに変換

Slide 53

Slide 53 text

53 ハッシュをToggle定義とマッピング

Slide 54

Slide 54 text

54 リクエストごとに発生するこの部分を減らしたい

Slide 55

Slide 55 text

55 先頭にversionをつけ、toggle コンテンツのversioningをする

Slide 56

Slide 56 text

56 versioningされていることで、static toggles のメモ化ができる

Slide 57

Slide 57 text

57 ✏ TypeScriptで Toggle の取り扱いを安全に

Slide 58

Slide 58 text

58 型で toggle の名前を補完

Slide 59

Slide 59 text

59 消した toggle が使われていたら型エラー

Slide 60

Slide 60 text

60 ‘ xxx‘ で検索できる - 抽象化されているとtoggle がどこで使われているか追 えなくなる - toggle のキーは必ず ‘ ‘ で始まるように強制(それ以 外を弾いている) - string literal タイプなので キーを抽象化できない - ✖ get(‘ ‘ + name)

Slide 61

Slide 61 text

- toggle を生成する関数を経由 - ここで name を string literal タイ プで受け取る - string literal の name を返す Toggle オブジェクトの型引数に 渡して保持 型の推論 61

Slide 62

Slide 62 text

- 集めた toggles を Object.freeze で readonly に - typeof で型に変えた toggles の name を ユニオンとして取り出す 型の推論 (2) 62

Slide 63

Slide 63 text

まとめ

Slide 64

Slide 64 text

- (ダーク)カナリアリリースは便利 - リリースの安全性が高まる - Feature Toggles で実現できることが多い - デプロイとリリースのタイミングをずらすことで色々できる - 電子版では Feature Toggles を(結構)活用している - ダークカナリアリリース、スケジューリング、 A/Bテスト、... まとめ 64

Slide 65

Slide 65 text

まとめ (2) - Feature Toggles の運用を見返してシンプルに - 何でもできてしまう toggles は禁止、制約を強めに - VCLを駆使すれば 外部のサーバは必要ない - VCL Snippets でデプロイフローも簡潔に - TypeScript でアプリケーションコードを安全に - 補完や検索、エラー検知も容易に 65

Slide 66

Slide 66 text

ありがとうございました 66

Slide 67

Slide 67 text

余談: restart_and_you_will_be_fired

Slide 68

Slide 68 text

VCL restart; 68

Slide 69

Slide 69 text

- 人類にはまだ早い - 使わずに済むなら使わない方が良い - これを消したかった、というのも今回のリプレースの動機の大きな一つ - 中々解決できない問題は大体 restart 起因であることが多い(個人の経験上) - 様々なパターンを網羅的に知っておかないと思わぬ事故に繋がってしまう - 一度目のリクエストで( restartまでに)何が行われたのか - どんなHTTP ヘッダが付いてどう影響するのか - 次のリクエストでどうなるか ...etc. - もちろん必要なとき、有用なときはある - e.g. フレンドリーなエラーページに差し替えるとき VCL restart; 69