Japan
サイト内の現在位置を表示しています。
ブログ
OSS貢献活動Open Source プロジェクトにおける継続テストとその活用
こんにちは、NEC・OSS推進センターの大道です。
私はこれまで Linux カーネル、OpenStack、Kubernetes の Open Source プロジェクトに開発者として参加し、デバッグ・テスト周辺の開発に携わってきました。そこで本記事ではその経験を基に「Open Source プロジェクトにおける継続テストとその活用」を解説します。
なお、本記事は2022年10月29日に開催された「Open Source Conference 2022 Fall Online」で発表した内容を基に執筆しています。講演の動画はこちらを参照ください。
Open Source プロジェクトにおける継続テスト概要
はじめに Open Source における継続テストの概要について説明します。
OpenStack、Kubernetes など多くの Open Source では継続テストが行われています。具体的には開発者が Pull Request による変更提案を行った場合、その Pull Request に対して、単体テスト・結合テスト・E2E テストなど一連のテストが実行され、それらのテストが全て正常に通ったのちにメンテナーによるコードレビューが実施され、問題がなければ本流(main/master ブランチ)に統合される、という流れで開発が行われています。
もしも一部のテストが失敗した場合、そのような Pull Request はコードレビューが実施されず放置されることになります。よって、Pull Request を提案している開発者はテストが失敗した原因を調査し、随時その対処を実施していく必要があります。
また、新機能追加やバグ修正を提案する際はそれに対応する単体テストを実装することが必須となっている場合が多いです。これにより単体テストレベルでのテスト網羅度を高い状態に保とうとしています。結合テスト・E2E テストも要求される場合がありますが、修正と単体・結合・E2E テストを1つの Pull Request に含めると変更量が大きく、コードレビューが大変になるので、ケースバイケースになっています。
ここからは Kubernetes で具体的にどのようなテストがプロジェクトで実行されているか見ていきます。前述の通り、単体・結合・E2E という基本的なテストが Kubernetes で実施されています。それ以外にもコーディングスタイルのチェック、タイポのチェック、マークダウンで書かれたドキュメントのスタイルチェック、各ソースファイルにおけるライセンス明記のチェック、開発貢献合意サインのチェック、パッケージ命名規則チェック、オプション命名規則チェックなどが行われています。
図2 Kubernetes で自動実行されるテスト群
上記の画面は Kubernetes で1件の Pull Request を提案したときに自動実行されるテスト群を抜きだしたものです。19のテスト群、テスト項目としては4万5千項目が自動実行されます。
なぜ、これほど多くのテスト・チェックが実装されているのでしょうか。
ご存じの通り Open Source プロジェクトでは世界中から人種・国の制限なく多種多様な多くの人が開発参加しています。Kubernetes v1.25 (2022年10月時点での最新版) では4か月の開発期間に1,700人以上の開発者が参加し1万件以上のコミットが統合されました。このような大規模開発では、参加する開発者全員にそのOpen Sourceプロジェクトの開発ガイドラインの事前理解・順守を期待することや、メンテナーが人手で開発ガイドラインに順守しているかを全てチェックすることは難しくなります。その代わりに、Kubernetes プロジェクトでは、自動テスト・自動チェックを実装することで、開発参加人数が大幅に増えても開発ガイドラインの順守や品質の確保ができるスケーラブルな仕組みを構築しています。
また、自動テスト・自動チェックは人間の作業的負担だけでなく精神的負担を軽減できます。自動コーディングスタイルチェックがない状態で「ここのコーディングスタイルはこうすべき」とコードレビューで指摘するのは精神的負担が大きいことがあります。相手に「それってあなた独自のコーディングスタイルなんじゃないの?」と疑われたりすることもあるためです。それに対してコーディングスタイルチェックが有効になっていれば、チェックが失敗した Pull Request に対して「こう修正すればこのチェックはパスすることができるよ」と前向きにアドバイスでき、良い人間関係を構築できます。
E2E テスト概要
ここではE2Eテストのパターンについて説明します。
単体、結合テストでは環境依存の部分が小さいため、一つの環境で全テスト項目を実行可能です。一方、E2E テストは実際に開発対象物をデプロイし、デプロイされた対象物の API のテストを行うため、環境依存の部分が大きくなります。例えば Kubernetes のE2Eテストでは、Kubernetes クラスタをデプロイしますが、OS として Ubuntu を使うのか、それとも AlmaLinux などの Red Hat 互換を使うのか、または IPv4/v6 のデュアルスタックを有効にするかしないのか、Container Runtime や CNI は何を使うのかなど複数のパターンを考慮する必要があります。これらの組み合わせを行ったうえでデプロイし、E2E テストを実行し、それぞれの環境でのテストが正常に通ることを確認することになります。
ちなみに OpenStack に関係する人に説明すると、ここでの E2E テストとは、OpenStack での Tempest になります。
E2E テストによる API 網羅度
図3 APISnoop による API 網羅度測定 (2022年10月時点)
上記のグラフが現在最新の APISnoop で出力されている API 網羅度を示すグラフです。この測定ができるようになったおかげで、2017年時点17%だった E2E テストの網羅度がここにあるように2022年現在では70%以上に大幅に向上しました。
Conformance テスト概要
このように大幅にテスト数が増加した E2E テストですが、環境依存の機能に対する E2E テストが多数存在します。例えば、HPA (Horizontal Pod Autoscaling) 機能ですが、Kubernetes ではかなり一般的な機能の一つになっています。しかし、この機能を使うには metrics-server アドオンをデプロイする必要があり、環境依存の機能と言えます。関連して Kubernetes クラスタを構成するノードのリソース状況を確認できる `kubectl top nodes` コマンドも metrics-server がないと動作しませんので、こちらもコアな機能とは言えません。
一方で、Kubernetes としてコアな機能が正常動作しているのかを確認するテストセットが必要となりました。AWS、Azure などのクラウドプロバイダと更にはオンプレミス環境に構築された Kubernetes クラスタ間でもアプリケーションの相互運用が可能であることを確保したいという要求が運用者側から上がってきたためです。このようなコアな機能に対するテストを E2E テストから抽出したテストセットを Conformance テストと言います。
Conformance テストの条件は複数ありますが、ここではいくつか例として挙げます。Kubernetes では新たな API は Alpha バージョンとして追加され、いくつかのバージョンを経て十分なテストが行われているとみなされた段階で、Beta、GA と API として昇格していきます。Conformance テストでは、GA の API のみを使っていることが条件の一つとして設定されています。また、インターネットが接続できない Air-gapped 環境でも利用可能なテストであることも条件になっています。詳しくはこちらのリンク先をご参照ください。
この Conformance テストが通った Kubernetes マネージドサービスは相互運用性を確保したプラットフォームとしてCNCF から「Certified Kubernetes Conformance」と認定され、CNCF の公式ページに認定プラットフォームとして掲載されます。
図4 Certified Kubernetes Conformance 認定プラットフォームが利用できるロゴ
Open Source プロジェクトの継続テスト活用
Open Source は世界中にあるソフトウェアの最大公約数と考えらえます。多くの人にとって有益である部分を公約数として抽出、共有部分として開発していると言えます。
そのテストも多くの人に有益であれば、コミュニティの資産を使って継続テストに加えることが可能です。ここでいうコミュニティの資産とは、Linux Foundation、CNCF、OpenInfra Foundation などの Open Source 団体に対してスポンサー企業が自身の資源を寄付して出来た資産のことです。例えば、Google Cloud などのクラウドプロバイダは CNCF に対して自身のクレジットを寄付しています。Kubernetes 開発ではそのクレジットを利用することで、開発テストがクラウド上で実行されています。
このようなコミュニティ資産を使って実施するテストも多くの人にとって有益なものであるべきです。少数の特定の人にしか有益でないテストをコミュニティで実行してしまうと、コミュニティ資産を消費するだけでなく、そのテストで問題があった場合にコミュニティで活動する開発者の貴重な時間をデバッグで消費することになってしまいます。
では、どのようなテストをコミュニティで追加・実施するのがよいのでしょうか。
例えば、皆さんの中には SELinux もしくは AppArmar を有効にすることが必須でシステムを組む必要がある方もいると思います。Kubernetes との組合せテストを自身でやるのが大変だと思えば、コミュニティ側でそのようなテストパターンを追加して継続的にテストを実行してもらうことも可能です。コミュニティで継続テストしてもらうことで、手元で問題が発生した際にコミュニティでのテストの設定・環境などの差分を調査することで、問題の原因を見つけ出すことが可能になります。実際に下記の Pull Request はそのような観点で Kubernetes クラスタのセキュリティ強化デプロイに関するテストパターンを追加したものです。
図5 Kubespray へのセキュリティ強化設定のテスト追加の例
このテストパターン上で他の開発者が報告したセキュリティ強化環境で発生する問題を再現させ、その修正に必要な変更を検証することもできます。メンテナー視点では問題の再現とそれに必要な修正が透明性を担保した状態で確認できるため Pull Request のレビュー・統合が行いやすくなります。
コミュニティにおける役割とテスト実行権限
上記で説明した通り、継続テストではコミュニティの資産を使って実行されその資産は有限なため、効率的にテストを実施する必要があります。その1つの方法として Kubernetes ではそのプロジェクトにおける役割によって継続テストの実行権限が異なっています。
Kubernetes では、下図にあるように下から Non-member contributor、Member、Reviewer、Approver、Owner という役割があります。
図6 Kubernetes における開発者の役割
Kubernetes プロジェクトへの開発参加者は全員 Non-member contributor から始まり、プロジェクトへの貢献度・責任性などを考慮してそれぞれのポジションに昇格していきます。そして Non-member contributor が投稿した Pull Request は継続テストが自動的には実行されません。Member 以上の開発者によるテスト実行許可 (ok-to-test ラベル) を取得する必要があります。
これは、新たに参加した開発者の Pull Request が資産を使ってテストを実行する必要・価値があるかを事前にチェックするためです。このように、Non-member Contributor とそれ以上の役割を持つ開発者には明確に権限の差が存在します。
なお、Reviewer は Member と権限的には違いがないのですが、担当する領域に関する Pull Request が投稿された場合に Review 要求が飛んでくるという違いがあります。また Approver になると他人が投稿した Pull Request を Approve することもできますし、自分が投稿した Pull Request には自動的に Approved のラベルが付与されます。
前述の通り、Open Source プロジェクトでは会社組織と同様に権限を獲得するには昇格する必要があります。その昇格のためには多くのノウハウがありますが、ここではテスト観点で3点ご紹介します。
コードレビューへの積極参加
1点目はコードレビューへの積極参加です。Open Source プロジェクトにおけるポジションの昇格とは、本流へのコードの統合権限が伴うものになります。プロジェクト視点では有益な機能・バグ修正のコードを高い品質を保持した状態で統合する必要があります。そのためにはコードレビューにおいて他の開発者が提案する Pull Request に対して、有益なアドバイスを与えられるかが重要になります。有益なアドバイス無しに LGTM のラベルを付けるだけで数を稼ぐようなコードレビューをしても中々昇格が難しい場合があります。Open Source プロジェクトの開発者は意外なほどそのあたりの開発者としての振る舞いを見ているためです。他の開発者が気付かない・見落としがちなコメント・アドバイスを行うことで、プロジェクトの品質確保に貢献することでコミュニティでの信頼を獲得し昇格につながります。
テスト観点では、例えばバグ修正とセットで実装された単体テストがちゃんとしたものかをレビューする方法があります。バグ修正とともに提案された単体テストは、バグ修正が行われていない状況ではテストとして失敗する必要があります。バグを再現させ、そのバグが修正されたことを確認するために実装されている必要があるためです。コードレビューをしていると、バグを再現しているように見えない単体テストを実装している Pull Request を発見することがあります。そんなときは、バグ修正のコードをリバートし、その単体テストだけを流してみるとよいでしょう。もしバグ修正をリバートした状態でテストが通ってしまったら、その単体テストは無効なものとコードレビューで指摘できます。ここまで手間をかける人はあまりいないので有益な貢献とみなしてくれると思います。
テストの改善
2点目はテストの改善です。コードレビューをしていると、別々の Pull Request に対して同じような指摘をすることがあります。例えば、ドキュメントやコード上のタイポやコーディングスタイルなどです。そんなときは、継続テストを追加することを検討しましょう。メンテナーのコードレビューの負担が軽減できます。メンテナーとしては「この観点はあの継続テストでカバーされているはずだから別の観点でレビューをしよう」と考えることができます。また、Pull Request 提案者もコードレビュー前に自身の誤りを早い段階で検出できるため、開発の高速化につながります。
Flakeテストの解消
3点目は Flake テストの解消です。Flake テストとはテスト結果が安定しないテストのことです。主に E2E テストで発生します。E2E テストでは実際に開発対象物、Kubernetes の場合は Kubernetes クラスタをデプロイし、そのクラスタに対して API を通して期待した振る舞いになっているかテストします。そのため、このテストは多くの外部要因に依存することになります。たとえば、Kubernetes クラスタをデプロイするために多くのサイトからパッケージやファイルのダウンロードが必要になりますが、その一部のサイトがダウンした場合、テストとしては失敗になります。また、テストはクラウド上で実施されますが、ネットワーク・ストレージの性能が十分でないと所定の時間内にテストが完了せず、タイムアウトが発生します。
Kubernetes 固有のケースだと、HPA の E2E テストは難しいテストの一つです。CPU 負荷をかける Pod を作成し、その負荷に対して期待したレプリカ数まで Pod が自動的に増えたことを確認するテストなのですが、CPU 性能やレプリカが増えるタイミング次第でテストが失敗するためです。
このような Flake テストを解消すると、コミュニティでの信頼獲得に大きくつながります。無関係な Pull Request のテストが失敗することを防げ Pull Request 提案者がフラストレーションを抱えないで済むこと、再テスト実行のためのコミュニティ資産を消費するという状況を防げるからです。また、Flake テストを解消するには多くの労力がかかることを多くの開発者が理解しているため、コミュニティへの貢献が大きいとみなしてくれます。
以上、コードレビューへの積極参加、テストの改善、Flake テストの解消の3点がテスト観点での昇格ノウハウです。
プロプラ開発での継続テスト
今回説明したような継続テストは Open Source、プロプラにかかわらず今日では実行されるようになりました。一方で継続テスト自体面倒でなかなか第一歩を踏み出せないという方もいらっしゃると思います。そんな方は、まずはコーディングスタイルチェックやタイポチェックから継続テストとして実行するのはいかがでしょうか。テストが失敗した場合の原因が明確で対応がしやすいので、開発チームに継続テストの文化を浸透させる良い一歩になります。
まずは、単体のテストスクリプトを作成します。それをローカル実行し、テストスクリプトとして動作するか確認します。そして Pull Request と連携するための設定を行います。GitHub を使っているのであれば、GitHub Actions を使って簡単に連携が可能です。GitLab でも、YAML 形式の設定ファイルでテストスクリプトを指定すれば簡単に Pull Request との連携が行えます。これにより、Pull Request が提案されるたびに指定したテストスクリプトが実行され、その結果が Pull Request 上に表示されます。
最後に Kubernetes Upstream Training のご紹介
本ブログでは Kubernetes をメインに継続テストについてご紹介しました。テストだけでなく、Kubernetes の Open Source 開発に興味を持たれた方がいらっしゃいましたら、「Kubernetes Upstream Training」の受講をお勧めします。これは Kubernetes カンファレンス KubeCon の前日に開催されている Contributor Summit の教材を基に Kubernetes への開発参加方法を日本語で学べるトレーニングコースになります。Kubernetes コミュニティのサポートを得ながら講義内容の更新を行いつつ、日本での開催を継続的に行っています。
第1回目は2019年の Cloud Native Days Tokyo で実施し、2022年10月時点で7回、日本で Kubernetes Upstream Training を開催しています。今後の開催スケジュールは contributor-playground/japan at master · kubernetes-sigs/contributor-playground (github.com) に記載しています。
Kubernetes への開発参加を検討されている方はぜひご参加ください。
図7 第1回Upstream Training (2019年開催)
執筆者
大道 憲一(Kenichi Omichi)
日本電気 (NEC Corporation)
世界最大規模の OSS プロジェクトである Kubernetes において、品質向上とデプロイメントツール Kubespray の機能強化に取り組んでいます。現在、Kubernetes のApprover、Kubespray の Maintainer を務めています。Kubernetes Upstream Training の日本開催を企画・実施することによって、日本からの開発貢献を支援しています。(2022年11月時点の情報)