Carpe Diem

備忘録

OpenFeature の Provider を使ってみる

概要

OpenFeature の Provider の使い方を以下のチュートリアルに沿って実現してみます。

openfeature.dev

Provider には flagd を使います。

環境

  • Go v1.24.0
  • open-feature/go-sdk v1.14.1
  • flagd v0.11.1

事前準備

まず何もフィーチャーフラグ管理サービスが入っていない状態のWebサーバを用意します。

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

const defaultMessage = "Hello!"

func main() {
    // Initialize Go Gin
    engine := gin.Default()

    // Setup a simple endpoint
    engine.GET("/hello", func(c *gin.Context) {
        c.JSON(http.StatusOK, defaultMessage)
    })

    engine.Run()
}

このサーバにOpenFeatureを追加実装して行きます。

OpenFeatureの導入

Provider 無しの実装

まずは Provider を入れない状態の実装です。

package main

import (
    "context"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/open-feature/go-sdk/openfeature"
)

const defaultMessage = "Hello!"
const newWelcomeMessage = "Hello, welcome to this OpenFeature-enabled website!"

func main() {
    // Iinitialize the OpenFeature client with domain name
    client := openfeature.NewClient("my-domain")

    engine := gin.Default()

    engine.GET("/hello", func(c *gin.Context) {
        // Evaluate welcome-message feature flag
        welcomeMessage, _ := client.BooleanValue(
            context.Background(), "welcome-message", false, openfeature.EvaluationContext{},
        )

        if welcomeMessage {
            c.JSON(http.StatusOK, newWelcomeMessage)
            return
        } else {
            c.JSON(http.StatusOK, defaultMessage)
            return
        }
    })

    engine.Run()
}

ポイントとしては以下です。

  • NewClient()でクライアントの初期化
    • 今回のドメイン名(Provider識別子)は my-domain
  • BooleanValue()でフラグ評価
    • 今回の flag key は welcome-message
    • デフォルトバリアントは false
  • if文で結果をハンドリング

APIを呼び出すと当然ながらデフォルトメッセージが返ります。

$ curl localhost:8080/hello
"Hello!"

flagd Provider を導入

次に flagd という Provider を導入してみます。

flagd とは

flagd はフラグ管理バックエンドシステムで、以下の機能を持っています。

  • リアルタイムでフラグを変更可能
  • 様々なタイプのフラグを定義可能 -ブーリアン、文字列、数値、JSON
  • 特定のユーザーやユーザーの特徴をターゲットにするために、文脈依存のルールを使用可能
  • Fractional Evaluation
  • 新機能のためのプログレッシブ・ロールアウト
  • 複数のソースからフラグ定義を集約
  • 集約されたフラグをgRPCストリームとして公開し、インプロセス・プロバイダで使用可能
  • 設定されたフラグに対してOFREPサービスを公開

UIは提供されていないですが、パフォーマンス面では毎秒数千リクエスト捌けるレベルなので本番でも利用可能です。

Provider 環境の用意

フラグファイル

flagd はJSONリクエストやJSONファイルでフラグを管理できるので、JSONファイルを用意します。

先ほど welcome-message という flag key で扱っていたので、そのkey名でフラグファイルを管理します。

{
  "flags": {
    "welcome-message": {
      "variants": {
        "on": true,
        "off": false
      },
      "state": "ENABLED",
      "defaultVariant": "off"
    }
  }
}

OpenFeature の構成要素やデータモデル - Carpe Diem

を見れば上記JSONのデータモデルのイメージは付くと思います。

docker-compose

flagd サーバを立ち上げるための準備です。

先ほどのJSONファイルを./flags/flagd.jsonに用意します。

それを読み込むようにflagdサーバのdocker-compose.yamlを用意します。

version: '3.8'

services:
  flagd:
    image: ghcr.io/open-feature/flagd:latest
    command: start --uri file:/etc/flags/flagd.json
    ports:
      - "8013:8013"
    volumes:
      - ./flags:/etc/flags/

立ち上げます。

$ docker compose up

実装の修正

実装は簡単で、以下の様にProviderの設定を追記するだけです。

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    flagd "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg"
    "github.com/open-feature/go-sdk/openfeature"
)

func main() {
    err := openfeature.SetNamedProviderAndWait("my-domain", flagd.NewProvider())
    if err != nil {
        log.Fatalf("Failed to set the OpenFeature provider: %v", err)
    }
    // Iinitialize the OpenFeature client with domain name
    client := openfeature.NewClient("my-domain")

ポイントとしては

  • Provider 識別子であるドメイン名を設定する
    • 今回だと my-domain

くらいです。

動作確認

では Provider の動作確認をします。

defaultVariant: offの場合

以前と同様の結果です。

$ curl localhost:8080/hello
"Hello!"

defaultVariant: onの場合

JSONファイルを変更します。

{
  "flags": {
    "welcome-message": {
      "variants": {
        "on": true,
        "off": false
      },
      "state": "ENABLED",
      "defaultVariant": "on"
    }
  }
}

flagd はリアルタイムに変更を検知します。

flagd-1  | 2025-02-16T19:40:39.515Z     info    file/filepath_sync.go:108       filepath event: /etc/flags/flagd.json WRITE     {"component": "sync", "sync": "fileinfo"}

APIを呼び出すと、フラグが変更されてメッセージも変わりました。

$ curl localhost:8080/hello
"Hello, welcome to this OpenFeature-enabled website!

その他

サンプルコード

今回のサンプルコードはこちらです。

github.com

Q&A

リアルタイム変更が検知されているということは都度flagdで評価?

はい、現状の設定ではそのように評価されます。

なので可用性やパフォーマンス面を考慮すると、flagd は軽量なのでサイドカーとして使うのが良いでしょう。

もしくは flagd はin-process評価、つまりフラグ管理システム側での中央集権的な評価でなく、サーバ内での評価(local evaluation)もサポートしているので、それを使うことでネットワーク遅延を発生させず高いパフォーマンスを発揮することが可能です。

まとめ

OpenFeature で Provider を使ったサンプルコードを実装してみました。

今回は flagd を使いましたが、OpenFeature をサポートしていればSaaSやOSSなどどれも使えるので、ベンダーロックインを避けることが可能です。

参考