BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル 継続的デリバリのパターン

継続的デリバリのパターン

原文(投稿日:2011/09/15)へのリンク

ビルドサーバがあって、継続的統合に必要なスクリプトがあれば、次に浮かび上がる問いは“ビルドの成果物をどうしたらいいだろう”ということです。 継続的デリバリ、つまり、ある環境から次の環境への自動的あるいは半自動的な移行は、多くの場合、企業を次の段階へ進化させます。

継続的デリバリはどのような大きさの企業にも適用できます。しかし、そのプロセスは企業毎に大きく異なります。例えば、すべてを4人で処理しているチームと正式な品質保証チームとしっかりとした運用サポート部隊がある大人数の企業のニーズが全く異なるのは明らかです。この記事ではすべてを包摂するソリューションを考えるより、様々なシナリオや選択肢を紹介したいと思います。

継続的統合のツールセットを選ぶ

継続的統合のツールセットを選ぶのは、最低限の重要な意思決定です。企業のためにワークフローを開発する場合、そのワークフローに適合するツールを選ばなければなりません。セットアップや構成に時間がかかることを考えれば、2、3日かけてカスタムのツールを作るのもいいかもしれません。

もっと重要なのは、ロックインのリスクがほとんどないということです。ソースコード管理システムを選ぶのとは違って、継続的デリバリのツールは欲しいだけ手に入りますし、乗り換えるのは自由です。開発環境かのビルドツールとそのビルドをステージング環境へ配置するツールが別のものであっても、品質保証チームには関係ありません。

基本シナリオ

基本シナリオでは、限られたリソースしかない企業のためのパターンを探します。つまり、3人か4人で開発者と運用担当を兼ねるIT部門のためのパターンです。普通、中小規模のビジネスの支援、特に技術そのものには注力しない企業のサポートはこのくらいの大きさのチームが行っています。大きな企業の場合も、お互いにやり取りをしない独立した小さなチームに分割することで、このような大きさチームになるように組織を作っているかもしれません。

継続的デリバリを導入しようとする前に、いくつかの準備が必要です。真っ先に必要なのは、ビルドサーバに合うソースコード管理システムです。ビルドサーバは継続的統合を実施するサーバにもなります。ひとつひとつのチェックインをビルドできるサーバでなければなりません。一般的に言って、この用途では“既成”のビルドサーバが欲しくなります。チェックインを監視して、自動でビルドをする仕組みを構築するのは、想像以上に大変です。利用しているソースコード管理システムにフックできるトリガがあるとしても、ビルド失敗時の通知機能のような他の機能を実装するには割に合いません。

リソースが限られているとしても、継続的デリバリにとってステージングサーバは重要です。ステージングサーバは本運用環境に可能な限り似せておく必要があります。ここで第一の問題は“予算がいくらあるか”ということです。本運用環境のデータベースサーバがとても高価な場合、同じ構成でステージング環境を作る余裕はないでしょう。また、そうしたいとも思わないでしょう。

本運用環境と似ている環境と作る場合、よくやってしまう失敗はハードウエアを似せ過ぎでしまうことです。例えば、本運用環境では秒間100リクエストを処理できるとします。この場合、ステージング環境に同じ性能のサーバを導入すると、テストの最中は秒間1、2リクエストしか来ませんので、構成が歪みます。理想的に言えば、本運用環境と同じリクエストをシュミレートするためにロードサーバを購入してセットアップするべきですが、これはとても高価で構築にも時間がかかります。このくらいの大きさのチームなら、ステージング環境には低性能のハードを導入するのがいいでしょう。

もうひとつ見過ごされがちなのは、ビルドのバージョン設定です。各ビルドは他のビルドと区別するために一意な識別子を持たなければなりません。ひとつの公開ブランチを使っている場合は、タイムスタンプや自動インクリメントのバージョン番号を使うのが効率的でしょう。より複雑なシナリオについては後述します。

構造

上述した環境を図にすると下記のようになるはずです。

(クリックして拡大)

デリバリの戦略

