Cells におけるフィーチャーフラグの設定
| Status | Authors | Coach | DRIs | Owning Stage | Created |
|---|---|---|---|---|---|
| proposed | @rpereira2 | @nolith, @skarbek | ~devops::platforms | 2024-10-14 |
概要
フィーチャーフラグは、SRE やエンジニアがアプリケーションの動作を迅速に制御する強力なツールです。 また、パーソナライゼーションの向上やゴーツーマーケットツールとして業界で広く活用されています(例: LaunchDarkly)。
現在、単一の本番インスタンスでは、フィーチャーフラグの設定は単一の ChatOps コマンドで実行され、API 呼び出しによって即座に本番環境へロールアウトされます。
複数の Cell が導入されると、各 Cell でエンジニアがフィーチャーフラグを設定することは管理不可能になります。
この ADR の前のイテレーションも参照してください: Cells: Feature Flags
動機
Cells マイルストーンとそのイテレーションの変更に伴い、レガシー Cell のみでフィーチャーフラグを管理し、他の Cell では管理しないという前提はもはや成立しません。
フィーチャーフラグは一般に、リスクが高いと判断される機能や、本番環境への投入準備が整っていない機能を導入するために使用されます。 さらに、インシデント対応のためにフィーチャーフラグが定期的に使用されます。
フィーチャーフラグは、最初の Cell に顧客が入る前に、Cells で使用できる状態にする必要があります。
目標
このドキュメントでは、フィーチャーフラグの設定方法および Cells へのロールアウト方法の概要を説明します。
実装の詳細はこのドキュメントの対象外です。一部のケースでは、設計を実装するためのオプションも記載しています。
- Cells においてエンジニアと SRE がフィーチャーフラグを設定するためのインターフェースを説明する。
- Cells におけるフィーチャーフラグのロールアウト戦略を定義する。
非目標
フィーチャーフラグの他の側面については、Cells に向けて見直しが必要な場合があっても、ここでは議論しません。
- 現在のフィーチャーフラグ状態の可視化。
- フィーチャーフラグのライフサイクル変更(フラグ数の制限、長期間有効なフラグの削減など)。
提案
主要用語
- ChatOps - ChatOps コマンドは、環境のフィーチャーフラグを制御するために使用されます。
- Tissue - Tissue プロジェクトは、Cells における変更の調整に使用されます。
概要
現在の使用状況
ChatOps は現在、フィーチャーフラグの設定に使用されています。ChatOps コマンドは ChatOps プロジェクトのパイプラインをトリガーし、フィーチャーフラグ API を使用して本番環境にフィーチャーフラグを設定します。レガシー Cell のフィーチャーフラグは引き続きこのメカニズムで設定できます。
レガシー Cell と他の Cell で有効化されているフィーチャーフラグのセットは異なる場合があります。
Cells へのフィーチャーフラグのロールアウト
フィーチャーフラグの主な用途のひとつは、新機能のリリースに伴うリスクを低減することです。 フィーチャーフラグは通常、機能をテストする意欲のある小規模な顧客グループ(社内顧客を含む)に対して有効化されます。報告されたバグを修正した後、より大きな顧客グループに対してフィーチャーフラグが有効化されます。このプロセスはフィーチャーフラグが全顧客に有効化され、機能が安定していると判断され、フィーチャーフラグを削除できるようになるまで繰り返されます。
フィーチャーフラグはアプリケーションの動作を迅速に変更するために有用ですが、フィーチャーフラグの組み合わせが多すぎると、予期しない動作を引き起こす相互作用が発生する可能性があります。また、新しい変更がどのように動作するかを予測しにくくなり、インシデント管理をより困難にする可能性もあります。
顧客の小さなグループに対してフィーチャーフラグを有効化できるようにしながら、本番環境で管理可能な数のフィーチャーフラグ設定を維持する必要があります。これを実現するために、Application Deployment with a Cellular Architecture で導入されたリングアーキテクチャを活用できます。
各リングは異なる顧客グループを表します。内側から外側のリングに移動するにつれて、連続する各リングは顧客にとってリスクが低くなるように設計されています。より高いリスクを受け入れ、最新のプロダクトアップデートを望む顧客は内側のリングに配置され、安定性を望む顧客は外側のリングに配置されます。各リングは「リスクの層」と見なせます。本番環境でサポートする必要のあるフィーチャーフラグ設定の数を抑えながら、一度に少数の顧客グループにフィーチャーフラグをロールアウトできるようにするため、フィーチャーフラグは最も内側のリングから順にリングごとにロールアウトできます。
そのリングに影響するアクティブなインシデントがある場合、またはメトリクスがリングの異常を示している場合、フィーチャーフラグ変更のロールアウトは許可されるべきではありません。
Cells でフィーチャーフラグを制御するインターフェース
短期的には、ChatOps を使用して Cells にフィーチャーフラグをロールアウトできます。
ChatOps は Tissue と連携してフィーチャーフラグを Cell に設定できます。ringctl を使用してフィーチャーフラグのゲート値を変更するパッチを作成できます。Tissue の変更管理システムがリングへのフィーチャーフラグの変更をロールアウトします。
フィーチャーフラグアクターのサポート
Cells のフィーチャーフラグは、現在本番環境でサポートされているアクター(プロジェクト、グループ、ユーザー、current_request、:instance)と同じものをサポートします。 特定のアクターに対してフィーチャーフラグを設定する必要がある場合は、すべてのリングでそのアクターに設定する必要があります。これにより、アクターがどの Cell に存在しても、フィーチャーフラグが期待どおりに動作することが保証されます。
さらに、Organization モデルもフィーチャーフラグアクターとして有効化される予定です(https://gitlab.com/gitlab-org/gitlab/-/issues/498238)。
短期
イテレーション 1
ChatOps は、エンジニアが Cells のフィーチャーフラグを変更するためのインターフェースを提供します。
ChatOps は Tissue と通信し、Tissue はすべての Cell にフィーチャーフラグを設定します。
操作の成功または失敗は、ロールアウト Issue へのコメント、およびフィーチャーフラグログプロジェクトの Issue を通じて通知されます。
インシデントによりフィーチャーフラグをオフにする必要がある場合は、すべての Cell でオフにする必要があります。
フィーチャーフラグの変更がロールアウトされるときは、フィーチャーフラグログプロジェクトに Issue を作成します。 また、フィーチャーフラグのロールアウト Issue にコメントを追加します。
フィーチャーフラグの現在の状態が照会されると、Tissue は各 Cell に問い合わせます。
Epic: https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1423
イテレーション 2
ChatOps コマンドは個別の Cell をターゲットにするためのインターフェースを提供します。特定の Cell をターゲットにすることは、その Cell が隔離リングにある場合にのみ許可されます。それ以外の場合、フィーチャーフラグの変更はすべての Cell に伝播される必要があります。
各 Cell のフィーチャーフラグの現在の状態はキャッシュされる必要があります。フィーチャーフラグの現在の状態を照会する際に、毎回すべての Cell に問い合わせる代わりにキャッシュを使用できます。
キャッシュはフィーチャーフラグの変更がロールアウトされるときに更新できます。また、Cell のフィーチャーフラグに手動変更が加えられた場合にキャッシュが更新された状態を維持するために、定期的に更新される必要があります。
Epic: https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1448
長期
フィーチャーフラグの変更のロールアウトはできる限り自動化される必要があります。Tissue によるロールアウトの自動化を可能にするために、各フィーチャーフラグのメトリクスとログを定義する必要があります。 開始者はどのリングまで変更をロールアウトするかを指定します。ロールアウトはリング 0 から始まり、リング 1(レガシー Cell)をスキップし、リング 2 から続きます。
フィーチャーフラグ向けの機能をさらに追加するにつれて、フィーチャーフラグサービスを検討できます。サービスのビルドおよびランニングのコストは、Tissue にフィーチャーフラグ機能を過負荷にするリスクとのメリットと比較検討する必要があります。
ここで説明するロールアウト方法は「リスク低減」タイプのフィーチャーフラグ向けのものです。実験フィーチャーフラグや A/B テスト用フィーチャーフラグなど、他のタイプのフィーチャーフラグに対しては異なるロールアウト機能を提供できます。例えば、A/B テスト用フィーチャーフラグは特定のリングにのみ許可する場合がありますが、この制限はリスク低減タイプには適用されません。
段階的ロールアウト
段階的ロールアウトでは、エンジニアがメトリクスとログのクエリ、およびゲートされている機能が正常に動作しているかどうかを判断するために使用できる閾値を定義する必要があります。
- 開始者はロールアウトをどのリングまで行うかを指定します。
- リングへのロールアウトが完了すると、ロールアウトは一時停止します。一定期間後、FF がロールアウトされたリングがメトリクス(FF メトリクスおよび一般メトリクス)またはログで引き続き正常であることが示されると、次のリングへのロールアウトが継続します。
- リング内の Cell でのフラグ設定が失敗した場合、ロールアウトはそのリングで停止し、ロールアウトを開始したエンジニアに通知されます。
- FF がロールアウトされたリングのメトリクスまたはログが異常を示している場合、ロールアウトは停止されます。
- フィーチャーフラグは、メトリクスまたはログで異常が示された場合にロールバックすることもできます。ロールバックには別の閾値を定義できます。
即時ロールアウト
即時ロールアウトは、インシデント対応のために SRE のみが使用する必要があります。
- 開始者はロールアウトをどのリングまで行うかを指定します。
- リング間に一時停止はありません。メトリクスの内容に関わらずロールアウトは継続されます。
- 任意の Cell でフィーチャーフラグの設定が失敗した場合、開始者に通知されます。
設計と実装の詳細
各 Cell でのフィーチャーフラグの設定
GitLab インスタンスのフィーチャーフラグは、フィーチャーフラグ API または Rails コンソールを通じて設定できます。
| API | Instrumentor |
|---|---|
| Tissue は各 Cell への API 呼び出しを直接トリガーできます。 | Tissue は Instrumentor パイプラインをトリガーしてフィーチャーフラグを設定できます。 |
| Tissue はすべての Cell の管理者トークンへのアクセスが必要です。 | Instrumentor は各 Cell の toolbox pod へのアクセス権を持ち、それを使用してフィーチャーフラグを設定できます。 |
| 管理者トークンはブートストラッピング手順の一部として、Cell 作成時に Vault に追加できます。 | Rails コンソールの起動が伴うため、プロセスが遅くなります。 |
フィーチャーフラグ設定の保存
フィーチャーフラグの設定は現在、各 GitLab インスタンスのデータベースに存在します。変更を容易に追跡・管理できるように、フィーチャーフラグの設定をインフラストラクチャーアズコードとして扱う必要があります。Cells では、フィーチャーフラグの設定をどこかのファイルに保存する必要があります。
- フィーチャーフラグの設定は、テナントモデル内、またはリングごとや Cell ごとの個別のファイルに保存できます。
- テナントモデルの変更は通常 Instrumentor によって適用されます。
- この情報をテナントモデル外に保存すると、Cell に関する情報が 2 つの場所に保存されることになります。ただし、別のファイルをテナントモデルにリンクすることはできます。
- 考慮すべき要因のひとつは、Cell が移動された場合に何が起きるかです。テナントモデル内の情報は Cell とともに移動しますが、フィーチャーフラグの設定を Cell 外に保存している場合は、それも移動する必要があります。
現在のフィーチャーフラグ状態のキャッシング
フィーチャーフラグで最も一般的な操作のひとつは、フィーチャーフラグのゲート値を照会することです。エンジニアがフィーチャーフラグの現在の状態を要求するたびに毎回すべての Cell に問い合わせることは、低速でリソースを消費します。
Tissue は Cell ごとに永続化されたフィーチャーフラグとそのゲート値をキャッシュできます。定期的なインターバルで、Tissue は各 Cell からすべてのフィーチャーフラグとそのゲート値を取得できます。これはリングへのデプロイ後にも実行できます。
代替ソリューション
サードパーティのフィーチャーフラグサービス
フィーチャーフラグサービスは、アプリケーションが Cell ID を認識する必要がないという理想的な形で、私たちのリングアーキテクチャを模倣できる必要があります。
サードパーティサービスをセルフホストするか、SaaS サービスを使用する場合は、インシデント対応に使用するため可用性に強い保証が必要です。
サードパーティサービスへの切り替えは潜在的に大きな取り組みであり、短期または中期的に実現できるものではありません。多くの未知要素を含む Cells マイグレーションの中でフィーチャーフラグ向けにサードパーティサービスという別の未知要素を加えることは、最善の方向性ではないと思われます。この議論は後の段階で再検討できます。
Unleash ベースの GitLab 独自のフィーチャーフラグ製品の使用も検討できます。ただし、機能セットを私たちにとって有用なものにするには大規模な開発が必要です。これにより製品が Cells と gitlab.com に特化しすぎる可能性があります。
プッシュモデルではなくプルモデル
Cell が定期的な間隔でフィーチャーフラグサービス(Tissue)にポーリングして変更を取得するようにできます。これにはアプリケーションが Cell ID を認識する必要があるかもしれません。Cell ID をリクエストに含めることで、フィーチャーフラグサービスがリクエスト元を識別して適切に応答できます。
これはフィーチャーフラグサービスにとってよりリソースを消費する可能性があります。ポーリング頻度によっては、フィーチャーフラグの変更が Cell に伝播するまでの時間と予測可能性が低下することもあります。頻度を上げると FF サービスのリソース使用量も増加します。
レプリケートされたフィーチャーフラグデータベーステーブル
この代替ソリューションは Cells アーキテクチャに関する 2 つの前提に基づいています。
- すべての Cell が同じデータにアクセスできるようにするデータベースレベルのレプリケーションメカニズムを使用します(例: すべての Cell が同じ
plansとplan_limitsデータを持つべきなど)。 - すべてのフィーチャーフラグアクター(ユーザー、プロジェクト、グループ、Organization)はクラスター全体で一意であり、すべての Cell がフィーチャーフラグデータを共有することは問題ありません。
ソリューションの概要:
feature_gatesとfeaturesテーブルをすべての Cell にレプリケートする機能を構築します。- 新しい
Gitlab::Cellアクターを導入します。 Feature.enabled?で、フォールバックとして現在の Cell に対してフィーチャーフラグが有効になっているかどうかを確認します(これにより、Cell 全体でフィーチャーフラグを有効化できるようになります)。- ChatOps で新しい
--cell <cell-id>または--cell <cell-name>オプションを導入します。
メリット:
- ChatOps の
--production flagでは、フィーチャーフラグは引き続き「レガシー Cell」に設定され、フィーチャーフラグデータはデータベースレベルですべての Cell にレプリケートされます(chatops が「必要な情報を収集するためのサービスと通信する機能」を持つ必要がありません)。 - フィーチャーフラグデータはデータベースレベルですべての Cell にレプリケートされるため、クラスターに新しい Cell が導入されても特別なセットアップは不要です。
デメリット:
- Cell ごとの「時間/アクターのパーセンテージ」のサポートには若干多くの変更が必要ですが、不可能ではありません。
- デプロイメントリングごとのフィーチャーフラグ有効化はサポートされていませんが、ChatOps で
--ring <ring-id>とring => [cell リスト]のマッピングを使用してサポートできます。そのため--ring <ring-id>オプションは複数の--cell <cell-id>呼び出しに変換されます。
