AWS AppConfigの属性機能を使ってフィーチャーフラグをONOFFではなくenum値として管理する
クラウド事業本部サービス開発室の佐藤です。
はじめに
フィーチャーフラグは、コードをデプロイせずに機能のON/OFFを切り替えることができる便利な仕組みです。AWS AppConfigを使えばAWS上でフィーチャーフラグを管理することができます。
しかし、実際の運用では単純なON/OFFだけでは足りないケースがあります。例えば最近の開発時の事例では、レートリミット機能を導入する際、以下のような複数のモードがある段階的なロールアウトが必要になることがありました。
- 無効(disabled): レートリミットを無効化
- カウントのみ(count): レートリミットは適用せず、ログにのみ記録
- 有効(enabled): レートリミットを有効化
この記事では上記の様な課題を解決するために、AWS AppConfigの属性機能を使って、デフォルトのBooleanフラグではなくenum型でフィーチャーフラグを管理する方法を紹介します。実装はKotlinをサンプルにしています。
AppConfig自体の導入方法やAppConfig Agentの導入等は割愛します。今回の構成では、ECSのサイドカーでAppConfig Agentが構成されていることを前提としています。
当初の実装
当初は上記の3値を管理するために、以下のように2つのフラグを組み合わせて機能の状態を管理していました。
{
"ratelimit": {
"enabled": true
},
"ratelimit_countmode": {
"enabled": true
}
}
fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
// レートリミット機能自体のONOFF
if (!featureFlagService.isEnabled("ratelimit")) {
filterChain.doFilter(request, response)
return
}
// レートリミット機能がONのときのモード
if (featureFlagService.isEnabled("ratelimit_countmode")) {
// COUNTモード: ログ出力のみ
} else {
// 通常モード: 429レスポンスを返す
}
}
問題点
- 2つのフラグの組み合わせで3つの状態を表現するのは分かりにくい
- ratelimit=false の場合、ratelimit_countmode の値に関係なく無効になる
- フラグが増えると組み合わせが複雑になる
どうにかAppConfigで解決できないかということで調べてみると、属性という機能があることがわかりました。
AppConfigの属性機能を使った実装
AppConfigの属性機能を利用するにはまずフラグを作成する必要があります。フラグを作成すると、フラグの詳細画面に属性定義という欄があります。

フィーチャーフラグ自体は true or false のBoolean型のみなんですが、属性については以下のようにBoolean以外にも数値や文字列の型が用意されており、柔軟な設定を追加することができます。今回はenum値で管理したいので文字列型にして、制約に列挙型(enabled, disabled, count)として定義しました。

AppConfig上でフラグと属性を定義したので、これらをアプリケーション側で取得します。今回はKotlinをベースに記述していますが、他の言語でもほとんど同様にできると思います。
enum型の定義
フィーチャーフラグの状態を表すenumを定義します。
enum class RateLimitMode {
/** レートリミット機能を無効化 */
DISABLED,
/** レートリミット機能を有効化(429を返す) */
ENABLED,
/** 監視モード(カウントのみ、実際の制限はしない) */
COUNT,
;
// AppConfigのenum値からenumクラスにマッピング
companion object {
fun fromString(value: String?): RateLimitMode = when (value?.lowercase()) {
"enabled" -> ENABLED
"count" -> COUNT
"disabled" -> DISABLED
else -> DISABLED
}
}
}
フラグを取得する
今回はECSのサイドカーでAppConfig Agentを起動させているので、フラグを取得するには http://localhost:xxxx//applications/{app名}/environments/{環境名}/configurations/{設定名}?flag={フラグ名} というREST API経由で取得できます。
まず、AppConfigからフラグを取得する関数を作成し
// 属性のenum型フラグ用
data class RateLimitFlag(
val mode: String?,
)
val rateLimitFlagResponseType = object : ParameterizedTypeReference<RateLimitFlag>() {}
val headers = HttpHeaders().apply {
contentType = MediaType.APPLICATION_JSON
}
// AppConfigからREST API経由でフラグを取得
fun getRateLimitMode(): RateLimitMode = try {
val res = restTemplate.exchange(
String.format(APPCONFIG_URL_TEMPLATE, appconfigAgent.endpoint, appconfigAgent.app, appconfigAgent.env, appconfigAgent.profile, "flag=$flagName"),
HttpMethod.GET,
HttpEntity<String>(headers),
responseType,
)
return RateLimitMode.fromString(res.body?.mode)
} catch (e: Exception) {
logger.warn("Failed to get ratelimit flag, defaulting to DISABLED", e)
RateLimitMode.DISABLED
}
}
これをアプリケーションコードから取得するようにして、必要な分岐を書くイメージです。
val rateLimitMode = getRateLimitMode()
when (rateLimitMode) {
RateLimitMode.COUNT -> {
// カウントモード: 429を返さずログとメトリクスで記録
}
RateLimitMode.ENABLED -> {
// 通常モード: 429を返す
}
// 無効
RateLimitMode.DISABLE -> {}
}
こうすることで当初の複数のフラグを管理する方式に比べて、フラグを一つにまとめることができフラグがシンプルになりました。
まとめ
AWS AppConfigの属性機能を使えば、フィーチャーフラグのONOFF以外にも柔軟な設定値を追加することができます。機能に必要な設定値を一緒に管理できるようになるため、シンプルに運用することができます。








