こんにちは。Quipper採用担当の鈴木です。今回の記事は、@ravelll による「LaunchDarkly による Feature Management」です!是非、ご覧ください!
昨年の6月に入社した Web Engineer の @ravelll です。令和2年になりましたね。どうですか?
今回は私が以前所属していた新規事業開発を担うチームで Feature Management に利用した LaunchDarkly の紹介と利用事例について書こうと思います。
LaunchDarkly とは
LaunchDarkly 社が運営する Feature Management Platform です。
LaunchDarkly を利用することでカナリアリリースや A/B テストなどを実現する Feature Toggles*1 の仕組みを容易にソフトウェアへ組み込むことができます。
導入した際のコードは、例えば以下のようになります。
これは LaunchDarkly に設定した Feature Flag のうち "enable-something-updated" という名前のものを参照し、評価するコンテキストとなるユーザーを識別するものは user.id 、Flag のデフォルト値は false、ということを表しています。この Flag の値を Web 上のコンソールから変更することができます。
更に、Flag の値は様々な条件を元に出し分けることができます。この条件には、まず #variation
の第2引数に渡した属性を利用することができ、一致や正規表現マッチ、大小関係などの条件付けをすることができます。また全体の 30% のユーザーへ true を返す、といった条件付けも可能です。
Flag の値は必ずしも boolean である必要はなく、String や JSON (!) などの type も選択可能で、値も3つ以上を出し分けることができます。ただし、1つの Flag で複数の type の値を出し分けることはできません。
利用できる環境については、21の言語やプラットフォーム(公式ページ情報)に大して SDK が用意されており*2、Web アプリケーションに限らず多くのソフトウェアから利用することができるでしょう。
より詳しい機能の説明は公式のドキュメントに譲るとして、利用事例の話に移ります。
Quipper での利用事例
今回は、ある Microservices 環境にあるプロダクトで認証を行うバックエンドサービスを移行した際の利用事例を紹介します。
"Web サービス" のようないわゆる "サービス" と Microservices の文脈における "サービス" が混在するとややこしいので、以降では次のように書き分けます。
- Service: Microservices の文脈における Service
- プロダクト: "はてなブログ" や "スタディサプリ" など、いわゆる Web サービス
背景
当時所属していたチームのプロダクトは、以下のような Microservices で構成されていました。
図からも分かる通り、認証については以前から存在していた他のプロダクトが所有する Service を利用して行っていました。これにより、他のプロダクトとの依存が生まれ、ユーザーに関する機能開発を私たちチームの要求に応じて柔軟に行うことが難しくなっていました。また依存先のプロダクトに稼働停止を伴うメンテナンスを行うことがあれば私たちのプロダクトも巻き込まれてしまうという問題もありました。
これらの問題を解消するため、私たちのチームで独自にユーザーに関する処理を責務とする Service (以降この Service を Users Service と呼びます)を開発し、それを用いて認証を行うように Service の移行を行うこととしました。
下の図は移行後のアーキテクチャのイメージです。
移行ツールの検討
人間がせっせとコミットを積み幾月日、ついに Users Service は完成しました。
さて、問題はどのように認証の経路を Users Service を使うように切り替えるか、ということです。
ここでの要求・制限としては以下のような事柄がありました。
- プロダクトは 24/365 でユーザーに使われるものではないが、ユーザーの利用中に問題が発生することは極力避けたい。そのため、Users Service への移行期間中に何か問題が起きた際にすばやく旧経路に戻せるようにしたい
- ユーザーがプロダクトを利用する時間帯に開発者全員がオフィスに居ない可能性があるため、旧環境への切り替えはコードのデプロイや社内向けの管理ツールを使わずに行いたい
- 経路を切り替えるための仕組みを自分たちでゼロから作りメンテナンスしていくことは開発リソースの面から避けたい
- 同僚の @ujihisa 謹製の Ruby 用 Darklaunch モジュール*3は、それがさらに依存する社内ライブラリ gem や、それが必要とするインフラなどの理由から利用しない*4
これらを踏まえていくつかの Feature Toggles を実現するプロダクトを検討しましたが、将来的にユーザーの属性に応じた細かな Feature Management を行いたいという要求を満たすことも後押しし、LaunchDarkly を採用することとなりました。
LaunchDarkly の導入
そうと決まれば早速導入です。
導入対象のアプリケーションは Ruby on Rails 製のアプリケーションで、HTTP Server には Unicorn を利用していました。公式ドキュメントに書かれていますが、ruby の SDK から LaunchDarkly へのコネクションは Unicorn の各 worker ごとに張ることが強く推奨されています。
ところが、CI など一部の環境では Unicorn は利用されないため、Unicorn の設定ファイル内で定義した client を素朴に利用すると実行時やテスト時に client が参照できずエラーとなってしまうことが分かりました。
この問題には、client が存在しない場合は Flag の値を見ずに false を返す処理を挟んだ薄い wrapper module を作成し使うことで対応しました。
Service の移行
移行は以下の手順で行いました。
- これまでの認証処理に加え、Users Service を利用した認証処理を書く。その際、処理の経路を LaunchDarkly で切り替えられるようにする
- 両方の処理経路で期待通りの動作をすることを開発用の環境で確認する
- これまでの認証処理が利用されるように Flag の値を設定し、コードを本番環境にデプロイする
- ユーザーが利用していない時間帯に Flag を値を変更し、Users Service を利用する処理経路で期待する動作をすることを確認する
- ユーザーに Users Service 経由の認証を利用してもらい、問題が起きないことを見守る。万が一問題が起きたらすぐに Flag を更新してこれまでの認証処理の経路に戻す
結果、経路を戻すこともなく、無事に Service を移行することができました🎉
今にして思うと、Users Service 側で問題が発生したら Flag の値を LaunchDarkly の API から更新して処理経路を戻すような仕組みを入れておけばより盤石でしたね。
ちなみに、全ユーザーではなくスタッフ用のアカウントだけを対象に Flag の値を更新すれば時間帯を問わず安全に確認ができたのでは?と思われるかもしれませんが、Feature Toggle したい箇所がログインよりも手前にあるためにユーザーを一意に識別する情報を送信することができず全ユーザーを対象とするしかありませんでした。
まとめ
今回は Feature Management Platform である LaunchDarkly について、その簡単な概要と Quipper での利用事例を紹介しました。
現在は僕が以前に所属していたチームの枠を超えて別のチームでも導入が進んでいるところで、今後の展開にワクワクしているところです。
LaunchDarkly を問わず、他の現場での Feature Management の事例もぜひ知りたいと思っているので、各社知見をお持ちのかたはコソッと(もちろん公然とでも!)共有していただけると嬉しいです。
*1:Feature Toggles については Pete Hodgson 氏が書かれた記事を読むと大枠がつかめるでしょう https://martinfowler.com/articles/feature-toggles.html
*2:github.com/launchdarkly を見ると公式ページのリストにない言語・プラットフォームの SDK やサンプルコードもあります。
*3:ユーザーの ID などを条件に boolean を出し分けるシンプルな Feature Toggles を実現できるツール。社内でヘビーに利用している。
*4:これを拡張して一般化する方法もありましたが、この社内ライブラリはあくまで最初期を支える実用的な試作品という位置づけで、汎用プロダクトを利用して解決する方向に動きたい、という思惑もありました。