コードをコンパイルするためのビルドサーバとそのビルドを配置するステージング環境を準備したら、次は配置戦略を決めます。この場合、小さなチームにはふたつの基本戦略があります。ひとつは“チェックインに応じて配置する”戦略、もうひとつは“タイマーに応じて配置する”戦略です。

チェックインに応じて配置する

チェックインに応じて配置する戦略は即時に配置できるという利点があります。コードベースの大きさにもよりますが、新しい機能をチェックインしてからステージングサーバでテストできるようになるまで、1、2分もかかりません。

この戦略のマイナス面は、ステージングサーバが不安定になる傾向があるということです。私はある機能をテストしていたときに、突然ステージング環境に新しいバージョンが配置されてしまい、テストが台無しになった人を知っています。さらに悪いことに、多くの場合、ステージングサーバはデモ環境を兼ねますが、重要なプレゼンの時に不幸な事故が起きます。

もうひとつの問題はコードが混ざってしまうことです。例えば、連続して3つのチェックインがなされた場合、ビルドとデリバリのサイクルが3連続で走りますが、本当に必要なのは最後の1回のサイクルだけです。極端な状況では、コードがひどく混ざってしまった結果、ステージング環境が使い物にならないくらい動作しなくなってしまうこともあります。幸運にも、ほとんどのビルドサーバはビルドの実行を遅らせるオプションや、一定の間隔内で1回以上のビルドを行わないオプションがあります。

タイマーに応じて配置する

タイマーに応じて配置する戦略は、より予測しやすいです。ビルドがいつ始まるのか正確に分かるので、必要に応じて、ビルドの前にチェックインするか、後にチェックインするか計画できます。普通は1日に1回か2回のビルド/デリバリサイクルが走ります。

日次ビルドのマイナス面は開発の現場に不要なストレスを与えてしまう可能性があることです。開発者は急いで開発を仕上げて、ビルド開始時間の前にチェックインしようとするかもしれません。開発者が作業をしていないと思われる深夜にビルドを予定していれば、このストレスを最小限にできるかもしれませんが、こうすると次の日までチェックイン後のテストが実施できないということになります。

“日次ビルド”というような言葉を使い始めると、ビルドについて話しているわけではないということを忘れがちです。私たちが議論しているのは、ビルド/デリバリサイクル全体についてです。継続的統合を実践しているとビルドが失敗した場合すぐに分かります。間違った変更の修正やロールバックは単純な問題になるので、一度予定通りにデリバリを行えば、失敗を過ぎに見つけられるのです。

本運用環境に配置する

このシナリオは特定のビルドに対して集中的にテストする人がいない状況を想定しています。この場合、普通、新しいチェックインがされて、ビルドされてステージングサーバに配置された成果物は、そのまま本運用環境に配置されます。しかし、この本運用環境配置にもトレードオフを伴ういくつかの選択肢があります。

ステージングからの昇格

よく行われるのは、検証済みのビルド物をそのままステージング環境から昇格させる方法です。この方法の利点はとても手堅いということです。この方法なら、理論的には、テストしたのとは違う成果物を間違って配置してしまうことはありません。ステージングから本運用環境にファイルをコピーするスクリプトを書くのも簡単です。

しかし、他の多くのことと同様に、この理論は常に成り立つとは限りません。ステージングサーバに自動ビルドで配置を行っていると、テストが完了して、コーヒーを一杯飲んで戻ってくると全く違うビルドになっていることはよくあることです。さらに悪いことに、ビルドプロセスでステージングサーバのファイルを上書きしている最中にそのファイルが本運用環境に配置されていることもあります。

ビルドサーバからの昇格

ビルドサーバから直接昇格させる方法の方が優れたやり方です。この方法なら、上述した“ステージングから”昇格させる方法のタイミングの衝突問題を回避できます。さらに、あるビルドが本運用環境に配置されたということをしっかりと意識することができます。

しかしこの方法にも欠点があります。本運用環境に間違ったバージョンを配置しやすくなるのです。

リビルドと配置

