クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

フレームワークのアップグレードにおけるテスト戦略とリリース戦略

所(@ctokoro_me)です。

弊社のメインサービスであるクラウドワークスフレームワークをRails3からRails4へと移行を行った事に関しての連載です。本記事ではアップグレードに際してのテスト戦略とリリース戦略に関して書きたいと思います。

engineer.crowdworks.jp

テスト戦略

サービスを開始して4年を超えたクラウドワークスのコードベースは、今回行われたフレームワークのバージョンアップのような大規模な変更は未経験でしたが、開発体制として自動テストと継続的インテグレーション(CI) は早い段階から組み込まれており、今回のRails4移行においてもCI上で Rails3/Rails4 のクロスビルドは行われておりました。

しかし、特に直近のクラウドワークスは事業規模もエンジニア数も急拡大した時期であり、それに伴いコード量も10万行を超えコードの成長速度も加速していったものの、自動テストの拡充はコードの成長に追いついているとは言えない状況でした。

f:id:uzuki-first:20160712155203p:plain

フレームワークをアップグレードするような影響範囲の大きい変更では、十分なテストカバレッジを持つように自動テストをまず拡充すべきという定石はもちろん承知していましたが、テストカバレッジの現実に加えて、先日リリースされた Rails5 と次のフレームワークの更新も目前に迫ってきており、周辺ライブラリが Rails3 サポートを切り捨ててアップデートできなくなるなど、悠長なことは言っていられない状況になっていました。

またこのような顕在化していく問題に加えて、フレームワークの最新の文法や機能を使えないという開発者のストレスもどんどん溜まっていくばかりだったのは言うまでもありません。

テスト戦略の決定

このような差し迫った状況の中でどのようにしてテストを行うのか議論に議論を重ねた結果、開発チーム全体を巻き込んで愚直に手動テストを行うことにしました。

手動テストを選択した理由は、単にテストが不足していたという事もありましたが、事前に行われたウォークスルーテストの結果を勘案しながら、仮に自動テストのカバレッジを上げても検出しづらい問題(例: assets リンク切れ等)も散見されたためです。これはクラウドワークスのコードベースが今回のような大規模なアップグレードを今まで経験していなかったがゆえに、今まで潜在的に抱えていた問題が一気に顕在化してしまったという側面もあります。

この辺りの歴史は昨日の八木さんの記事にも書かれていますので合わせてご参照下さい。 engineer.crowdworks.jp

リリース戦略

どのようにして旧システム(Rails3) を新システム(Rails4)へ移行するリリースを行っていくのか、大きく分けて下記5つのプランが検討されました。

1. 一度に新システムに切り替える

最もシンプルなリリースプランです。問題が起これば急いで修正してリリース、問題が大きそうであればRails3ベースの旧システムに切り戻しを行い、修正後に再度Rails4のリリースを行うことを想定していました。

この案のメリットは現行のインフラ構成のままで対応できるので、移行のための工数が少なくシンプルに移行リリースが行えることです。しかし、どのような問題がどのくらいの量発生するのか全く予測できておらず、出しては切り戻すというサイクルが長期化するのではないかという恐れがありました。

2. ロードバランサー配下に旧システムと新システムのアプリサーバーをバランシングしながら、徐々に新システムの割合を増やして切り替える

ロードバランサー配下の旧システム群に新システムを徐々に増やしていくことで、新システムへ移行していくプランです。 一度に切り替えるプランに比べて、ユーザーが新システムを使う確率を制御できるので、問題が起きた場合でもユーザー影響をある程度制御できるメリットがありました。

このプランも現行のインフラ構成に大幅な変更を加えることなくデプロイが行えるのですが、Rails3/4のアプリを共存し並行稼動することになるのでアプリケーションの修正と検証が必要でした。

3. ロードバランサーアプリケーションサーバーの間にリバースプロキシ層を用意し、セッション毎に新システムへの割合を増やして切り替える

プラン1,2 に比べて更にユーザー影響をコントロールできる方法です。プラン1,2 では rails3 と rails4 へのリクエストがランダムに振り分けられてしまうので、問題の切り分けがどちらのバージョン起因なのか追いづらくなる課題がありました。

このプランでは先述の課題は小さくできますが、Rails3/4の並行稼動の検証に加えてセッション毎にバランシング出来る機能を持つリバースプロキシサーバーの検証と、現行のインフラ構成の変更が必要でした。

4. ロードバランサーアプリケーションサーバーの間にリバースプロキシ層を用意し、URL毎に新システムへ切り替える

プラン1,2,3 に比べて最もユーザー影響をコントロールできる方法です。URL毎に制御できれば機能単位での移行リリースが可能になります。

安全なリリースを目指すという点では最も魅力的ですが、プラン3と同様に並行稼動の検証とインフラ構成の変更が必要なことに加え、そもそも実現方法の確証を持てていませんでした。

