アジャイル DevOps: あらゆるものをバージョン管理する

ソフトウェア・システムのすべての構成要素をバージョン管理しなければならない理由を学ぶ

バージョン管理しなければならないのはどのタイプのソフトウェア・システム成果物なのでしょうか?連載「アジャイル DevOps」の今回の記事で DevOps のエキスパートである Paul Duvall が DevOps チームにお勧めするのは、アプリケーション・コード、インフラストラクチャー、構成、データ、さらには内部システム成果物までをもバージョン管理することです。これらのものをすべてバージョン管理することによって、ソフトウェアを迅速かつ頻繁にユーザーに提供できるようになります。

Paul Duvall, CTO, Stelligent

Paul DuvallPaul Duvall は、Stelligent の CTO です。数々の主要なソフトウェア・カンファレンスでメインの講演者を務めている彼は、ソフトウェア・プロジェクトの開発者、プロジェクト・マネージャー、アーキテクト、およびテスターと、実質上すべての役割を担当した経験を持ちます。彼が中心的な著者となった共著書『継続的インテグレーション入門 開発プロセスを自動化する 47 の作法』(Addison-Wesley、2007年) は、2008年度の Jolt Award を受賞しました。また、『Startup@Cloud』、『DevOps in the Cloud LiveLessons』(Pearson Education、2012年6月) の著者でもあります。その他の本にも貢献し、developerWorks では 20 の記事からなる連載「万人のためのオートメーション」を書いています。彼は、継続的デリバリーとクラウドを利用して、高品質のソフトウェアをより短時間に、より頻繁に提供することに熱意を持っています。Stelligent.com で彼のブログを読んでください。



2013年 1月 24日

この連載について

開発部門は、運用部門から多くのことを学べます。同じく運用部門も開発部門から多くのことを学べます。この連載では、運用部門の考え方と開発部門の考え方を相互に適用し、ソフトウェア製品をこれまでよりも迅速かつ頻繁に提供可能な総体的要素として捉える場合の実用性を探ることに話題を絞ります。

あらゆるものをバージョン管理するとは、その言葉どおり、インフラストラクチャーから、構成、アプリケーション・コード、そしてデータベースに至るまでのすべてを管理するということです。このすべてをバージョン管理することにより、ソフトウェア・システムを作り出すための唯一正式な構成要素を持つこととなり、ソフトウェア・システム (そして、ソフトウェアを作成するために必要なすべてのもの) を 1 つの総体的な単位としてとらえることができるようになります。チームがすべてのものをバージョン管理すれば、始終アプリケーション・コードのどのバージョンがどのデータベースに対応しているのかを調べたり、ソフトウェア・アプリケーションのどのバージョンがどの環境で動作するのかを調べたりすることはありません。そのようなチームは、ソフトウェア・システムを構成するソース・ファイルを共有サーバー上に置くことも、ラップトップ上のフォルダー内に隠すことも、バージョン管理されていないデータベースに組み込むこともしません。

すべてのものをバージョン管理していれば、許可されたチーム・メンバーの誰もが随時、(アプリケーション・コード、構成、インフラストラクチャー、およびデータで構成される) ソフトウェア・システムのどのバージョンでも再現することができます。また、バージョン管理リポジトリー (ほんの一例を挙げると、Subversion、Git、CVS、Rational ClearCase など) にコミットされたバイナリー以外の成果物のみ (ただし、変更されることのないライブラリーは例外です) を使用して、ソフトウェア・システム全体を作成することができます。

私の経験では、すべてのものをバージョン管理するという概念を理解するのは簡単ですが、この概念が完全に適用されている例を目にすることはほとんどありません。もちろん、アプリケーション・コードや、構成の一部、そしておそらくデータについては、バージョン管理が行われているのを目にするでしょう。ソフトウェア開発で使用するライブラリーを管理するために、依存関係のリポジトリーやツール (例えば、Nexus) を使用しているチームもあれば、共有ドライブとバージョン管理システムの組み合わせを使用しているチームもあります。けれども、構成のすべて、構成が依存するコンポーネントのすべて (yumapt-getrpm などのパッケージ・リポジトリーのコンポーネント)、そしてデータベースとそのデータベースを構成するデータを作成するために必要なスクリプトのすべてをバージョン管理している企業を見かけることはあまりありません。

