ハドソンの継続的インテグレーション

テストはソフトウェア開発ライフサイクルの不可欠な部分であることは、私たち全員が理解しています。 コードを頻繁にテストすればするほど、開発中にコードに侵入したエラーをより迅速に検出し、修正することができます。 同時に、開発サーバーには表示されないが戦闘に表示される可能性のあるエラーを検出できるように、戦闘環境にできるだけ近い環境(OS、ソフトウェア、ハードウェア、負荷)でテストすることを強くお勧めします。 上記の2つの論文を一緒に構成すると、継続的インテグレーションと呼ばれる概念が得られます。

CIの本質は、可能な限り早期にエラーを検出し、それらについて開発者に通知するために、戦闘環境に可能な限り近いところで開発中のソフトウェアを常に(たとえば、各コミット後に)ビルドおよびテストすることです。 CIの概念そのものはMartin Fowlerに属し、彼は彼の記事で詳細に説明しています

継続的なアセンブリプロセスの自動化には、既成のソリューション( HudsonCruiseControl )があり、そのうちの1つ(Hudson)の統合はこの記事で説明します。



挑戦する


したがって、2つのプロジェクトがあるとしましょう:Javaサービス(独自のデータベースを使用)と、PHPクライアント(独自のデータベースを使用)。 両方のプロジェクトはdebパッケージとして配布されます。 これらのプロジェクトを継続的に統合するためのインフラストラクチャをセットアップする必要があります。


実装


最後に何を達成したいのかを理解するために、最後から始めます:実装したいスキームを考えます:




開発者は自分のマシンでプロジェクトに変更を加え、SVNでコミットします。 SVNサーバー上で、コミット後フックがトリガーされ、CIサーバー上の対応するプロジェクトのビルドプロセスが開始されます。 CIサーバーはSVNからパッケージバージョンを更新し、プロジェクトをコンパイルし、単体テストを実行し、プロジェクトをステージングサーバーに配置します。

Webインターフェースのないプロジェクトの場合、統合テストが実行され、Webインターフェースのあるプロジェクトの場合、Seleniumテストが実行されます。 CIサーバーはレポートを生成し、必要に応じて(プロジェクトアセンブリのいずれかの段階で障害が発生した場合)ユーザーに電子メール通知を送信します。

戦闘サーバーのリポジトリへのプロジェクトパッケージのアップロードは、リリース時に開発者が手動で行います。


ハドソン


私たちのシステムの主で最も興味深いノードは、CIサーバーです。 この場合、最も人気があり一般的な無料ソリューションの1つとしてハドソンになります。
まず、インストールしてください。 Hudsonはパッケージとして提供されているため、インストールは非常に簡単です。 さらに、Hudsonはその構成全体をファイル( / var / lib / hudson )に保管します。これは、データベースとの統合を必要としません。



ハドソンのアーキテクチャはプラグインに基づいています。 つまり、本質的に、ハドソンの仕事は、プロジェクト/プラグインの設定を保存し、プロジェクトを構築することです。 次に、プロジェクトのアセンブリは、プロジェクト設定に含まれるインストール済みプラグインを特定の順序で起動することで構成されます。
プラグインは、「パイプライン」とも呼ばれるプロジェクトのビルドサイクルを形成するいくつかの条件付きグループに分割できます(プロジェクトプラグインの構成は、[プロジェクト設定]メニューから利用できます)。

残念ながら、Hudsonでは、アセンブリグループに含まれるプラグインの実行順序のみを変更できます(そのグループ内の残りのプラグインの実行順序は、プラグインの@Executionアノテーションコードの値に基づいて決定されます)。 したがって、「Assembly」グループの標準プラグインでは不十分なビルドスクリプトを実装する必要がある場合は、次の3つの方法で実行できます。
  1. このスクリプトを実装する外部の実行可能スクリプトを呼び出します(「ビルドステップの追加」メニューの「シェルの実行」アイテム)。
  2. プロジェクトビルドシステム(Phing、Ant、Maven)のプラグインを接続し、必要な目標を指定します。
  3. プラグインを作成します。

