Slide 1

Slide 1 text

feature fl ag と OpenTelemetry 株式会社サイバーエージェント 岩 見 彰太 GitHub:@BIwashi X: @B_Sardine OpenTelemetry Meetup 202 4 - 0 6

Slide 2

Slide 2 text

自己 紹介 岩 見 彰太 / Iwamin 株式会社サイバーエージェント ೥౓৽ଔೖࣾ "*ࣄۀຊ෦ڠۀϦςʔϧϝσΟΞ%JW アプリ運 用 カンパニー @BIwashi @B_Sardine 'FBUVSF'MBH%FFQ%JWF ΦϒβʔόϏϦςΟݚम࣮ફฤ 0QFO'FBUVSFͱࣗಈੜ੒Λ׆༻ͨ͠ϑΟʔνϟʔϑϥάͷ એݴతू໿؅ཧ The Go gopher was designed by Renée French. GO Feature Flag

Slide 3

Slide 3 text

突然ですが

Slide 4

Slide 4 text

feature fl ag 使ってますか?

Slide 5

Slide 5 text

HOGE_FLAG := true if HOGE_FLAG == true { // flag ON ͷ࣌ʹ࣮ߦ͍ͨ͠ॲཧ // ex.) ൓ө͍ͨ͠मਖ਼ // ex.) AB ςετͷ A Λ༗ޮԽ͢Δ // ... } else { // flag OFF or flag Λઃఆ͍ͯ͠ͳ͍࣌ʹ // ࣮ߦ͍ͨ͠ॲཧ // ex.) मਖ਼લͷطଘͷ࣮૷ // ex.) AB ςετͷ B Λ༗ޮԽ͢Δ // ... } feature fl ag The Go gopher was designed by Renée French. GO Feature Flag 開発 生 産性 トランクベース開発 データ駆動 ABテスト リスク軽減 カナリアリリース ダークカナリアリリース

Slide 6

Slide 6 text

feature fl ag は複雑である • 恩恵は多くあるが、feature fl ag は実装を複雑化させてしまう コード上に if 文 に溢れてしまう これはアーキテクチャや実装の問題でもある 処理を追うのがどんどん 大 変になってしまう • feature fl ag はモニタリングやオブザーバビリティも複雑化させる 今まではデプロイ単位で 見 ればよかった リリースの tag や version、commit hash etc … feature fl ag で実装した部分に到達するタイミングが 非 常に複雑 動的なフラグだと到達して実 行 時に初めてわかる 分岐が 大 量にあるとテストが実質不可能 枝葉の数だけテストパターンが増えていく

Slide 7

Slide 7 text

0CTFSWBCMF'FBUVSF3PMMPVUTXJUI0QFO5FMFNFUSZBOE0QFO'FBUVSF%BOJFM%ZMB.JDIBFM#FFNFS:PV5VCF

Slide 8

Slide 8 text

feature fl ag の o 1 1 y を 考えたことはありますか?

Slide 9

Slide 9 text

次の問いに答えられますか? パーセント単位で ON/OFF を切り替える設定をしました それは本当に指定した割合になってますか? エラーが発 生 した trace での feature fl ag の値は? デプロイタイミングとは別タイミングでエラーレートが上がりました どの feature fl ag が原因かわかりますか? カナリアリリースした割合っぽい割合でエラーが発 生 していそうです 本当にカナリアリリースが原因ですか?

Slide 10

Slide 10 text

feature fl ag のオブザーバビリティ • 「 fl ag の値を変えた時間からエラーが増えている」には根拠がない fl ag が原因であるということを特定したい • 複雑なシステムは実質網羅的にテストすることはほぼ不可能 モニタリング、オブザーバビリティをちゃんとしておく必要がある • 監視は問題検知 ・ 特定だけではなく、ABテストや実験、カナリアリリースな どにも使 用 できる ex. アルゴリズムを変更するような実装をした場合、実際に改善されたか

Slide 11

Slide 11 text

feature fl ag の アーキテクチャ

Slide 12

Slide 12 text

• feature fl ag の状態をリモート管理してくれる • feature fl ag の状態を管理する 方 法は 色 々ある 環境変数 json などの静的ファイル • feature fl ag の値を管理し、リアルタイムでの変更や context 情報から適切に値を決定す るなどを実現するサービスが存在 • これらは基本的にSDK(API)になっており、 fl ag 名と contex 情報(ターゲティングなど をする際のユーザー情報やリクエスト情報など)を渡して、その結果として評価値を返す フラグ管理システム(以下FFSaaS)

Slide 13

Slide 13 text

feature fl ag のアーキテクチャ

Slide 14

Slide 14 text

feature fl ag の Signal

Slide 15

Slide 15 text

feature fl ag の Signal

Slide 16

Slide 16 text

feature fl ag の Signal fl ag の評価値の結果

Slide 17

Slide 17 text

feature fl ag の Signal fl ag の評価値の結果 fl ag のレイテンシー

Slide 18

Slide 18 text

feature fl ag の Signal fl ag の評価値の結果 fl ag のレイテンシー エラーになっていないか

Slide 19

Slide 19 text

どうやって計装するか

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

これの課題

Slide 22

Slide 22 text

• SDK の実装は各サービス違う 変更したくなった場合には使 用 している実装部分 を全て修正する必要 • 開発のフェーズやニーズによって速度や求 められる機能が異なる 置き換えたくなる需要がそこそこ発 生 する可能性 • 移 行 が 大 変 置き換えした際にデグレする可能性もある • 複数兼 用 する可能性もある 複数の SDK を使いこなす必要がある 依存などがあった場合は 大 変 ベンダーロックイン import configcat "github.com/configcat/go-sdk/v9" client := configcat.NewClient("#YOUR-SDK-KEY#") enabled := client.GetBoolValue("flagName", false, nil) if enabled { doTheNewThing() } $PO fi H$BU import flagsmith "github.com/Flagsmith/flagsmith-go-client/v3" client := flagsmith.NewClient(os.Getenv("#YOUR-SDK-KEY#")) flags, _ := client.GetEnvironmentFlags(ctx) enabled, _ := flags.IsFeatureEnabled("flagName") if enabled { doTheNewThing() } 'MBHTNJUI

Slide 23

Slide 23 text

• SDK の実装は各サービス違う 変更したくなった場合には使 用 している実装部分 を全て修正する必要 • 開発のフェーズやニーズによって速度や求 められる機能が異なる 置き換えたくなる需要がそこそこ発 生 する可能性 • 移 行 が 大 変 置き換えした際にデグレする可能性もある • 複数兼 用 する可能性もある 複数の SDK を使いこなす必要がある 依存などがあった場合は 大 変 ベンダーロックイン import configcat "github.com/configcat/go-sdk/v9" client := configcat.NewClient("#YOUR-SDK-KEY#") enabled := client.GetBoolValue("flagName", false, nil) if enabled { doTheNewThing() } $PO fi H$BU import flagsmith "github.com/Flagsmith/flagsmith-go-client/v3" client := flagsmith.NewClient(os.Getenv("#YOUR-SDK-KEY#")) flags, _ := client.GetEnvironmentFlags(ctx) enabled, _ := flags.IsFeatureEnabled("flagName") if enabled { doTheNewThing() } 'MBHTNJUI GetBoolValue という関数で bool を取得 GetEnvironmentFlags という関数で fl ags を取得 IsFeatureEnabled という関数で bool 取得

Slide 24

Slide 24 text

• 2022年5 月 に公開された 比 較的新しい OSS CNCF の Incubating Project にも採択 • feature fl ag 管理のオープン標準規格 • 直接的な feature fl ag を管理する OSS ではなくあくまで抽象化層 • Provider を介して、対応している feature fl ag service と繋ぐことができる OpenFeature Provider • Provider の interface を実装すると任意の custom provider を作れる ex. 環境変数、S 3 、 自 前サーバー etc … • Dynatrace が主導している OpenFeature とは 0QFO'FBUVSFc$/$'

Slide 25

Slide 25 text

• ベンダーロックインを避けられる アプリケーションの実装部分が変わらない 好きな FFSaaS を気軽に乗り換えられる 複数のツールを使うこともできる • 環境や種類によって FFSaaS を使い分けられる 環境 dev、stg では頻繁に fl ag を 入 れ替えるため、便利さのために FFSaaS を使 用 するが、本番環境ではリードタイムを許容できるのと 金 銭 面 から環境変数によって更新する 種類 ターゲティングがなく、特に context 情報などを必要としない Release Toggle などは環境変数を使 用 特定の条件の 人 に出し分ける AB テストや限定公開の条件をある程度 長 期的に更新する必要があるような Experiment Toggle や Permission Toggle などでは FFSaaS を使う • feature fl ag の実装箇所は変えることなく Provider を差し替えるのみ OpenFeature のなにが嬉しいか 1SPWJEFSTc0QFO'FBUVSF

Slide 26

Slide 26 text

OpenFeature のなにが嬉しいか OpenFeature: 標準 API を定義し共通の SDK を提供 ベンダー: プロバイダーの開発に集中 アプリケーション開発: 共通の SDK を使 用 して実装し Provider を付け替えるだけ OpenFeature - a standard for feature fl agging | OpenFeature

Slide 27

Slide 27 text

CloudNative Days Summer 2 02 4 の資料で詳しめに解説しています OpenFeature について詳しく知りたい 方 0QFO'FBUVSFͱࣗಈੜ੒Λ׆༻ͨ͠ϑΟʔνϟʔϑϥάͷએݴతू໿؅ཧ

Slide 28

Slide 28 text

OpenTelemetry と思想が似てる

Slide 29

Slide 29 text

OpenTelemetry と思想が似てる Hooks

Slide 30

Slide 30 text

)PPLTc0QFO'FBUVSF • フラグ評価サイクルの明確に定義されたポイントで任意の動作を追加できるよ うにするメカニズム • 使 用用 途 コンテキストへのデータ追加 ログ記録 テレメトリ OpenFeature Hooks