すべてのものをバージョン管理しているかどうかを判断するには、単純に「バージョン管理システムから特定のリビジョンを取得する 1 つのコマンドを実行して、特定のバージョンの (インフラストラクチャー、データ、ソフトウェア、構成を備えた) 完全なソフトウェア・システムを再現できるかどうか?」を考えてみてください。これができなければ、すべてのものをバージョン管理していることにはなりません。

複数のリポジトリー

然るべき理由から、バージョン管理対象のデータベースに保管されている構成アイテムを、複数のソース・リポジトリーまたは依存関係管理リポジトリーを使用して保守しなければならないこともあります。この場合に有効な手法は、これらの複数のリポジトリーに論理バージョンを適用し、ソフトウェア・システムの各リビジョンを識別するために使用できる「構成アイテム一覧」を生成することです。

すべてのものをバージョン管理する際に重要となる前提条件は、すべてのソース成果物をスクリプトの形にすることです。このことは、インフラストラクチャー、データ、構成、アプリケーション・コードに当てはまります。唯一の例外として、決して変更を加えることなく使用されるライブラリーとパッケージ (JAR ファイルや RPM パッケージなど) はスクリプト化する必要はありませんが、すべてのソース成果物がスクリプト化されていれば、これらの成果物を簡単にバージョン管理することができます。

この記事では、各種のソフトウェア成果物をコードに記述する方法と、これらのコードを効果的に利用する方法を説明します。記事に記載するコードは、各コンポーネントをスクリプトとして実行およびバージョン管理できるように定義する方法を例示するものであり、コンポーネント・タイプごとのスクリプトを作成および実行する方法を説明するものではありません。この記事で説明するコンポーネントについては、連載「アジャイル DevOps」および連載「万人のためのオートメーション」の記事にさらに詳しい例が記載されています。

アプリケーション・コード

アプリケーション・コードは、ソフトウェア・システムの構成要素のなかでも、バージョン管理しなければならないものとして、おそらく最も明白な要素です。アプリケーション・コードの一例として、リスト 1 に、特定のデータを取得するためにオブジェクトのメソッドを呼び出す単純な Java クラス (UserServiceImpl) を記載します。

リスト 1. Java アプリケーション・コード
...
public Collection findAllStates() {
    UserDao userData = new UserDaoImpl();
    Collection states = userData.findAllStates(UserDao.ALL_STATES);
    return states;
}
...

図 1 に、新しいアプリケーション・コードのソース・ファイル (リスト 1 に記載した UserServiceImpl.java) を GitHub でホストされている Git バージョン管理リポジトリーにコミットする方法を示します。

図 1. 新規ソース・コード・ファイルを Git リポジトリーにコミットおよびプッシュするコマンド
新規アプリケーション・ソース・ファイルをコミットするには、(1) ファイルを追加対象としてマークし (git add UserServiceImpl.java)、(2) git コマンドとコメントを使用してコードをコミットして (git commit -m 'added user service impl class')、(3) git push コマンドでコードをマスター・リポジトリーにプッシュします。

ソフトウェア・アプリケーションを作成するために必要なアプリケーション・コードはすべて、バージョン管理リポジトリーにコミットしてください。他のすべてのソース・ファイル (インフラストラクチャー・コード、データ、構成) にも、これと同じプロセスを使用します。


インフラストラクチャー

アプリケーション・ソース・ファイルの場合とまったく同じように、インフラストラクチャーもコードとして定義することができます (「アジャイル DevOps: インフラストラクチャーの自動化」を参照)。したがって、インフラストラクチャーもバージョン管理システムでバージョン管理することができます。インフラストラクチャーのスクリプトにはマニフェスト、モジュール、クックブックなどが指定される場合もありますが、いずれにしても、環境を作成するために実行できるテキスト・ベースのスクリプトであることに変わりはありません。

インフラストラクチャーをコードに定義することがベスト・プラクティスであるとしたら、人々は一般にどのような手法を採っているのでしょうか?それは、環境を毎回手作業で構成する「作品」を寄せ集めるという手法か、手作業による手順と自動スクリプトの実行を組み合わせるという手法です。いずれにしても、エンジニアが毎回その一連のステップを実行しなければならないことから、どちらの手法もボトルネックとなります。このボトルネックを是正するために、説明書に 1 つひとつのステップを入念に記述しようとする人もいるかもしれませんが、問題は、説明書に誤りやステップの記述漏れがある場合や、一連のステップを実行するオペレーターが正確に手順に従わない場合もあることです。したがって、1 つのコマンドで実行できるコードにインフラストラクチャーを完全に記述することが、唯一の解決策となります。