デフォルトでは、HudsonにはSVNとMavenを操作するためのプラグインがすでにインストールされています。 Javaプロジェクトのみの場合、これで十分です。 ただし、PHPプロジェクトでも作業する必要があるとします。 この場合、Phingを使用してプロジェクトをビルドする方が論理的です。プロジェクトのプラグインは個別にインストールする必要があります。 これは、「設定/プラグインの管理/利用可能なアップデート」セクションに移動して行います。



一部のプラグインではJava 6でHudsonを実行する必要があることに注意してください。/ etc / default / hudsonファイルで JVMへのパスを変更できます(残りの構成オプションと同様)。 それ以外の場合、Hudsonの操作に直接関連するすべての構成パラメーターは、Webインターフェースのブラウザーを介して編集できます。

プラグインの設定に関しては、プラグインに一般設定(「システム設定/構成」)とプロジェクト設定(「設定/プロジェクト名/プロジェクトの構成」)の両方があることも言及する価値があります。



必要なすべてのプラグインがインストールされたので、対応するプラグインの名前と設定を指定することで、新しいプロジェクト/タスクを作成できます。SVNリポジトリのURLとアセンブリのコマンド。





プロジェクトに変更がある場合、スケジュールやリポジトリをポーリングするときではなく、SVNのコミットに従ってビルドできることに注意してください。 HudsonにはGETリクエストを行うことでプロジェクトのアセンブリ開始できる「リモートアクセスAPI」があるため、プロジェクトに適切なpost-commitフックを簡単に追加できます(たとえば、 svnlookを使用)。

ビルドフェーズを検討します。



この時点で、パッケージの構築には、リポジトリからデータを取得し、Phingの目標(パッケージの構築)を完了することが含まれます。 原則として、ここで単体テストの起動を追加し、プロジェクトをステージングサーバーに展開できます。 ただし、ここではいくつかの点に注意する価値があります。

まず、ステージングサーバーでアプリケーションを実行するための構成は、戦闘構成とは異なる場合があります。 この場合、明らかな解決策は、ステージングサーバーの構成をプロジェクトに保存し、アセンブリ中に元のプロジェクト(Phingの場合はアセンブリの別のターゲット、またはMavenのプロファイル )に置き換えることです。

次に、 SCPおよびSSHプラグインを使用してステージングサーバーにパッケージをインストールします(プラグインが機能するには、sshd構成のPasswordAuthenticationパラメーターがyesに設定され、ステージングサーバーホストが既知のホストに追加されていることを確認します)、SSHプラグインとしてプロジェクトのアセンブリフェーズを指し、SCPプラグインはアセンブリ後の操作を指します。 したがって、プロジェクトをステージングサーバーにデプロイする問題は、PhingまたはMaven + AntRunを使用して解決する必要があります。
ビルドスクリプトがリモートサーバーでアクションを実行するには、sshキーを生成する必要があります:CIサーバーに秘密キーを残し、対話するすべてのサーバーに公開キーを配布します:ステージング、レポ、svn-それらをリストに追加します既知のホスト(known_hosts)。 さらに、Hudsonがリモートサーバーにパッケージをインストールできるようにするには、対応するユーザー(hudson)をリモートサーバーに作成し、sudoを与える必要があります。

第三に、Mavenを使用してJavaアプリケーションを正常に構築するには、CIサーバー上のhudsonユーザーのMaven設定を決定する必要があります( 〜/ .m2ディレクトリを意味します)。

ステージングサーバーにパッケージをインストールした後の次のステップは、統合テストの開始です。 CIサーバー自体で実行できますが、ステージングサーバーで実行することをお勧めします。 最初のケースでは、すべてが非常に簡単です。適切なPhing / Mavenターゲットを呼び出すか、 SeleniumHQプラグインを構成します。
ただし、外部サーバーでテストプロセスを開始する場合(たとえば、Selenium RCサーバーにアクセスする場合)はどうすればよいですか? ここでの答えは可能な限り簡単です:Selenium RCにはそれと連携するHTTP APIがあるため、この場合の最も簡単な解決策は、テストプロセスを開始し、時々テストを完了するためにリモートサーバーをポーリングする任意の言語で小さなスクリプトを書くことです 次に、このスクリプトは、プラグイン「シェルの実行」を介してビルドスクリプトに接続されます。 また、スクリプトの成功または失敗は、スクリプトのリターンコードに基づいてハドソンによって決定されることも付け加えます。



