FastAPIでFeatureFlagを使ってみる
はじめに
トランクベースの開発でCD(デリバリー/デプロイ)を良い感じにする方法がないか調べていたところFeatureFlagを試したくなりました。私が普段使っているWebAPIフレームワークのFastAPIでFeatureFlagを使ってみます。
fastapi-featureflags
FastAPI用に開発されたfastapi-featureflagsを使っていきます。これを使うことでFastAPIのエンドポイントからFeatureFlagの切り替えが出来ます。FeatureFlagの数がN個ある場合、何の工夫もしなければ2のN乗個の環境を用意して検証しますが、管理用のエンドポイントからFeatureFlagを切り替えることができるので一台で幾通りの環境を検証できます。開発用や検証用で費用を抑えたい場合などで活用できます。
とりあえずpipを使ってインストールします。
pip install fastapi-featureflags
実際に触ってみる
まずはFastAPIが関係ないFeatureFlagの例です。
from fastapi_featureflags import FeatureFlags, feature_flag, feature_enabled
FeatureFlags.load_conf_from_dict(
{
"flag1": False,
"flag2": True,
}
)
print("Enabled Features:", FeatureFlags.get_features())
@feature_flag("flag1")
def flag1_enabled():
print("Feature Should be enabled: flag1")
flag1_enabled()
if feature_enabled("flag2"):
print("Feature Should be disabled: flag2")
FeatureFlags.enable_feature("flag1")
flag1_enabled()
$ python demo.py
Enabled Features: {'flag1': False, 'flag2': True}
Feature Should be disabled: flag2
Feature Should be enabled: flag1
1回目のflag1_enabled()
を実行した際はflag1がFalseなので実行されずスキップされました。flag1を有効にして再度flag1_enabled
を実行すると動作しました。
次にFastAPIを使ったFeatureFlagの例です。
from fastapi import FastAPI
from fastapi_featureflags import router as ff_router
from fastapi_featureflags import FeatureFlags
FeatureFlags.load_conf_from_dict(
{
"flag1": False,
"flag2": True,
}
)
app = FastAPI()
@app.get("/")
def enabled_features():
return FeatureFlags.get_features()
app.include_router(ff_router, prefix="/ff", tags=["FeatureFlags"])
$ pip install uvicorn
$ uvicorn fastapi-demo:app
/docs
にアクセスするとSwaggerのUIから以下のAPIエンドポイントが確認できます。
-
/all
: FeatureFlagsの値を取得 -
/enable/{feature_flag}
: {feature_flag}の有効化 -
/disable/{feature_flag}
: {feature_flag}の無効化 -
/reload
: FeatureFlagsを初期状態に戻す
これらのエンドポイントを使ってFeatureFlagの切り替えをしてみます。まずは現状の値の確認から。
$ curl -X 'GET' 'http://localhost:8000/ff/all' -H 'accept: application/json'
{
"flag1": false,
"flag2": true
}
FeatureFlagの有効化と無効化。
$ curl -X 'GET' 'http://localhost:8000/ff/enable/flag1' -H 'accept: application/json'
{
"feature_flag": "flag1",
"enabled": true
}
$ curl -X 'GET' 'http://localhost:8000/ff/disable/flag2' -H 'accept: application/json'
{
"feature_flag": "flag2",
"enabled": false
}
$ curl -X 'GET' 'http://localhost:8000/ff/all' -H 'accept: application/json'
{
"flag1": true,
"flag2": false
}
flag1を有効化、flag2を無効化しました。FeatureFlagsを初期状態に戻すにはreload
のエンドポイントをたたくと出来ます。
$ curl -X 'GET' 'http://localhost:8000/ff/reload' -H 'accept: application/json'
{
"feature_flags": {
"flag1": false,
"flag2": true
},
"reloaded": true
}
$ curl -X 'GET' 'http://localhost:8000/ff/all' -H 'accept: application/json'
{
"flag1": false,
"flag2": true
}
まとめ
FeatureFlagをFastAPIで使ってみました。本番環境で意図しないFeatureFlagの更新を防ぐためにapp.inclucde_router(...)
を本番では実行しないであったり、include_in_schema=False
にしてSwaggerUI上では見えないようにするのが良いかもしれません。
参考
Discussion