5. バージョンアップしない

バージョンアップのコストが大きすぎるので、バージョンアップしないという選択肢も検討されました。現状のクラウドワークスはモノリシックなRailsアプリケーションですが、将来的にはマイクロサービス化していくことを計画していたので、バージョンアップではなくマイクロサービス化を優先してサービスを分割していくことで、コアとなるアプリケーションのバージョンアップの必要性自体を小さくしていくというプランです。

検討はしましたが、バージョンアップのコストが大きいのと同じようにマイクロサービス化のコストも大きいと容易に予想でき、マイクロサービス化の形も見えていない検討時点では単なる問題の先送りにしかならない可能性がありました。また、開発チームとしてはある意味で敗北とも言えるプランでした。

リリース戦略の決定

議論を重ねた末、一番コストが見積もれずチャレンジングなプランでしたが、最もユーザー影響をコントロールできるプラン4に挑戦することにしました。

最終的にこのURL毎に新システムに切り替える仕組みの確立に成功したのですが、技術的な解説は森田さん(@minamijoyo)がまとめているので合わせてご参照ください。

engineer.crowdworks.jp

段階的リリース

前述のURL毎にリリースする仕組みを整えたことにより、機能毎に段階的にリリースすることが可能になりました。

クラウドワークスのroutesファイルは2000行弱、バッチジョブ数は100近く存在しており、それらをビジネスドメイン毎に分割し(会員機能、仕事依頼機能、マッチング機能、決済支払い機能など)、全部で10のフェーズに分けました。

全体のテストは各チームで分担しながら、次のフェーズのリリース日に合わせて該当フェーズのテスト期間を設け、全体テストとリリースを並行で進めながら実際の移行を進めていきました。

Rails3.2 から Rails4 の最新である 4.2 系へ

Rails Upgrade Guideではマイナーバージョンアップグレードを順番に刻んでアップグレードすることを推奨していますが、今回は開発全体で手動テストを行う方針としたので手動テストのコストを勘案して、推奨されている通りバージョンを刻んでいくのではなく一気にRails4系の最新へアップグレードするという判断も合わせて行われました。

ただし、間のマイナーバージョンである Rails4.0, 4.1 で出力される Deprecation Warning はアップグレードに際して貴重な情報であり、CI 上でマイナーバージョンを適用してログを収集しアプリケーションの修正に役立てました。

テスト環境

事前テスト

開発全体でのテストを行う前に、Rails4対応チームのコアメンバーによるざっくりとしたシナリオテストを事前に行いました。

この事前テストでは Rails3/4 のマシンを別途用意し、両方に開発ブランチの最新リビジョンを自動的にデプロイを行いつつ同一DBを参照するようにし、Rails3/4 での並行稼動時の問題点の洗い出しと修正を進めました。 Rails3/4 並行稼動用のモンキーパッチ 🐒 の多くはこの事前テスト期間中に課題として発見され作成されたものです。

engineer.crowdworks.jp

全体テスト

全体テストにおけるテスト環境は、terraformchef を用いてインフラ構成もほぼ本番と同等の環境をAWS上に作成、DBも本番データから個人情報をマスクしたものを用意し、出来る限り本番環境に近いテスト環境でテストを行いました。

このテスト環境で新システムの動作確認はもちろんのこと、実際のURL毎の切り替えを伴うインフラ側でのリリーステストも兼ねていました。また、リリース後に何か問題が発生した場合には、問題の再現条件の確認や対策方法の検証を行う環境としても利用され、トラブル検証用としても有用な環境として利用することができました。

まとめ

上記の段階的リリースを約2ヶ月弱の期間をかけて行い、大きな事故もなく無事にRails4へと移行することができました。今回の機能毎に分割するインフラ構成と知見は、次回のアップグレード時にも再利用できるものになっているので、次回は今回よりも低コストで行えるでしょう。また、今回の移行リリースを通じて既に不要となっていた機能やURLの掃除も行い、約5%の不要コードを削除することができました。

クラウドワークスでのRails4移行は手動テストという泥臭い面も採用しつつ、リリース方法については影響をコントロールできるように技術的チャレンジも同時に行ったものでした。

今回のクラウドワークスの状況とみなさんのプロジェクトとの状況は異なるでしょうし、私達と違うアプローチを取ることもあるでしょうが、この記事では私達が検討した選択肢や理由等についてなるべく説明するように心がけました。 フレームワークのアップグレードのような大規模移行リリースを行う際に、この記事が少しでも役に立てば嬉しいです。

We're Hiring !

クラウドソーシングのクラウドワークスでは、フレームワークのバージョンアップも積極的に行っていくエンジニアを募集中です!

www.wantedly.com

© 2016 CrowdWorks, Inc., All rights reserved.