技術部アルバイトの鈴木(@draftcode)です。
クックパッドが内部向けに開発・運用を行ってきた、分散テスト実行システムRRRSpecをオープンソースとして公開しました。RRRSpecは時間のかかる自動テストを分散処理することで、全体のテスト時間の短縮を狙うアプリケーションです。現在クックパッドでは17000を超えるテスト項目があり、マシン一台でテストを実行すると完了まで数時間かかります。このテストを60並列程度の分散処理で行うことで、平均8分から9分程度で完了できるようになりました。また、Amazon EC2のスポットインスタンスを利用することにより、大幅なコスト削減も同時に達成しました。
https://github.com/cookpad/rrrspec
分散テスト実行とは
アプリケーションが大きくなるにつれて、自動テストの数も大きくなっていきます。クックパッドでは、非常に多くの自動テストが書かれており、通常の方法では一回の実行に数時間かかってしまいます。このような非常に大きいテストスイートのために、parallel_tests のようなテストを分散実行するライブラリが既に存在しています。しかし、parallel_testsはマシン一台のみでの分散実行のため、クックパッドのテストスイートを実行すると、一台で分散実行してもなお、一回のテストに数十分かかってしまいました。
parallel_tests単体ではまだテスト時間が十分短縮できなかったため、クックパッドではremote_specという、複数台に対してparallel_testsを実行するライブラリを開発しました。これにより、一回のテストにかかる時間が10分以内におさめることができ、実用的な範囲で実行することができるようになりました。
remote_specによって複数台のマシンによるテストの分散実行が達成出来ましたが、まだいくつかの問題点がありました。例えば、remote_specの仕組み上、マシン台数を柔軟に増減させることが出来なかったり、マシンやテスト実行プロセスの障害に対して、上手く対応できないという点が挙げられます。これらの問題を解決するために、RRRSpecはゼロベースで設計されました。
RRRSpecの機能
RRRSpecはRSpecで記述されたテストに特化した分散テスト実行システムです。RRRSpecは一台のマスタと複数台のワーカで構成されます。テスト実行を開始するクライアントはマスタに対してテスト実行に必要なソースコードを転送し、テスト実行キューにテストをキューイングします。マスタはキューに入れられたテストを適宜ワーカに割り振り、ワーカがテスト実行を行います。
このような通常の分散テスト実行以外にも、いくつかの興味深い機能を備えています。
- マシンシャットダウン・プロセスの停止の自動復帰
- 無反応のプロセスに対するハードタイムアウト・ソフトタイムアウト
- 失敗したテストの自動再実行
- テスト実行順序の最適化
- テストの投機的実行
これらの機能がなぜ必要なのか、どのように行っているのかといったデザイン上の決定はRRRSpec Design Decisionsにまとめられています。また、RRRSpecの実行シーケンスなどがRRRSpec Hacking Guideにまとめられています。
RRRSpecのマスタとワーカを一台のPCで動かし、テスト実行を行う環境をlocal_testに用意してあります。セットアップの際に参考にしてください。
理論的な背景
RRRSpecにまつわるエンジニアリング的な話題は先のDesign Decisionsにまとめられてるのですが、それとは違う理論よりのアカデミックな話題もあります。ここでは、そういった話題について、簡単に紹介します。
ワーカーマシンのスケーリング
クックパッドではRRRSpecの導入により、Amazon EC2のスポットインスタンスを利用して動かすことができるようになりました。スポットインスタンスとは、Amazon内で余らせているインスタンスをオークションで落札することにより、安く借りることができるインスタンスです。ただし、安く借りられる代わりに、予告なしにマシンが落とされるという制約があります。
RRRSpecはマシンの停止に対して故障耐性があるため、スポットインスタンスで運用して、急にマシンが落とされても問題ないようになっています。スポットインスタンスを上手に落札して、マシンをスケールさせることができれば、効率よくテストを実行することが出来ます。これを達成するために、クックパッドでは次のようなことを行ってきました。
- スポットインスタンスのオークションの仕組みを推定し、ゲーム理論・オークション理論を参考にして、効用関数を決定。線形計画問題に帰着させることにより、入札価格決定問題を解く。
- マシンのスケーリングスケジュールを決めるために、マシンの使用状況を過去のデータから予測し、セグメント木を使って効率的に資源配分戦略を計算。
- 待ち行列理論を応用して、時間あたりに実行されるテストの平均実行時間が規定値以下になるために必要なマシン台数を決定。
このように、使っているのは大学の学部生で学ぶ、初歩的な計算機科学や教養で出てくるツールですが、上手く問題を変形することにより、現実に発生する問題に対して応用しています。
モデル検査とフォールトインジェクション
RRRSpecは分散システムの一種になります。分散システムでは複数のマシンが協調して動作するため、マシンやネットワークの障害による影響が大きいです。たとえマシン一台の故障する確率が非常に小さくても、マシン台数が増えるにつれて故障の発生確率は上がっていきます。
先ほど触れたようにスポットインスタンスには、急にマシンがシャットダウンされることがあるという制約があります。このスポットインスタンスの制約は、RRRSpecからみるとマシンの故障としてもとらえることができます。スポットインスタンスを用いたシステムや分散システムでは、故障が頻繁に起こりやすいため、システム内で障害が起こったときに、全体として正常に動作するかどうかといった、システムの故障耐性が重要になってきます。故障耐性を調べる方法として、モデル検査とフォールトインジェクションが挙げられます。
モデル検査とは、システムの全状態を探索することで、正常でない状態に陥らないかどうかを検査する手法です。ただしシステムそのままの全状態を探索しようとすると、一台のマシンのメモリ空間だけでも2の2の64乗の大きさとなり、全く現実的ではありません。そこでモデル検査では、システムのミニチュアを作ることにより、全状態の探索を可能にしています。有名なモデル検査器としてSpinやNuSMVなどが挙げられます。
フォールトインジェクションとは、システムにわざと故障を埋め込むことによって、故障したときの動作を調べる手法です。たとえばCPUのような半導体に対して放射線を当てることによって、ビットフリップを誘発させたりします。
モデル検査とフォールトインジェクションを組み合わせることにより、システムの故障時の動作を全探索することができ、これにより、システムに故障耐性があるかどうかを検査することが出来ます。
RRRSpecではRRRSpec Hacking Guideにあるように、システムのモデリングを念頭に置きながら、いくつかの故障シナリオとその復帰方法を明らかにし、実行シーケンスまで分析しています。
まとめ
分散テスト実行システムRRRSpecを開発し、オープンソースにしました。なぜこのようなツールが必要になったのかという問題について説明し、開発に際し直面したエンジニアリング的な話題と、その裏側にある理論よりのアカデミックな話題について説明しました。
同様の問題に直面している方でRRRSpecをそのまま運用できれば嬉しいですし、そうでなくても、RRRSpecで直面した問題とその解決法が、他の同様のシステムや別の問題に対して参考になれば幸いです。