3つ目の方法は、継続的統合サーバに本運用環境へ配置する処理を含む新しいビルドオプションを作成することです。しかし、この方法は注意する必要があります。理論的には、リビルドしたコードはテストしたものとまったく同じものはずですが、何かの間違いが起こる余地があります。

また、異なるコンパイラオプションを選べるようになるということです。この方法を採用すると、ステージング環境用のデバッグビルドと本運用環境用のリリースビルドを使い分けたいという誘惑にかられます。この2つのビルド構成の動作に違いがあると破滅的な結果をまねくこともあります。

継続的統合サーバのセットアップは簡単かもしれませんが、この選択肢には推奨しません。

シナリオ 2: 品質保証チームを導入する

この状況に品質保証チームを導入するともっと複雑になります。チーム間のコミュニケーションやスケジューリングが必要なので、もっと環境が必要ですし、しっかりとしたオーナシップも必要です。

構造

品質保証チームが入ると下記の図のように、非運用環境として最低でも3つのサーバが欲しくなるでしょう。コードの変更は一定の条件に従って、各サーバから次のサーバへと反映されます。

(クリックして拡大)

統合環境

ある機能がチェックインされた時に最初に影響を受けるのはこの環境です。これが統合環境と呼ばれるのは、すべての機能が完全に統合されるからです。この環境でビルドを簡単にテストして、品質保証チームに提供していいかどうかを確かめます。この環境は不安定になっても問題ありませんが、いつまでも同じビルドを残さないようにします。

品質保証環境

品質保証チームが作業をするのはこの環境です。この環境は統合環境を元に必要に応じて更新されます。

ステージング環境

ステージングサーバはデモと本運用環境リリース前の最終動作確認にのみ利用します。このデモや確認に使われるいはとてもしっかりとしたビルド物でなければなりません。また、必須というわけではありませんが、ステージング環境はデータベースやファイルシステムなど本運用環境のリソースにアクセスできます。

品質保証デリバリの選択肢

ビルドサーバから統合環境へコードを移動するには上述の基本シナリオのモデルのひとつを使います。統合環境から品質保証環境へ移行するには少し考える必要があります。複数のチームが関係するからです。私が見たうまくいくパターンをいくつか紹介します。

開発者が主導する

開発者が主導する場合、開発者がいつチェックして品質保証環境に移行するかを決めます。この方法は、言い方は悪いのですが、品質保証チームが開発チームの下にいる場合に使えます。開発者にとってはやりやすい方法のように思えますが、問題はどこにでも隠れているものです。例えば、品質上、既知の問題がある場合、品質保証チームはその問題が修正されるまでただ単に待つことになってしまいます。

極端な場合、タイマーを使って品質保証環境に自動的に配置する仕組みも必要かもしれません。

品質保証チームが主導する

これはほとんどのチームが導入できる典型的な方法です。この方法でも、開発者も関わります。統合環境で自分たちの仕事が問題ないか確認する必要があるからです。

この方法では、品質保証チームは、新しい機能のテストの準備をするとき、直近の“問題ないと分かっている”ビルド物を用意します。普通、この仕事は品質保証マネージャが行います。品質保証チームのメンバのニーズを一番把握しているのは彼だからです。この作業をメンバの誰もが出来るようにしている品質保証チームもあります。

テスト実施者が主導する

自動テストを追求している企業はこの方法がゴールになります。統合サーバにビルド物が乗ると自動テストが実行されます。すべてのテストにパスすると自動的に品質保証環境に配置されます。他の自動デリバリの仕組みと同じように、このサイクルはチェックイン単位で行われるか、タイマー制御で行われます。

この方法に必要な投資を低く見積もってはなりません。全体をテストできる仕組みが必要なだけでなく、すべてのテストにパスしなければなりません。テストに通らなかった場合、新しい機能障害で失敗したのか、先々で修正すればいい問題で失敗したのかはビルドサーバは区別できません。