例えば、リスト 2 に記載する Puppet マニフェストは、PostgreSQL データベースをインストールする手順をコードに記述しています。このコードは、コマンドラインから実行することも、CI (Continuous Integration: 継続的インテグレーション) サーバーから実行することもできます。

リスト 2. PostgreSQL のインストールを記述する Puppet マニフェスト
class postgresql {
  
  package { "postgresql8-server":
    ensure => installed,
  }
  
  exec { "initdb":
    unless => "[ -d /var/lib/postgresql/data ]",
    command => "service postgresql initdb",
    require => Package["postgresql8-server"]
  }
...

このマニフェストは単独で、サーバーのダウンロードから、インストール、実行までを行います。さらに他のマニフェストを追加することによって、環境全体をスクリプトに記述することができます。これらのスクリプトをバージョン管理リポジトリーにチェックインすれば、インフラストラクチャーのすべてのリビジョンを追跡できるようになるため、より効果的な変更管理が実現します。


構成

構成は、環境によって異なる情報を定義します。そのような情報の例には、ディレクトリーとファイルの場所、ホスト名、IP アドレス、サーバー・ポートなどがあります (リスト 3 を参照)。環境を作成する際に実行されるスクリプトでは、この構成情報を使用して、ビルド、デプロイメント、そしてテストを実行します。

リスト 3. properties ファイルに定義された構成
jboss.home=/usr/local/jboss
jboss.server.hostname=jenkins.example.com
jboss.server.port=8080
jboss.server.name=default

リスト 4 のコードは、構成情報を NoSQL データベースにロードする Ruby スクリプトです。

リスト 4. 動的構成情報を NoSQL データベースに書き込むスクリプト
AWS::SimpleDB.consistent_reads do
  domain = sdb.domains["stacks"]
  item = domain.items["#{opts[:itemname]}"]
  
