Goで書いたAPIにOpenFeatureを入れたい
まだフィーチャーフラグがなく、メンテナンス? ガハハ無言でコンテナシャットダウンだ、というフラグのフの字も無いGo net/http製のAPIに OpenFeature(Go Feature Flag)を導入して、簡単に設定を切り替えできる基盤を用意したいというスクラップです。
元ネタは半年以上前にはてなブックマークで話題になっていたこの記事です。
今使っているAPIでは OpenAPI / OpenTelemtryを既に導入しているので、もっとOpenなものを入れて、色々楽に設定できるようにしようってのがモチベーションです。
目標は WebUIから 特定のAPIを一瞬でメンテナンスモードと切り替えできるようにすること。(キルスイッチ的なもの)
不正アクセスされたら即死なので、WebUIを使うのは、直感的にセキュリティ上よろしくない気がしますが個人開発・ほぼ個人利用のAPIで使っているので別にいいかなと...
フィーチャーフラグとは
機能の有効無効を切り替えするフラグ的なやつのこと。バグが見つかってヤバいとなったときに、サーバー自体を止めずに、フラグの変更だけで特定のエンドポイントを無効化するというのが主なユースケースだと思う。
OpenFeatureとは
フィーチャーフラグの標準規格、ベンダーに依存しないので、好きなベンダーを選んで、そこの値をフィーチャーフラグとして扱ったりできるらしい。
(下記から非常に大雑把に抜粋)
フラグを受け取る側(client)に使うもの
github.com/thomaspoignant/go-feature-flag
前例があることから、というよりたぶんこれ以外になさそう?
フラグを提供する側(provider)に使うもの
flagsmith
feature flag provider saas
と検索して一番上でおすすめされていたもの。
コード内を openfeature
と検索したところ go用のproviderライブラリが提供されていたので、使えそうと判断。SaaSでもオンプレでも行けるので、とりあえずこれを試す。
SaaSとしてのサイトを見ると無料プランがあったので無料枠を試そうと思う。
無料枠では 1ヶ月あたり 50000回
という呼び出し回数制限があるので、
1日あたり 1612回
/ 1時間あたり 67回
/ 1分あたり1.7回
までのリクエストに制限する。
FlagSmithのアカウントを作り チュートリアルにしたがってフラグを定義してみた。
どうやらFlagSmithのフラグは 特定の条件のユーザーにだけ 機能を提供する、のようなトラフィックのN%だけ有効にするみたいな カナリアリリース等のユースケースを想定していそう。今回はAPIサーバーの設定変更にだけ使うので、その機能は無視する。(どのみち無料枠では呼び出し回数枠を使い切ってしまう
(本筋から脱線だが) 色々連携して、フラグの変更ログを送ったりできるらしい。Discordに送りたかったのだけど、このSlack連携はSlack認証だったためDiscordのSlack互換Webhookは使えなかった、残念。
実装を始めようと思ったが用語に混乱したのでまとめる
OpenFeatureとは
前述の通り標準規格
GoFeatureFlagとは
OpenFeatureを実装するGoライブラリ。GoOpenFeatureでだけ使えるファイルフォーマット等のエコシステムがある。便利でこの仕様をGoだけに限定するのはクローズドだということで、GoFeatureFlag RelayProxy
を経由して、色々な言語に対応するライブラリが派生している。(Goでの実装以外は、OpenFeature.dev で書かれている純粋なOpenFeatureの公式ライブラリとは別物になっている...)
GoFeatureFlagのドキュメントで言うProviders
単に提供しているSDKバインディング一覧を指す。(どういう意図でProviderなんだろう...?)
現状サーバー側Go/Java/node/Python/.NET
クライアント側Kotlin/JavaScript
に対応している。
GoFeatureFlagのドキュメントで言うRelayProxy
フィーチャーフラグの値の提供元は複数のSaaSやファイルにまたがる可能性があるので、それらを一元的に管理できたらいいよねということでできた GoOpenFeatureで使えるバックエンドAPI。使わずに、プロバイダー情報を直接値を読み出す箇所にハードコードすることもできるが、このAPIを経由することが推奨されている。
OpenFeatureの言うProviders
フィーチャーフラグの値を保存しておりOpenFeature規格で提供してくれるライブラリ一覧
GoFeatureFlagでRelayProxyが推奨されているので、今回はGoFeatureFlag+go-feature-flag relay proxy+FlagSmithにしてみる。RelayProxyそれ自体のサポートは S3/Github/Gitlab/File/HTTP/GoogleStorage/KubernetesConfigMap
と個人的に見慣れない顔ぶれだが、HTTP
で FlagSmithのAPI `を叩いて値を持ってくることができるようなのでそれを使おうと思う。
FlagSmithはOpenFeature向けのGoバインディングとAPI仕様(GET /api/v1/flags/を見る限り、元あったAPIを後付けでOpenFeature対応にしているので、Relayproxyでは使えないようだった。
追加で参考記事出てきたので載せとく。
どうも無料枠とか、レイテンシーとか見てみたらDevCycleが良いかもしれないのでDevCycleも試してみる。
DevCycleを試してみた。
このサービスでは Variablesを定義してそれを束ねるFeatureを定義し、Feature単位でどのフラグに何を指定するかを選び、どんなユーザーにその値を提供するかを選べるらしい。
- メリット
- たぶん対象ユーザーグループの設定が細かくできそう
- たぶん前評判によればAPIレスポンスが早い
- デメリット
- フラグを変える画面の導線が遠い
- ダッシュボードの遷移がなぜかかなり重い
今回の自分の用途はサーバーサイドの機能切り替えのみで、カナリアリリースやA/Bテストをするつもりがサラサラ無い。なので、トップページ開いたら即フラグ切替画面が出てくれるシンプルさを持つFlagSmithの方にしようと思う。
RelayProxyを使いたかったが、提供されているのは前述の通りOpenFeature規格であり、GoFeatureFlag規格でないので
https://github.com/open-feature/go-sdk-contrib/tree/main/providers/flagsmith を直接 GoAPIの依存関係に挿入して使うことにする。
GoFeatureFlag != OpenFeatureなので GoFeatureFlagを使わなくとも github.com/open-feature/go-sdk/openfeature
さえあれば、OpenFeatureフォーマットの値は取れそう? GoFeatureFlagの役割がやや分かりづらい。ラッピングしてキャッシュとか取ってる?
SaaS群は OpenFeatureには対応していても GoFeatureFlagには対応していない気がする...
GoFeatureFlagを使わずSDKだけ使ったほうが早いのでは...?
GoFeatureFlagライブラリはOpenFeature規格ができる前からあって、それを様々な開発言語に対応させた後にOpenFeature規格に対応させたっぽい?
FlagSmithのEdgeProxyを使ってローカルキャッシュをして、GoFeatureFlagは完全に依存関係から消してなかったことにした方が話がシンプルそう
別のフィーチャーフラグ系SaaSも同様にEdgeProxyがあるらしい
EdgeProxyを設定するのが面倒なので、OSS版のFlagSmithのサーバーを立てて自前で動かすことにする。こちらなら呼び出し回数制限を無視できる。
結局 FlagSmith(セルフホスト) とopen-feature/go-sdk だけを使い、GoFeatureFlagを使わずになんとかした。若干レスポンスが遅くなるので、キャッシュ機構を入れた。これでいい感じになったので、とりあえずこれでクローズ。(別途Qiitaに記事を書きます。)