回避策としては、テストを必須通過テストプロジェクトと条件付きのテストプロジェクトに分ける方法があります。特に、TDDスタイルのプログラミングをしている場合は、条件付きのテストプロジェクトから開始します。テストが正しくて使えることが確認でき、コードがそのテストをパスしたら、このテストは必須通過テストプロジェクトに追加されます。ビルドサーバは条件付きのテストは実行しませんが、必須通過テストの結果は尊重します。

ステージング/本運用環境デリバリの選択肢

継続的デリバリの思想では、品質保証チームの与えられたビルドの扱い方はふたつしかありません。ステージング環境に配置しないか、それとも配置するかです。品質保証チームは他の機能が完成するまで、ワーキングビルドのそのままにしておくわけではありません。

ここで、ビルドが“ワーキング”であるとはどういうことなのかという疑問がおこります。ワーキングビルドとは安全に本運用環境に配置できるビルドのことを言います。仮に不完全な機能があっても、それが今までと同じ動作なら、そのビルドは先に進みます。本運用環境の設定の中で、アプリケーションの使用を妨げるような具体的な問題が無い限り、ビルドが差し戻されることはありません。

一度ステージングに乗ったら、そのビルドは次のリリースサイクルで本運用環境に配置されます。本運用環境への継続的配置が常に望ましいわけではありませんが、週次あるいは日次で配置するのは無意味ではありません。重要なのはそのビルドが問題ないことが分かったら、すぐに本運用環境へ移行することです。品質保証チームのメンバが次の新しい機能の検証に注力できるようにしなければならないからです。数週間あるいは数ヶ月もステージングに同じビルドが乗っているとあらゆる問題が発生するでしょう。

品質保証環境とステージング環境を分離しているのは、ワークフローを促進するためです。あるビルドがステージング環境に乗ったら、すぐに次のビルドが統合環境へ引き上げられます。このようにステージング環境は利害関係者や第三者が触れるように常に安定させておく必要があります。一方で、品質保証チームにも別の作業環境が必要です。ステージング環境とステージング環境が同じだと、環境を不安定にしてはならないときは、品質保証チームが作業できなくなってしまいます。

構成の問題

複数の環境を持つと、構成ファイルは深刻な問題になる可能性があります。例えば、ステージングサーバは適切に構成して、顧客にテストメールを送信したり、本物の決済ゲートウェイを使って発注をしてしまったりしないようにしなければなりません。私が一緒に働いたある見習い開発者は間違って構成されたテストサーバで数百万ドルの債券を買ってしまいました(この場合、運良く本運用環境よりも債券の値段が高かったので取引は成立しませんでした)。

このようなことが起こるのは、本運用環境の構成の設定がソースコード管理システムに保存されていて、アプリケーションと一緒に配置されるからです。こうするのは、本運用環境でない構成の設定が本運用環境のマシンに配置されてしまうという同じような問題を回避するためです。

上述した問題を驚くほど簡単に回避する方法はあります。ビルドエージェントに構成ファイルに対する書き込み権限を与えなければいいのです。こうしておけば、間違って構成ファイルをチェックインしても、配置は失敗してエラーが発生するはずです。

しかし、残念なことに方法も独特の問題を孕んでいます。それは、新しい構成値が必要なときは、手動で設定しなければならないということです。この場合、失敗すると環境が壊れてしまいます。更新が頻繁な場合はとても危険な作業です。

関心事と構成ファイルの分離

“関心事の分離”という言葉はひどい設計を正当化する時に使われることが多いですが、誰が何に関心を持っているかについて考えるのには便利です。例えば、本運用環境のサポートを担当は開発者がどのようなロギングフレームワークを選んだのかには関心がありません。しかし、データベースの接続文字列とエラーが送信されるメールアドレスには関心があります。

私が推奨する構成ファイルのタイプは下記です。

環境設定: 各サーバ毎に設定する必要のある設定値。何か大きな出来事がなければ変更しない値です。例えば、新しいデータベースやファイルサーバを利用するときです。

構成としてのコード: 依存性注入フレームワークのXMLファイルのような構成情報。構成値のように見えますが、開発者以外は触ってはなりません。これらのファイルはソースコード管理システムに保存してサーバ上では読み取り専用にしておくべきです。

