SmartHR のソフトウェアエンジニア ぷりんたい です。SmartHR には2017年2月に入社しました。
この記事は SmartHR 長時間のサービス停止を伴うシステムメンテナンスのお知らせ によせて書かれたものです。
ご挨拶
SmartHR では、昨年の6月より週2日という頻度で夜間のサービス停止を行ってきました。まずは、この運用形態を選択したことによりご利用中のお客様にはご不便をおかけしたことをお詫び申し上げます。
今日のクラウドサービスでは、無停止運用が当たり前といった風潮もありますが、なぜ SmartHR が停止メンテナンス運用を選択したのか、今後のサービス提供においてどのようなことを重視していくのかを技術者としての立場からご説明させて頂きます。
SmartHR の開発初期とマルチテナント問題
SmartHR は2015年2月に開発が始まり、同年11月にサービスインしました。 開発当初から抱える問題として、マイナンバーや給与情報などのセンシティブな個人情報を扱うという特性から、導入企業間でのデータの分離を保証しなければならないという要件があります。 これはマルチテナント SaaS であれば共通で抱えることになる問題ですが、解決の方針は選択したフレームワークやプラットフォーム、開発体制やビジネスの要求に応じて様々あると思います。
Ruby on Rails を採用している SmartHR ではマルチテナント問題に対する初期選定として、 apartment というライブラリを選択しました。
このライブラリは、 MySQL で言う所のデータベース、 PostgreSQL で言う所のスキーマをテナント単位で作成することによって、テナント単位の水平分割を提供してくれます。
導入の結果、無配慮に SELECT * FROM crews;
といったSQLクエリを実行したとしても複数のテナントにまたがったレコードが戻らず、テナントの切り替えはアプリケーションロジックに入る前に apartment が面倒を見てくれる仕組みがあるため、業務ロジックを実装する上で神経質にならずとも良いという大きな利点がありました。
結果として、この選択が開発初期における生産性向上に貢献しましたが、年末調整機能リリースなどにより利用者数が大幅に拡大した2016年下期からとあるリスクを抱えることとなりました。
積み上がった技術的負債
リレーショナルデータベースに詳しい方であればこの時点でお察しが付くかもしれませんが、テナント毎にテーブルを作成することによって、単純にアプリケーション内で利用するテーブル数に利用社数が掛け算となり、2017年1月の時点で1つのRDS ( MySQL / InnoDB ) インスタンスの中に 約3,000データベース / 約30万テーブル が存在することになりました。( AWS RDS の推奨値は 10,000テーブル以内 )
これは単純に次のような影響を及ぼします。
- マイグレーション (テーブル・カラムの変更) に掛かる時間の増加
- アプリケーションのデプロイタイミングによる不具合の発生
- マイグレーション中、テナント毎のスキーマが異なる事による不具合の発生
- マイグレーションが何らかの理由で中断した場合の復旧難易度の増大
- データベースのバックアップ・リストアに掛かる時間の増加
- データベースインスタンスのメモリ不足によるパフォーマンス低下
実は、2017年1月の時点ではこれらはさほど問題となりませんでした。
SmartHR のメイン利用者は企業の人事労務担当者であるため、大量にトラフィックを捌いたり無停止を要求されるものではなく、仮に停止したとしても利用者に莫大な損失や利益の逸失をもたらすものではありませんでした。
仮に、特定のテーブルの変更に100ミリ秒かかるとして、全体で3,000テナントであれば5分でマイグレーションは終了します。最大5分程度の停止であれば許容される、またはそもそも気づかれないといったケースが大半でした。
直面したトレードオフと選択
この状況が大きく変わり始めたのが2017年2月以後となります。開発チームメンバーが順調に増え、複雑度の高い機能開発に取り組み始めたのに合わせて利用社数の伸びも順調に推移した結果、マイグレーションに掛かる時間が増大しました。またサービスの性質上、機能改修においては高い確率でマイグレーションが発生するため徐々に安定性を失いつつありました。
そして2017年5月、先述のリスクが複雑に顕在した結果、SmartHR にとっては初めての大規模(8時間)なサービス障害が発生しました。
当然、これらのリスクに対して2017年頭から当時のSREチームが様々な予防策を検討していましたが、実際に発生した問題がより複雑だったため、根本的な解決には半年以上を見込むといった状況でした。
プロダクトチーム一丸となり障害の予後検討を行った結果、「当面の間は差分にマイグレーションを含む場合にホットデプロイを諦め、夜間の停止メンテナンスを行う」という選択を行いました。
今、当時を振り返っても Rails 5 にて実装された AR::Base.ignored_columns などを活用し、 ALTER TABLE
を適用する順番に気をつけることで、無停止のホットデプロイを継続することは技術的にはできたかもしれません。しかし、社内的にエンジニアのリソースは極めて貴重であり、ホットデプロイを継続する事によって考慮しなければならない問題を解決するために工数を費やすよりは、その工数をプロダクト開発に費やした方が結果としてサービスの向上に繋がるという判断をチームとして行いました。
結果として、今までのリリースサイクルを減速させることなく、2017年下期は従業員数1万人規模の全国展開チェーン店のお客様にもお使い頂けるようなサービスに成長、導入社数も8,700社を突破したことで2017年度は SmartHR の大きな飛躍の年となりました。 (現在は1万社以上にご利用頂いています)
プロダクト課題の再設定
しかしながら、テーブル変更を含むデプロイではデプロイ担当者の時間的・心理的負担が大きいといった状況は不健全ですし、 SmartHR のようにお客様のフィードバックを重要視するサービスでは、継続的デリバリが行えている状態が最も望ましく、利用社数や導入業界の増加に伴い平日日中だけでなく土日深夜にもサービスを使いたいという声も増えてきました。
今後の事業計画も考慮した上で、最終的にこの問題を解決しホットデプロイを再開するためには次のような課題設定が考えられました。
- マイグレーションを含むデプロイを原則無停止で行えること
- 10人程度の小規模なチームでも片手間で 24/365 の運用を実現できること
- 少なくとも直近5年は安定した継続的デリバリが行えるような設計であること
- テナント数が1万から10万、20万と増えたとしても運用に耐えうること
- マルチテナントSaaSに要求されるデータの分離を維持すること
- 現在の1000倍程度のトラフィックやデータ量に耐えうること
- 導入時点での費用対効果に合理性があること
- 移行にあたりサービスの機能開発を長期間止める必要がないこと
これらを解決する手法はなかなか見つからず、いずれかの要素を切り捨てるようなアイデアの実証実験を繰り返していましたが結果は振るわず、CTOの退任後にSREチームは解散となりました。
ソリューションの発見とサービスの今後
解散後も片手間で仮説検証やアーキテクチャの再設計を行っていましたが、納得のいくものにはならないまま時が経ち、年末年始休暇を目前に控えた頃にとあるプロダクトの存在を知ることになりました。
それは、過去 pg_shard という名称で開発されており、現在は Citus という名前のついた水平分割と高いスケーラビリティを提供する PostgreSQL の拡張です。
当然、これら拡張を導入するには基本的にデータベースサーバを自前で運用する必要がありますが、 Citus の開発主体である Citus社では、 Citus Cloud という Database as a Service を提供しており、フルマネージドで運用することが可能です。(AWS東京リージョンにホストすることも可能)
また、 apartment の代替として activerecord-multi-tenant も Citus 社によって提供され、 apartment に似た感覚でデータの分離を行えることもわかり、実際のアプリケーションを用いて検証した結果、これを活用することで上記要件を満たせるという判断に至り、導入を決定しました。
移行に際しては、データベースエンジンの変更 ( MySQL -> PostgreSQL ) やPKの変更 ( 連番 -> UUIDv4 ) などの問題があるため、万全を期すためにも2日半の停止メンテが必要と判断しました。
今回のメンテナンスを行うことによって、ただちに SmartHR の機能が増えたりするわけではありませんが、開発者の負担が減り、デプロイの回数を増やすことで今まで以上に細かなフィードバックループを回せるようになり、よりお客様の声を取り込みやすい開発体制に進化することができると考えています。
今後ともお客様のご理解とご協力と共に SmartHR というサービスを発展させて行きたいと思っています。何卒よろしくお願いいたします。
【PR】エンジニア絶賛採用中
プロダクトの力で世の中のバックオフィス業務を一緒に変えていきませんか? SmartHR 社では急成長するマルチテナント SaaS が抱える課題をチームで解決していけるソフトウェアエンジニアを募集しています。
いきなり応募では腰が引けるという方は、気軽に 私のTwitter までメッセージを下さい。頂いた質問には真摯にお答えいたします。(DM全体解放してます)