  file.each_line do|line|
    key,value = line.split '='
    item.attributes.set(
      "#{key}" => "#{value}")
  end
end

リスト 4 の構成情報 (IP アドレス、ドメイン名、マシン・イメージなど) はすべて動的に取得できるため、この構成でハード・コーディングする部分はまったくありません。構成のすべてを動的にすることは不可能な場合でも、クラウドを使用すれば、ほとんどのソフトウェア・デリバリー・システムの悩みの種となりがちな、ハード・コーディングされた構成の部分を大幅に減らすことができます。


データ

スクリプト化とバージョン管理に伴う作業を多いととらえるべきか?

私が最近、データベース、データ、変更をスクリプト化する概念を説明したときに、「あまりにも大量の作業になりそうだ」という意見がありました。私はそれに対して、本当の状態がわからないデータベースを保守するほうが、遥かに作業量が多く、危険であると答えました。データベースが「ブラック・ボックス」である場合、そのデータベースを保守できるのは、プロジェクトに携わる数少ない DBA だけです。データの変更がバージョン管理されていなければ、DBA が記憶を頼りにデータベースを稼働状態に維持することになります。

リレーショナル・データベースの構造は、DDL (Data Definition Language) スクリプトで定義することができます。DDL スクリプトには、データベース、テーブル、プロシージャーなど、データを除くあらゆるものを作成するためのスクリプトが含まれます。データを定義する場所は、DML (Data Manipulation Language) スクリプトです。DML スクリプトには、挿入、更新、削除のステートメントが含まれます。

リスト 5 に、データベース・テーブルを作成する手順を実行する DDL スクリプトの一部を抜粋します。

リスト 5. データベース・テーブルを作成する DDL
CREATE SEQUENCE hibernate_sequence START WITH 1 INCREMENT BY 1 NO MINVALUE \
NO MAXVALUE CACHE 1;
ALTER TABLE public.hibernate_sequence OWNER TO cd_user;

CREATE TABLE note ( id bigint NOT NULL, version bigint NOT NULL, cd_id bigint NOT NULL, \
note character varying(10000) NOT NULL, note_date_time timestamp without time zone \
NOT NULL);
ALTER TABLE public.note OWNER TO cd_user;
...

リスト 6 に、Liquibase XML スクリプトの抜粋を記載します。Liquibase は、データベース変更管理用のオープンソースのドメイン特化言語 (DSL) です。

リスト 6. 既存のデータベースの列を変更する Liquibase スクリプト
<changeSet id="9" author="jayne">
  <addColumn tableName="distributor">
    <column name="phonenumber" type="varchar(255)"/>
  </addColumn> 
</changeSet>
...

このようにデータベースの作成、データ、そしてデータベースの変更は、スクリプトに定義することができます。これらのスクリプトはビルド・プロセスの一貫として実行され、バージョン管理リポジトリーで完全にバージョン管理されます。


ビルドとデプロイメント

ビルドは、すべてのソース・ファイルをコンパイルして 1 つのディストリビューションにパッケージ化します。アプリケーション・コードの場合、ディストリビューションは WAR ファイルなどのバイナリーになるのがほとんどです。ビルドでインフラストラクチャー・スクリプトを実行して、環境を作成することもできます。作成される環境を仮想インスタンス、またはインスタンスを定義可能なイメージにすることも可能です。さらにビルドでは、データベース・スクリプトからデータベースを生成することもできます。ビルドで使用するのは、構成ファイルまたはデータベースに定義された構成です。リスト 7 に、ビルドのディレクトリーおよび構成を定義する Maven ビルド・スクリプトの一部を抜粋します。

リスト 7. Maven ビルド・スクリプトの抜粋
...
<build>
  <finalName>embeddedTomcatSample</finalName>
  <plugins>
      <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>appassembler-maven-plugin</artifactId>
          <version>1.1.1</version>
          <configuration>
              <assembleDirectory>target</assembleDirectory>
              <programs>
                  <program>
                      <mainClass>launch.Main</mainClass>
                      <name>webapp</name>
                  </program>
              </programs>
          </configuration>
          <executions>
              <execution>
                  <phase>package</phase>
                  <goals>
                      <goal>assemble</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
  </plugins>
</build>
...

いわゆる「デプロイメント自動化ツール」と呼ばれるツールを提供しているベンダーはいくつかありますが、その呼び名には多少誤りがあります。それは、これらのツールが行うのはデプロイメントの自動化ではなく、オーケストレーションだからです。これらのツールでデプロイメントのステップと順序を記述することはできても、実際のデプロイメントは一連のスクリプトや手動のプロセスによって行われます。デプロイメント成果物とワークフローのバージョン管理をサポートするツールというものには、めったにお目にかかれません。これらのツールのなかには内部でバージョン管理を行うものもありますが、そのツールを常に使用しているのでない限り、ソフトウェア・システムの特定のリビジョンを探す場合にはほとんど役に立ちません。ツールを常に使用するとしても、デプロイメントのバージョン管理とその他のソース成果物のバージョン管理は切り分けられています。しかし、このようなベンダーのツールを使用している場合でも、デプロイメントのバージョン管理を他と切り分ける必要はありません。代わりの手法となるのは、デプロイメント全体を、Capistrano などのデプロイメント自動化 DSL で記述することです。デプロイメントをスクリプトにすれば、デプロイメントのバージョン管理が可能になり、1 つのコマンドを実行するだけで完全なデプロイメントを実行することができます。自動化されたデプロイメントに自動テストを組み合わせて、オーケストレーション・ツールでそのデプロイメント・スクリプトを実行してください。

Capistrano は、複数のプラットフォームでのデプロイメントを記述するための DSL です。Capistrano を使用すると、複数のノードおよび環境の役割にまたがるデプロイメントに対し、サーバーの停止、ファイルのコピー、ワークフローの適用などのタスクを定義することができます。リスト 8 に、Capistrano スクリプトの抜粋を記載します。

リスト 8. Capistrano でのデプロイメント・スクリプトの抜粋
namespace :deploy do
  task :setup do
    run "sudo chown -R tomcat:tomcat #{deploy_to}"
    run "sudo service httpd stop"
    run "sudo service tomcat6 stop"
  end

...

デプロイメントを記述するには、DSL を使用するのが効果的な方法です。DSL を使用してデプロイメントのすべてのステップをスクリプトにすれば、これらのスクリプトは独自仕様のツールと密接に結合されないため、継続的デリバリー・パイプラインを実施しているチームにぴったりの方法となります。デプロイメントをスクリプトとして定義した後は、デリバリー・パイプラインの他のすべてのコンポーネントと同じようにデプロイメントをバージョン管理することができます。


システム

進歩的なチームの間では、インフラストラクチャー、構成、データ、およびアプリケーション・コードをバージョン管理することが望ましいという意見が大多数を占めるようになってきていますが、バージョン管理できるコンポーネントはこれらのほかにもあります。それは内部システム (CI 環境を定義するための構成など) です。ソフトウェア・デリバリー・システムの各部を作成するために使用している環境が機能しなくなったとしたら、ソフトウェア・システムがどうなるかを考えてみてください。こうした場合に備えて、ソフトウェア・システムの環境を完全にスクリプト化した後に、インフラストラクチャー自動化ツールを使用して、ソフトウェア・デリバリー・システムを作成するための環境を完全にスクリプト化することができます。このインフラストラクチャー・コードと、CI 構成に対して行われた変更 (CI ジョブ構成など) はバージョン管理されます。リスト 9 に、Jenkins サーバーとジョブ構成をバージョン管理する例を記載します。

リスト 9. Jenkins サーバーと構成変更をバージョン管理する単純な bash スクリプト
#!/bin/bash -v

# Change into your jenkins home.
cd /usr/share/tomcat6/.jenkins

# Add any new conf files, jobs, users, and content.
git add *.xml jobs/*/config.xml plugins/*.hpi .gitignore

# Ignore things we don't care about
cat > .gitignore <<EOF
log
*.log
*.tmp
*.old
*.bak
*.jar
.*
updates/
jobs/*/builds
jobs/*/last*
jobs/*/next*
jobs/*/*.csv
jobs/*/*.txt
jobs/*/*.log
jobs/*/workspace
EOF

# Remove anything from git that no longer exists in jenkins.
git status --porcelain | grep '^ D ' | awk '{print $2;}' | xargs -r git rm

# And finally, commit and push
git commit -m 'Automated commit of jenkins configuration' -a
git push

ソフトウェア・システムの他のあらゆる構成部分と同様に、このスクリプトをバージョン管理リポジトリーでバージョン管理することができます。


バージョン管理の対象

参加してください

developerWorks の Agile transformation ゾーンでは、皆さん個人、あるいは皆さんの組織がアジャイル開発の原則に基づく基礎を固めるために役立つニュース、ディスカッション、トレーニングを提供しています。

今回の記事では、任意の時点でソフトウェアをリリースすることを可能にする継続的デリバリー・プラットフォームを開発チームと運用チームが協力して作成するときには、すべてのものがバージョン管理の対象となること、そしてすべてのものがバージョン管理されることを説明しました。インフラストラクチャー、データ、アプリケーション・リソースのすべてがバージョン管理されていれば、ソフトウェア・システムを提供するシステムを構成するコンポーネントもバージョン管理することができます。ユーザーのために開発するソフトウェア・システム、そしてこれらのソフトウェア・システムをユーザーに提供する際に使用する内部システムのすべてのリソースをスクリプト化することで、あらゆるもののバージョン管理が可能になります。

次回の記事では、動的構成管理について説明します。この手法では、静的な環境固有のプロパティーを構成に使用することがなくなります。

参考文献

学ぶために

製品や技術を入手するために

  • Rational ClearCase: Rational ClearCase は高度なバージョン管理、ワークスペース管理、並行開発のサポート、そしてビルド監査により、生産性を向上させます。
  • IBM Tivoli Provisioning Manager: Tivoli Provisioning Manager は物理サーバー、仮想サーバー、ソフトウェア、ストレージ、およびネットワークを自動化して、動的なインフラストラクチャーを実現します。
  • IBM Tivoli System Automation for Multiplatforms: Tivoli System Automation for Multiplatforms は、エンタープライズ規模のアプリケーションおよび IT サービスの高可用性および自動化を実現します。
  • ご自分に最適な方法で IBM 製品を評価してください。評価の方法としては、製品の試用版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品を使用することもできます。また、SOA Sandbox では、数時間でサービス指向アーキテクチャーの実装方法を効率的に学ぶことができます。

議論するために

  • developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べることができます。
  • developerWorks の Agile transformation コミュニティーでは、組織がアジャイル開発の原則に基づく基礎を固めるために役立つニュース、ディスカッション、トレーニングを提供しています。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=DevOps, Open source, Java technology
ArticleID=855658
ArticleTitle=アジャイル DevOps: あらゆるものをバージョン管理する
publish-date=01242013