微調整: 環境依存ではないものの、運用を支援するために必要な設定があります。例えば、一括アップロードのバッチサイズであったり、ウェブページに対するリクエストへのタイムアウト設定であったりします。

この3つ構成タイプの中で、正しく行うのが大変なのが微調整です。理想的には、ソース管理システム上にはデフォルト値が設定されている構成ファイルがあり、それを各環境のマシン固有のバージョン管理されていないファイルでオーバーライドするのがいいでしょう。

構成とトレーニング

不要な構成の設定が追加されるのを防ぐ方法はドキュメントを用意して、各設定についてトレーニングをすることです。運用サポート担当にいつどんな目的で構成値を調整するか教える時間がとれないのなら、サポート担当はそのような調整はできないままなので、構成値は変わりません。

シナリオ 3: サービス指向アーキテクチャを持つ複数のチーム

サービス指向アーキテクチャを扱う場合、複数のチームで働くのは普通のことです。例えば、ひとつのチームがデータベースとサービスを構築し、別のチームがユーザインターフェイスを扱うというようなことは極めて一般的です。ふたつのチームの関係が深く、メンバ同士を交換している場合もあります。また、ふたつのチームが別々の会社で、世界の反対側にある場合もあります。しかし、ふたつの会社がどれほど離れていたとしても、基本的なパターンは同じです。

構造

シナリオ 2で見たように、サービスチームにはビルドをテストするためチームのメンバ以外に悪い影響を与えないような場所が必要です。一方で、UIの開発者には、いつも安定していることが保証されているサーバが必要です。そうでないと仕事ができないからです。このように上述したひとつのチームの場合のシナリオで必要な環境の他に“開発環境”が必要なのです。

(クリックして拡大)

UI統合フェーズを超えるとシナリオ 2と同じ流れになります。デリバリの選択肢も同じですが、UIチームだけがこのプロセスに関係することに注意してください。ビルド物の品質についてのUIチームの意見はサービスチームの意見よりも尊重されなければなりません。

開発デリバリの選択肢

開発環境にいつどうやってコードをデリバリするか。これは不安の種です。品質保証環境に動作しないビルドを配置してしまったら、品質保証チームは仕事が出来ずに、新しい機能に対するテストの準備や回帰テストの改善というような他の仕事をする他ありません。動作しないビルドを開発環境に配置してしまったら、UIチームは全く仕事ができません。この場合、上述した品質保証デリバリの方法はどれも有効ですが、自動化テストを使った方法が最も優れています。

サイドバー: 誰が統合テストを書くのか

分離されたサービス層を扱う場合、このサービス層を作るチームもサービス層を使うチームも自動テストを書くことが重要です。サービス層と作ったチームが内部の動作に最も詳しいからです。他の人が知らないようなテストを書くことが必要です。

しかし、だからといって、サービスを利用する側がテストを書かなくてもいいわけではありません。利用する側は、サービスの作り手側が想定していなかったシナリオについてテストを実施するだけでなく、自分たちのサービスに対する理解もテストできます。例えば、UIの開発者はある呼び出しは決してnullやマイナス値を返さないと想定しているとします。このような場合、そのUIが実際に利用するパラーメータのすべての組み合わせを試すことで、想定が正しいことを保証できます。

企業に単なる品質保証アナリストではなく、品質保証エンジニアがいると、サービス層に対して自動テストを書きすぎてしまうことがあります。UIのテストを自動化するとき、特に結果をUIを通じて確認する必要のないテストをするときこのようなことが起こりがちです。

シナリオ 4: 複数チームによる並列開発

これは本当に難しい状況です。今まで紹介したどのシナリオも、ひとつの開発ブランチを想定しています。複数の開発チームが平行して同じコードベースで作業する場合、いつどうやって開発部門が開発した機能を最も重要な開発フローに乗せるのか決めなければなりません。ここでは、私が知る成功した方法を紹介します。

機能プッシュモデル