アセンブリプロセスをセットアップしたら、プロセスの最も重要な部分、つまり開発者にアセンブリ結果を通知することを忘れません。 Hudsonを使用すると、特定の受信者とcommit'ovの作成者の両方にメール通知を設定でき、その変更により内訳が発生します。



さらに、PHPプロジェクトにHudsonを使用する場合は、Hudson Wikiの関連記事を読むことをお勧めします。


ステージングサーバー




パッケージのインストール


そのため、すでに前述したように、インフラストラクチャには、戦闘に可能な限り近いサーバーを構成する必要があります。 Hudsonは、トランク経由で収集された最新のプロジェクトパッケージをこのサーバーにインストールします。 これは私たちに機会を与えます:
  1. 戦闘条件に可能な限り近い条件で統合テストを実施し、
  2. 最新の機能を実証するためのプラットフォームを用意できます。

このサーバーをセットアップするときに解決する必要がある主な問題の1つは、パッケージのサイレントインストールです。 パッケージを不必要なダイアログなしでインストールする(つまり、Hudsonスクリプトまたはプラグインを使用してインストールできる)には、debconfを再構成する必要があり( dpkg-reconfigure debconf )、質問で使用される質問よりも重要度が高いことを示しますdebパッケージのインストールスクリプト。



さらに、ステージングサーバーにインストールされたパッケージ間に依存関係がある場合があります。 したがって、たとえば、クライアントプロジェクトはサーバープロジェクトに依存します。 この場合、「クライアント」パッケージのインストール時にステージングサーバーで必要な「サーバー」パッケージのインストールが確実に行われるようにする必要があります。

一見、debパッケージを介したプロジェクトの配布を考慮した明白な解決策は、
依存関係は、クライアントパッケージの依存関係制御ファイルに「サーバー」パッケージに関する情報を追加することにより、 dpkgによって管理されます。
この場合、CIサーバーによって収集されたすべてのパッケージがマージされる別のdebianリポジトリを選択し、パッケージをリポジトリにコピーするコマンドでビルドスクリプトを完了する必要があります。 さらに、新しいパッケージを追加するときに、このリポジトリ内のデータを自動的に更新するメカニズムを編成し、リポジトリプールへのアクセスを調整し(たとえば、Webサーバーを上げることによって)、このリポジトリをステージングサーバーのsources-listに追加する必要があります。 自動更新を手動で整理する場合(パッケージをリポジトリに配置した後にパッケージスキャナーを起動することにより)、aptを介して新しいパッケージをインストールできます。リポジトリがスケジュールに従って更新される場合、 dpkg -i packageなどのトリックを考案する必要があります。 apt-get -f install 。 debianリポジトリの設定について詳しくは、 こちらをご覧ください

ただし、このアプローチにはいくつかの欠点があります。 まず、依存関係は同じサーバーにのみインストールできます。 第二に、このアプローチはシステム全体を非常に複雑にします。これは、 KISSの原則と矛盾します(ロシア語の場合は「SMOKE」:D)。

私の意見では、最良の解決策は、バトルサーバーとのやり取りにのみリポジトリを使用することです。 同時に、リポジトリへのパッケージのアップロードは自動的に行われるべきではなく、開発者の決定に従って行われるべきです。 ステージングサーバーについては、メインパッケージのトランクからのパッケージとそのすべての依存関係がインストールされます。これにより、CIシステムの複雑さが大幅に軽減され、ステージングサーバーに最新の最新バージョンをインストールできます。
パッケージ。


DBを使用する


