Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

feature flag と OpenTelemetry

feature flag と OpenTelemetry

OpenTelemetry Meetup #3 2024-06 登壇資料

https://opentelemetry.connpass.com/event/317170/

Shota Iwami

June 24, 2024
Tweet

More Decks by Shota Iwami

Other Decks in Technology

Transcript

  1. 自己 紹介 岩 見 彰太 / Iwamin 株式会社サイバーエージェント ೥౓৽ଔೖࣾ "*ࣄۀຊ෦ڠۀϦςʔϧϝσΟΞ%JW

    アプリ運 用 カンパニー @BIwashi @B_Sardine 'FBUVSF'MBH%FFQ%JWF ΦϒβʔόϏϦςΟݚम࣮ફฤ 0QFO'FBUVSFͱࣗಈੜ੒Λ׆༻ͨ͠ϑΟʔνϟʔϑϥάͷ એݴతू໿؅ཧ The Go gopher was designed by Renée French. GO Feature Flag
  2. 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テスト リスク軽減 カナリアリリース ダークカナリアリリース
  3. feature fl ag は複雑である • 恩恵は多くあるが、feature fl ag は実装を複雑化させてしまう コード上に

    if 文 に溢れてしまう これはアーキテクチャや実装の問題でもある 処理を追うのがどんどん 大 変になってしまう • feature fl ag はモニタリングやオブザーバビリティも複雑化させる 今まではデプロイ単位で 見 ればよかった リリースの tag や version、commit hash etc … feature fl ag で実装した部分に到達するタイミングが 非 常に複雑 動的なフラグだと到達して実 行 時に初めてわかる 分岐が 大 量にあるとテストが実質不可能 枝葉の数だけテストパターンが増えていく
  4. 次の問いに答えられますか? パーセント単位で ON/OFF を切り替える設定をしました それは本当に指定した割合になってますか? エラーが発 生 した trace での

    feature fl ag の値は? デプロイタイミングとは別タイミングでエラーレートが上がりました どの feature fl ag が原因かわかりますか? カナリアリリースした割合っぽい割合でエラーが発 生 していそうです 本当にカナリアリリースが原因ですか?
  5. feature fl ag のオブザーバビリティ • 「 fl ag の値を変えた時間からエラーが増えている」には根拠がない fl

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

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

    のレイテンシー エラーになっていないか
  8. • 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
  9. • 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 取得
  10. • 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$/$'
  11. • ベンダーロックインを避けられる アプリケーションの実装部分が変わらない 好きな FFSaaS を気軽に乗り換えられる 複数のツールを使うこともできる • 環境や種類によって FFSaaS

    を使い分けられる 環境 dev、stg では頻繁に fl ag を 入 れ替えるため、便利さのために FFSaaS を使 用 するが、本番環境ではリードタイムを許容できるのと 金 銭 面 から環境変数によって更新する 種類 ターゲティングがなく、特に context 情報などを必要としない Release Toggle などは環境変数を使 用 特定の条件の 人 に出し分ける AB テストや限定公開の条件をある程度 長 期的に更新する必要があるような Experiment Toggle や Permission Toggle などでは FFSaaS を使う • feature fl ag の実装箇所は変えることなく Provider を差し替えるのみ OpenFeature のなにが嬉しいか 1SPWJEFSTc0QFO'FBUVSF
  12. OpenFeature のなにが嬉しいか OpenFeature: 標準 API を定義し共通の SDK を提供 ベンダー: プロバイダーの開発に集中

    アプリケーション開発: 共通の SDK を使 用 して実装し Provider を付け替えるだけ OpenFeature - a standard for feature fl agging | OpenFeature
  13. 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
  14. APM

  15. • span 中の feature fl ag event が網羅 的に 見

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

    events の配列扱いにくい 一 つの関数ないし span に複数の fl ag 分岐があると処理も追いづらい feature fl ag は 関数/span単位で 切り出すべき
  17. アーキテクチャ • 一 つの関数ないし span に複数の fl ag 分岐があると、その経路の処理を追う のが困難かつ

    span に対してフィルターもかけにくい • fl ag を評価する場所を集約して、意味ある単位で関数を分離すべき → DI 部分で fl ag を評価して切り替えられるようにする • 関数実装部分はもちろんのこと、全体のアーキテクチャが抽象化されており 疎結合であることが前提 より良い feature fl ag × o 11 y を実現しようと思うと、アーキテクチャからテコ 入 れする 必要がある
  18. 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) }
  19. OTLP で feature fl ag SaaS に送る • LaunchDarkly などは

    OTLP を送ることができる • feature fl ag を利 用 してカナリアリリースする際、メトリクスを使 用 するこ とでプログレッシブデリバリーが可能 • LaunchDarkly でもまだ Early Access 限定 まだあんまり成熟していない OLTP を送れる FFSaaS もまだほとんどない 4FOEJOH0QFO5FMFNFUSZUSBDFTUP-BVODI%BSLMZ
  20. まとめ • 「 大 いなる 力 には、 大 いなる責任が伴う※」といわれているように、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