この機能プッシュモデルでは、各チームが準備出来次第、変更をメインのブランチにプッシュします。このモデルには、自分のチームの都合で作業が行えるという利点があります。

マージ-プシュ

このモデルの場合、最も一般的にはローカルのブランチでマージとテストを行います。テストがうまくいったらメインのブランチに変更をプシュします。

このモデルの最大のリスクは自動マージがないことです。あるチームがある関数の名前やシグネチャを変更する一方、他のチームが新しいファイルを追加して同じ名前の関数を作成するのは、よくあることです。この変更が同じタイミングでチェックインされてしまったら、ビルドは失敗するものの、ソースコード管理システムは衝突を検出しません。

ロック-マージ-プッシュ

この方法にはロックをサポートするソースコード管理システムが必要です。新しいビルドをプッシュするときは、メインのブランチをロックします。新しい機能はローカルでメインブランチにマージして、テストして、メインブランチにプッシュします。ロックがあるのでマージに関する問題は解決しますが、テストに失敗したらすぐにロックを解放しなければなりません。

機能プルモデル

機能プルモデルでは、チームは変更を発行してはなりません。誰かが変更を管理するのではなく、チームがメインブランチに機能をプルする責任を持ちます。こうすることで、品質保証チームはテストの準備ができた変更のみ受け取ることができます。

この機能プルモデルには、統合された項目を追跡する機能をサポートするソースコード管理システムが必要です。タスク番号付きのタグ変更機能があるだけでは不十分です。“機能XをブランチYにマージする”という機能が必要です。また、ソースコード管理システムで必要なコードの変更を特定しなければなりません。これは手動でもできますが、時間がかかり間違いも起きやすいです。

単純なマージなら、変更管理担当のエンジニアが対処するのが一般的です。複雑なマージをする場合、時にチームがいつもとは違うやり方でブランチを切っている場合は、チームで支援しなければなりません。

構造

どのように機能がデリバリの流れに乗せる場合も、構造は同じです。各チームがそれぞれの統合環境を持ち、基本シナリオを遂行するかのように継続的に、次の環境へとビルドを発行します。各チームの環境が共通の統合環境へ流れ込みます。その後の流れは上述した通りです。

(クリックして拡大)

ホットフィックスをどうするか

今までのすべてのシナリオではホットフィックスという概念を説明していませんでした。これには理由があります。継続的デリバリの思想を受け入れるのならホットフィックスというものは存在しないのです。一度統合環境が変更されたら、それは素早く本運用環境に反映されなければなりません。この考えでは、ホットフィックスは必要ありません。機能開発よりも普通のバグ修正を優先して行えばいいのです。

残念ながら、現実は常に理論に従うわけではありません。機能の品質に問題があったり、対象の機能が多かったりする場合、品質保証チームで変更反映の流れが必要以上に止まってしまうこともあるでしょう。同じように、契約上の責任がある場合やしっかりと広告して特定の日に発表する場合は、本運用環境への配置が遅れるでしょう。このような状況の場合は、ホットフィックスが必要かもしせません。最良の対策は作業プロセスを無視して修正作業を行うことです。形式にこだわって、企業や顧客に過度の負担をかけてはなりません。混乱が収まって、危機を克服したら、原因は何だったのか調査します。

継続的統合の目的はホットフィックスを扱いやすくすることではありません。コーディングやテストの標準を開発してホットフィックスの必要をなくすのが目的です。何かの失敗に遭遇したときは、コーディングやテストを改善して大きなバグを生み出さないようにするための良い機会です。また、開発から本運用環境配置への流れを止めてしまったスケジューリングの方針の欠点を見直す機会にもなるでしょう。このような点に注力することで、厳密な手順に則ってバグの修正ができるようになります。

要するに、継続的デリバリにとっては継続的改善が本質なのです。

著者について

Jonathan Allen氏は2006年よりInfoQで執筆を行い、現在は.NETキューの主任編集者。InfoQでニューズを執筆したい方、教育的な記事を書きたい方はjonathan@infoq.comから氏へ連絡してください。

 

 

この記事に星をつける

おすすめ度
スタイル

BT