Ichizokuは日本唯一のSentry公認販売業者です。 日本語のドキュメント、動画、サポート窓口で日本のお客様のSentry活用を支援します。

Pythonのテストを数百の環境で高速に実行する方法

長文を読む気分ではない方は、ここからDjangoCon 2022で行われた講演(英語)を見ることができます。

Sentryの信念のひとつに「すべての開発者のために」というものがあります。
すべての開発者をサポートしたいと思っています。しかし、すべての開発者が最新の技術や広く採用されている技術スタックを使っているわけではありません。そのため、古いバージョンのライブラリやフレームワークもサポートするように心がけて取り組んでいます。

当社のSentry SDK for Pythonでは、次のことをサポートしています。

  • 約20種類のWebフレームワーク
  • Python2.7を引き続きサポートします(!)
  • Python 3.5から3.11まで対応しています
  • 古いバージョンのフレームワークをサポートしています。(例:8年前のDjango 1.8をサポートしています)

SDKが正しく動作することを確認するために、テストスイートには約450の自動テストが用意されており、SDKに変更を加わるたびに実行されます。

7つのPythonのバージョンと約20のフレームワークをサポートしているため、それぞれのフレームワークのバージョンが2~9になると、テストを実行する環境は400を超えます。

テスト用スタック

テストスイートはpytestを使用して実行されます。

テスト実行前に、Flake8blackを使ってソースコードのリントとフォーマットを行い、mypyを使って型チェックを行っています。

Toxは、さまざまな環境でテストスイートを実行するためのツールです。

ローカルマシンの異なる環境でテストスイートを実行するために、古き良きmakeを使用しています。そして最後に、GitHub ActionsをCIとして使用し、すべてのプルリクエストに対してすべての環境でテストスイートを実行できるようにしています。

スローテスト

私たちのテストセットアップは何年も前に作成され、時間の経過とともに多くのテストが追加されました。

しかし、テストセットアップ自体をリファクタリングする時間がありませんでした。そのため、テストスイートを実行するのに約40分も時間がかかっていました。

このようなテストスイートは、苦痛でしかありません。

SDKの新しいバージョンをリリースする際には、リリースごとにテストスイートを実行するため、テストが完了するのに最大で1時間もかかることもありました。

これは由々しき事態です。そこで私たちは、テストスイートの改善に取り組みました。

テストスイートを高速化する

すべてのテストをリファクタリングすることなく、より速くテストを実行する方法について、いくつか考えました。どのようなことをしたのかをご紹介します。

  • フレームワークごとにテストスイートを分割
  • テスト実行にかかる時間を大幅に短縮
  • 開発者の生産性を向上

考えた結果、テストそのものではなく、テストの実行方法を変えようという結論に至りました。

まず、テストスイートを改善するのに着目したのは次のことです。それはいままでそれぞれのプルリクエストで、すべての環境でテストスイートを実行するGiHub Actionsランナー1つでToxを起動し、1つずつ実行していました。

テストスイートを高速化する

これは、テストを実行する上で最も遅い方法であり、1回の実行に38〜42分はかかります。

アイデア1:toxでテストスイートを並列に実行する

Toxのコマンドラインには、–parallel autoオプションがあり、利用可能なCPUコアの数だけテストスイートを並列実行することができます。

toxでテストスイートを並列に実行する

これにより、すでにテストの実行時間は劇的に改善されました。

今まで40分かかっていたテストが、25分程度になったのです。しかし、これではまだ「速い」とは言えません。

GitHub ActionsのランナーはCPUコアを2つしか持っておらず、使用できるCPUコア数には限界がありました。

アイデア2:GitHub Actionsを使ったテストスイートの並列実行

GitHub ActionsのランナーのCPUを増やすことはできませんが、GitHub Actionsのランナーを増やすことは可能です(大きなマシンを購入しない場合)。

そこで、テストを実行するすべての環境に対してGithub Actionsの設定yamlファイルを作成するスクリプトを作成しました。

アイデアとしては、このようなものでした。

GitHub Actionsを使ったテストスイートの並列実行

しかし、GitHub Actionsの同時実行できるワークフローにも制限があることがわかりました。

私たちはGitHub Enterpriseを使っているため、GitHub Actionsでは180個のワークフローを同時実行することができます(Sentryの組織全体が対象です)。