Slide 31

Slide 31 text

)PPLTc0QFO'FBUVSF • フラグ評価サイクルの明確に定義されたポイントで任意の動作を追加できるよ うにするメカニズム • 使 用用 途 コンテキストへのデータ追加 ログ記録 テレメトリ OpenFeature Hooks

Slide 32

Slide 32 text

• go-sdk-contrib のような形で提供されている OTel 用 の Hooks が提供されている PQFOGFBUVSFHPTELDPOUSJC$PNNVOJUZNBJOUBJOFE0QFO'FBUVSF1SPWJEFSTBOE)PPLTGPS(P

Slide 33

Slide 33 text

Add OpenFeature Hooks for OpenTelemetry

Slide 34

Slide 34 text

import( ... otel "github.com/open-feature/go-sdk-contrib/hooks/open-telemetry/pkg" of "github.com/open-feature/go-sdk/openfeature" ... ) func Init(name string, ofp FeatureProvider, logger log.Logger) error { client.logger = logger client.ofc = of.NewClient(name) metricsHook, err := otel.NewMetricsHook() if err != nil { return cerror.Wrap(err, "init OpenTelemetry metrics for OpenFeature hook") } client.ofc.AddHooks( metricsHook, otel.NewTracesHook(), ) if err := of.SetNamedProvider(name, ofp); err != nil { return cerror.Wrap(err, "init openfeature") } Add Hooks • MetricsHook メトリクスデータを収集 feature_ fl ag.evaluation_requests_total feature_ fl ag.evaluation_success_total feature_ fl ag.evaluation_error_total feature_ fl ag.evaluation_active_count • TraceHook trace の Events に追加 after と error stage で hook

Slide 35

Slide 35 text

Datadog に送る

Slide 36

Slide 36 text

Datadog に送る OTLPを利 用 して datadog agent に送る

Slide 37

Slide 37 text

Metrics

Slide 38

Slide 38 text

APM

Slide 39

Slide 39 text

APM Events 配列で span 中で発 生 した feature fl ag への event が記録

Slide 40

Slide 40 text

シグナルをどうやって使う & 使いやすくする

Slide 41

Slide 41 text

• span 中の feature fl ag event が網羅 的に 見 れるのは良い • しかし配列なので key-value でフィル ターなどできない • 「特定の feature fl ag がxxxだった場 合にエラーが発 生 している」といった 相関がわかるようになりたい events の配列が扱いにくい

Slide 42

Slide 42 text

span と feature fl ag が 1 : 1 になると嬉しい events の配列扱いにくい 一 つの関数ないし span に複数の fl ag 分岐があると処理も追いづらい feature fl ag は 関数/span単位で 切り出すべき

Slide 43

Slide 43 text

• 1:1 になっていると Facets に登録できるので フィルター検索できる Facets

Slide 44

Slide 44 text

Facets • 1:1 になっていると Facets に登録できるので フィルター検索できる

Slide 45

Slide 45 text

複雑度を下げつつ オブザーバビリティを 実現するアーキテクチャ

Slide 46

Slide 46 text

アーキテクチャ • 一 つの関数ないし span に複数の fl ag 分岐があると、その経路の処理を追う のが困難かつ span に対してフィルターもかけにくい • fl ag を評価する場所を集約して、意味ある単位で関数を分離すべき → DI 部分で fl ag を評価して切り替えられるようにする • 関数実装部分はもちろんのこと、全体のアーキテクチャが抽象化されており 疎結合であることが前提 より良い feature fl ag × o 11 y を実現しようと思うと、アーキテクチャからテコ 入 れする 必要がある

Slide 47

Slide 47 text

Strategy Pattern • DI する場所で fl ag 分岐する • fl ag 評価部分で span を作成 span と fl ag が 1:1 type FlagFunc struct { flagAFunc FlagAFunc } func initFlagFunc(f FlagAFunc) *FlagFunc { return &FlagFunc{ flagAFunc: f, } } func (f *FlagFunc) do(ctx context.Context) { f.flagAFunc.do(ctx) } type FlagAFunc interface { do(context.Context) } type FlagAFalseFunc struct{} func (faf *FlagAFalseFunc) do(ctx context.Context) { // TODO: Implement false logic } 'MBH"GBMTFGVOD type FlagATrueFunc struct{} func (fat *FlagATrueFunc) do(ctx context.Context) { // TODO: Implement true logic } 'MBH"USVFGVOD func do(ctx context.Context) { span, ctx := trace.StartSpan(ctx, "do") defer span.End() var fn FlagAFunc if copenfeature.BooleanValue(ctx, "FlagA") { fn = &FlagATrueFunc{} } else { fn = &FlagAFalseFunc{} } initFlagFunc(fn).do(ctx) }

Slide 48

Slide 48 text

番外編 OTLP を FFSaaS に送る

Slide 49

Slide 49 text

OTLP で feature fl ag SaaS に送る • LaunchDarkly などは OTLP を送ることができる • feature fl ag を利 用 してカナリアリリースする際、メトリクスを使 用 するこ とでプログレッシブデリバリーが可能 • LaunchDarkly でもまだ Early Access 限定 まだあんまり成熟していない OLTP を送れる FFSaaS もまだほとんどない 4FOEJOH0QFO5FMFNFUSZUSBDFTUP-BVODI%BSLMZ

Slide 50

Slide 50 text

まとめ • 「 大 いなる 力 には、 大 いなる責任が伴う※」といわれているように、feature fl ag に は絶 大 な 力 があるが、コードの複雑化などもついてくる • これらを解決するために、feature fl ag を導 入 する際は o 1 1 y もセットで考える必要 がある • しかし feature fl ag × o 11 y 重要性はまだあまり認知されていない 先 日 の KubeCon + CloudNativeCon Europe 2 0 2 4 で OpenFeature のメンテナーが 行 ったセッショ ンが初めてくらい これらを実現するエコシステム周りもまだあまり充実していない • ベストプラクティスなどもまだあまり決まりきっていなさそうなので、 色 々作った り試したりしていきたい ※Feature Observability Semantic Conventions | OpenFeature