第3回「システムを創るプログラム」


前回のコラムでは自動化に使うツールについて詳しく説明しました。今回はそのツール上で動かす「プログラム」について説明します。

ツールとしてPuppet、Chef、Ansibleを紹介しました。これらのツールで使う「プログラム」ですが、Puppetは「manifest(マニフェスト)」、Chefは「recipe(レシピ)」、Ansibleは「playbook(プレイブック)」という名称でした。

先に言葉で説明するより、実際の「プログラム」を見てからの方が実感が沸きやすいと思いますので、httpdパッケージをインストールしてサービスを起動するだけの簡単な例を使って、それぞれの「プログラム」について説明します。

最初にPuppetの「マニフェスト」からお話しします。

Puppetでは「Puppet言語」と呼ばれる独自言語を使って、「プログラム」であるマニフェストを記述します。具体的には以下のようになります。

package { 'httpd':
  ensure => installed,
}

service { 'httpd':
  enabled => true,
  ensure  => runnning,
  require => Package ['httpd'],
}

Puppet言語で「パッケージをインストールせよ」や「サービスを起動状態にせよ」といった“命令”は、「リソース」と呼ばれるものになっています。この例ならば、パッケージのインストールは前段の「packageリソース」、サービスの管理は後段の「serviceリソース」が対応しています。

OSやディストリビュージョンの差異はリソースによって吸収されます。例えばCentOSとUbuntuを考えた場合、パッケージのインストールにCentOSはyum、Ubuntuではapt-getを用います。パッケージをインストールするpackageリソースが呼ばれ、インストールする際にはPuppet自身が動作している環境によってインストールのコマンドを使い分けます。そのため同じマニフェストをCentOSとUbuntuで書き換える事なく、使う事ができます。

※厳密にはパッケージ名称が異なる場合は、条件分岐など用いてマニフェスト側でパッケージ名称を切り替える必要はあります。

これらのリソースが「あるべき状態」を波かっこ{}で囲まれた部分で“属性”として記述します。この例ではインストールされている/されていない、実行中である/停止中であるといった状態を記述しています。

依存関係もこの属性として記述するようになっており、この例ではserviceリソースの属性として“require属性”が指定されています。この属性は「指定のリソースが適用済みである事」を保証させるもので、ここでは自身のリソースを適用する前にpackageリソースが適用されている事を保証させています。これはPuppet言語ではリソースの記述順と実際に実行される順番は一致しない特徴があるためで、この指定が無いと実行される順番は不定になります。

前回のコラムで説明した“冪等性(べきとうせい)”は、マニフェストで特に明記しなくても担保されます。既にリソースの属性で指定した状態になっていて、特に変更を加える必要がないとPuppetが判断した時、Puppetは何もせずスキップします。ただしコマンド実行など一部のリソースで冪等性を担保しきれないものもあります。

また、これらのリソースをノード別に記述したり、定義したりしたリソースの集合を、“クラス”や“モジュール”という一般的な言語のライブラリーのようなものとして管理する事もできます。

Puppetについてはここまでにして、次にChefの「レシピ」を紹介します。先ほどPuppet言語で記述したマニフェストと同じものを、Chefの「プログラム」であるレシピを記述すると以下のようになります。

package "httpd" do
  action :install
end

service "httpd" do
  action [:enable, :start]
end

Puppetのマニフェストと似ています。“命令”であるpackageやserviceを「リソース」と呼ぶのもPuppetと同様です。「あるべき状態」を属性としてブロックの中に記述するのも同様です。ただしPuppetでは独自のPuppet言語でしたが、ChefではRubyそのものなのが大きな違いです。そのためRubyのコードをレシピの中に書く事も可能です。冪等性もPuppetと同レベルで担保されます。

またレシピはRubyのコードですので、記述内容は上から順番に実行されていきます。Puppet言語は明示的にリソースの依存関係を記述しないと実行順が不定でしたので、これは大きな違いです。なお、Puppet言語のように明示的に指定するもの、Chefのように記述順に実行されるもの、それぞれにメリットデメリットはありますので、どちらが優れているというものではありません。

OSやディストリビュージョンの差異をリソースで吸収する事ができるのもPuppetと同レベルで実現していますが、特定環境のパッケージ管理コマンドを指定するような環境依存のリソースも複数用意されており、その点でPuppetよりも自由度が高くなっています。

レシピの段階では記述順に実行されるためリソース間の依存関係を記述しませんが、レシピを管理する単位である「cookbook(クックブック)」の中で、自身を適用するために必要なcookbookに対する依存関係を記述できます。cookbookを使うと、Puppetのモジュールと同様に、レシピや実行に必要な資材、情報などをまとめて管理する事ができます。

次にAnsibleです。Ansibleでは、Puppetのマニフェスト、Chefのレシピに相当するものをプレイブックと呼びます。プレイブックは、YAMLと呼ばれるシンプルな言語で記述し、次のようになります。

- yum:     name=httpd

- service: name=httpd
           enabled=yes
           state=started

serviceは、PuppetやChefと同じですが、packageの代わりにyumを利用しています。Ansibleでは、yum、packageをタスクと呼びます。PuppetやChefと異なり、ブロックではなく、YAMLで記述されるデータ構造で表現され、タスクは記述順に実行されます。

タスクを詳しくみてみると、serviceタスクはPuppetやChefと同じようにOSやディストリビューションの違いを吸収していますが、yumはRedHat系OS独自のパッケージインストールタスクを指定しているため、ディストリビューションの違いを吸収することはできません。packageというコマンドで抽象したところで、CentOSではApacheのパッケージ名はhttpdなのに対し、Debian/Ubuntu系ではapache2となっており、ディストリビューションの違いは吸収できません。だったら、コマンドレベルで分けた方がよいという考え方ですね。

AnsibleのプレイブックもChefと同じで記述順で実行されるため依存関係を記述しませんが、ノード毎の機能を定義する「Role」(Chefのcookbook相当)の中では、Role同士の依存関係を記述することができます。冪等性もPuppetやChefと同等に担保されます。

Ansibleは、タスクを記述するYAMLを別ファイルにして、Puppetのモジュールのようなライブラリー的な使い方をすることができます。また、任意の言語でAnsibleのタスク(Ansibleではこれをモジュールと呼びます)を作成して新たに定義したタスクをライブラリー的に簡単に使うこともできます。

次回はツールの“情報管理”について、詳細をお話します。



第3回「システムを創るプログラム」