しかし、プッシュするたびに400のワークフローを起動するのは、あまりいいことではありません。

アイデア3:両方の良いとこどり

妥協案としては、テストスイートをToxで並行して実行させ、20のWebフレームワークごとに1つのGitHubランナーを起動することにしました。具体的には次のようなものです。

テストスイートをToxで並行して実行

tox.iniファイルを解析するスクリプトを修正して、フレームワークごとに1つのyamlファイルを作成するようにしました。

これらの新規作成または更新されたyamlファイルは、tox.iniファイルに変更が加えられた後、リポジトリにコミットする必要があります。

これを確認するには、CI中にスクリプトを呼び出して、現在のtox.iniファイルがコミットされたyamlファイルと一致するかをチェックします。

もし一致しない場合、CIは失敗し、開発者に修正内容を伝えるエラーメッセージが表示されます。

これにより、誤った内容がコミットされることなく、常に正しい内容が反されるようになります。これは、このプロセスを長期的に機能させるために重要なことです。

最終的に、テストスイートの実行時間は10分程度になり、実行時間を大幅に短縮することができました。

アイデア4:Yamlファイルのクリーンアップ

tox.iniファイルからyamlファイルを作成するスクリプト(オリジナルのGitHub Actions yamlファイルに基づく)を作成する際に、いくつかの違和感に気がつきました。

yamlファイルの中にactions/setup-node@v3があったのですが、これは使われていませんでした。そのため、それを削除しました。

また、テストごとにRedisとPostgresを起動していましたが、Redisは全く使っていないことが判明しました。

なぜなら、今はfakeredisを使っているため、Redisサービスは削除することができたのです。また、Postresもすべてのテストで実行されていましたが、Djangoのテストでのみ使用されていました。

そこで、DjangoのテストでのみPostgresを起動するようにし、他の19のWebフレームワークでは起動しないように設定を変更しました。

これでさらに5分ほど短縮することができました。

修正の結果:
  • テストスイートを実行するのに40分かかっていたのが、5分に短縮されました。
  • テスト内容は変更せず、実行方法のみを変更することで対応することに成功しました。
  • Webフレームワークごとにテストスイートを分割することで、失敗したテストを見つけるのがとても簡単になりました。
    以前は、テスト実行のログ出力が数十行もあり、失敗したテストを見つけることに時間がかかっていました。
  • 開発者がみんな幸せになりました!
次は何をするのか

まだ実装していないアイデアだけのものもあります。

  • 社内のPyPIサーバーを利用して、テスト実行中に必要なもの(主に、これらのフレームワークの古いバージョン)をインストールしておきます。
  • test-requirements.txtを改良し、yamlファイルでaction/cache@v3を使用できるようにします。
  • GitHubランナーでRAMドライブを使い、メモリ上に仮想環境を作成します(GitHubはこれを許可しています!)。
  • 現在、各テスト実行後に最も遅い5つのテストをログに記録することによって(pytest -durations=5を追加することで)、それらのテストを改善することができるようになりました。

テストスイートを2分以内に実行することは、比較的簡単だと思っています。

なぜなら、フレームワークごとにテストスイートを分割してみると、20のフレームワークのうち、5分程度かかるのは4つだけで、大半のフレームワークは1~2分しかかかっていないからです。

テストスイートをさらに改善する方法に関するすべてのアイデアは、「Better Test Suite」マイルストーンで収集しています。問題やアイデアがあれば、ぜひご連絡ください!

Sentryは、アプリケーションのコード状況を監視するのに不可欠です。エラートラッキングからパフォーマンスのモニタリングまで、開発者は、フロントエンドからバックエンドまで、アプリケーションをより明確に把握し、より迅速に解決し、継続的に学習することができます。

Sentryは、世界中の350万人以上の開発者と85,000以上の組織に愛され、Disney、Peloton、Cloudflare、Eventbrite、Slack、Supercell、Rockstar Gamesといった世界で最も有名な数多くの企業に、コードレベルでの監視機能を提供しています。

毎月、インターネット上で有名なツールやサービスから数十億のデータを処理しています。


 

IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談はこちらのフォームからお気軽にお問い合わせください。

シェアする

Recent Posts