パッケージはデータベースを使用できます。 この場合、データベースはステージングサーバーにもインストールされ、データベース構造/データはdbdeployユーティリティを使用して更新されます。
dbdeployをプロジェクトに統合するには、2つの方法があります。
  1. SVNの各データベースに個別のプロジェクトが割り当てられ、その結果、HudsonでSVNフックによって開始される独自のビルドスクリプトを使用します(データベースが複数のプロジェクトで使用される場合、このオプションは意味があります)。
  2. dbdeployファイル構造がメインプロジェクトの一部になり、dbdeployデータベースバージョン更新スクリプトがパッケージのpostinstスクリプトで呼び出されます。


これとは別に、この場合、テスト中のデータベースデータの変更について疑問が生じます。 単体テストを作成するとき、データベースを操作せず、 モックオブジェクトを使用することは明らかです (たとえば、私はMockitoが好きです )。
ただし、単に「実際の」条件で動作する必要がある統合テストについてはどうでしょうか。 XUnitテストの場合、各テストをデータベースへのトランザクションの一部として実行できます。 私の意見では、dbdeployを介したデータベースのバージョン管理を考慮すると、このアプローチが望ましいと考えています。データベースに現在あるデータを常に把握しており、テストで安全にバインドできるからです。 ただし、Webインターフェイスをテストする場合(たとえば、Seleniumを使用する場合)、トランザクションのフレームワーク内で各テストを実行することはできません。
したがって、私の意見には2つのオプションがあります:Webインターフェイスのテストを開始する前に、利用可能なデータに基づいてデータベースのデータを完全に再初期化する
パッチ、またはデータベースからの特定のデータに結び付けられないようにテストを構築し(たとえば、Webインターフェイスを介してテストに必要なデータを作成するなど)、可能であれば「ゴミ」を残さないでください。


サーバーセレン


アプリケーションにWebインターフェースがない場合、上記で説明したように、ステージングサーバーでの統合テストは、XUnitテストの起動で構成されます。 ただし、ユーザーインターフェイスでは、Selenuimを使用してHTMLからデータベースまでのチェーン全体を完全にテストすると非常に便利です。

Seleniumは強力なWebアプリケーションテストシステムであり、条件付きで2つの部分に分けることができます。

明らかな理由から、2番目のオプションに興味があります。 Selenuimのインストールと設定は大きなトピックであるため、この記事にはポイントがありません。すべての情報はドキュメントにあります


備考


コミットの前にコードをコンパイルおよびテストするたびに、CIを手動モードで実行することもできます。 ただし、CIサーバーを使用したこのプロセスの自動化ははるかに便利です。 さらに、CIとナイトリービルドは同じものではないことを理解することが重要です。 後者では、バグを特定できますが、大きな遅延が発生しますが、CIの目標はエラーの迅速な検出です。 私の意見では、プロジェクトのアセンブリとテストが非常に長い時間を要するプロセスである場合にのみ、夜間アセンブリはCIの部分的な代替として機能します。 さらに、プロジェクトに単体テストと統合テストの両方がある場合、プロジェクトアセンブリを2つの部分に分割できます:最初の(単体テスト付き)はコミットするたびに実行され、2番目は統合テスト付きで実行されます-1時間に1回/日


おわりに


上で説明されたソリューションは働いて、 利益をもたらします。 ただし、ご存知のように、理論は残念ながら常に実践に対応するとは限りません。 CIシステムの実装には多くの問題の解決が必要でしたが、すべてが理想的に解決されたわけではありません。

戦闘機能に匹敵するステージングサーバーのリソースを誰かが提供する可能性は非常に小さく、ほとんどの場合、CIの原則の1つを根本的に損なう準放棄ホストマシン上の中規模の仮想マシンになります-同様の環境でのテスト。 そのため、統合テストには当初の計画よりもはるかに長い時間がかかる可能性があります。 したがって、私の場合、「継続性」は、SVNフックではなく、スケジュールに従ってテストを中断して実行する必要がありました。

一般に、チームに特定の開発文化がある場合(CIは万能薬ではなく、適切な処理を行うことで作業の品質を向上させることができるツールにすぎないという意味です)、CIの導入は正当化されます。

さて、おそらく最も重要なことです。実践が示しているように、CIシステムの統合はチームのタスクです。 これを解決するには、開発者、テスター、管理者の作業が必要になります。

Source: https://habr.com/ru/post/J108928/


All Articles