サーバー設定ツール「Chef」の概要と基礎的な使い方
はじめに
近年注目されているサーバー管理ツール「Chef(シェフ)」は、ファイルに記述した設定内容に応じて自動的にユーザーの作成やパッケージのインストール、設定ファイルの編集などを行うツールだ。今回はこのChefについて、基本的な環境構築方法と使い方を紹介する。
前回はサーバーの設定管理ツールとして「Puppet」を紹介したが、今回紹介するChefは、このPuppetと人気を二分するサーバー設定管理ツールだ。
Chefは米Opscodeが開発しているオープンソースソフトウェアで、Rubyなどのオープンソースな技術を使って実装されている。ライセンスはApache License 2.0だ。同社はChefにいくつかの機能を追加した有償版の「Private Chef」やクラウド型の「Hosted Chef」といったサービスも提供しているが、本記事ではオープンソース版のChefについて紹介する。
ChefとPuppetはともに設定ファイルに応じてサーバーの設定を行うツールという点では同じだが、Chefでは「Cookbook(クックブック)」や「Recipe(レシピ)」と呼ばれる設定ファイルの再利用がしやすい構造になっている点が特徴だ。Cookbookの構成はやや複雑になっているものの、Chefの開発元やサードパーティが提供しているCookbookを利用することで、少ない記述でサーバーの設定を実行できるようになっている(図1)。
ChefのRecipeはRubyスクリプトとなっているのも特徴だ。そのためRubyとの親和性が高く、Rubyの知識がある人にとってはその動作の仕組みや構造を理解しやすいというメリットがある。ただ、エラー発生時にはRubyの出力するエラーメッセージを読まなければならないため、逆にRubyの知識がない人にとってはトラブルシューティングのハードルが高いかもしれない。
Chefのアーキテクチャと構成要素
Chefはフランス語での「料理人」という言葉が由来になっており、その構成要素も料理をモチーフにして命名されている。まず、Chefでは設定内容を記述したファイルのことを「Recipe(レシピ)」と呼ぶ。ChefのRecipeはRubyの実行ファイルとなっており、その拡張子は.rbだ。Chefは広義にはサーバー設定を行うための関数やクラスといった機能を提供するRubyライブラリおよびヘルパーアプリケーション集であるとも言える。
また、Recipeの管理単位を「Cookbook(クックブック)」と呼ぶ。CookbookにはRecipeに加え、設定ファイルを作成するための「Template(テンプレート)」や、環境に応じてその値を変更できる変数を定義した「Attribute(アトリビュート)」などが含まれている。Chefでは処理内容や対象とするアプリケーションごとにCookbookを作成するのが一般的で、Recipeでは環境に依存しないように設定を記述し、Attributeでその内容を制御する、という使い方が推奨されている。Recipe内からはほかのCookbookやRecipeを参照して利用することも可能だ。
Chefで管理対象とするサーバーの設定項目や設定ファイルなどは、「Resource(リソース)」と呼ばれる。リソースには型(リソースタイプ)があり、たとえばユーザーの作成や削除には「user」というリソースタイプを、Yumによるパッケージ操作については「yum_package」というリソースタイプを利用する。Chefでは基本的なシステム管理に必要となるリソースタイプがあらかじめで用意されているほか、Cookbook内で新たなリソースタイプを定義することも可能だ。
Cookbookは自分で作成するだけでなく、公開されているものを利用することも可能だ。Opscodeが運営するコミュニティサイト(http://community.opscode.com/)にはChefを開発するOpscodeやサードパーティの開発者によって作成されたCookbookがまとめられており、無料でダウンロードして利用できる(図1)。
公開されているCookbookは、MySQLやApacheといったアプリケーションのインストールや設定を行うものからユーザーの作成などシステム管理に関わるもの、iptablesなどOSの特定機能の設定を行うものまで、多岐にわたる。これらを利用することで、自分ではほとんどRecipeを書くことなしにサーバーの設定を行うことも可能だ。
Chefによるサーバー管理構成
Chefを利用するための構成としては、1台のサーバー内でCookbookの作成や管理、実行などをすべて完結させるスタンドアロン構成と、Cookbookをサーバーで集中管理し、クライアントはサーバーからCookbookをダウンロードして実行するというクライアント/サーバー構成の2通りがある。
まずスタンドアロン構成の場合だが、この場合はサーバーソフトウェアのインストールは不要で、クライアントのみが必要だ。Recipeの実行はクライアントに含まれる「chef-solo」というコマンドで行う。
いっぽうクライアント/サーバー構成の場合、サーバー側にはChefサーバー、「Node(ノード)」と呼ばれるクライアント側にはChefクライアントのインストールが必要となる。
クライアント/サーバー構成の場合、サーバー側でRecipeの作成や検証作業を行っても良いのだが、このような作業は別のマシン上で行うこともできる。このようにRecipeの作成や管理を行うマシンを「Workstation(ワークステーション)」と呼ぶ。Workstationでのレシピの作成や環境管理などには、「knife」というツールを利用する。knifeはChefクライアントをインストールすると同時にインストールされる。
さて、以下ではまずchef-soloを利用するスタンドアロン構成を使ってChefの基本的な使い方やCookbook/Recipeの利用方法を解説し、続いてクライアント/サーバー構成についても紹介していく。
なお、以下で利用している環境はクライアント/サーバーともにRed Hat Enterprise Linux 6.4互換であるCentOS 6.4(x86_64)だ。
chef-soloを使ったスタンドアロン構成
Chefで用いられるRecipeは、スタンドアロン構成でもクライアント/サーバー構成でも、どちらの場合でも基本的には同じものが利用できる。そのため、まずはスタンドアロン構成でChefを利用してRecipeの作成方法やCookbookの管理方法などを把握すると良い。また、クライアント/サーバー構成を取る場合はサーバーのインストールや設定などが必要であるため、管理するサーバーが少数の場合はそれぞれのマシンでchef-soloを使ったスタンドアロン構成を取ったほうが手間がかからない場合もある。
Chefクライアントのインストール
Chefはまだ歴史の浅いソフトウェアということもあり、ディストリビューションの公式パッケージとしては提供されていない場合が多い。また、提供されていてもそのバージョンが古いという場合もある。たとえばDebianでは安定版/不安定版の両方でパッケージが提供されているものの、提供されているのは現在の最新版であるバージョン11系(2013年8月現在でバージョン11.6.0)ではなく、その1つ前であるバージョン10系だ。そのため、最新版を利用したい場合はRubyのパッケージ管理システムであるRubyGemsを使ってChefクライアントを含む「chef」パッケージをインストールするのがおすすめだ。
CentOSの場合、RubyGemsを利用するにはRubyだけでなく、rubygemsパッケージのインストールが必要だ。そのほか、バイナリのビルドのためにruby-develやgcc、makeといったパッケージも必要となる。これらは次のようにyumコマンドでインストールできる。
# yum install ruby ruby-devel rubygems make gcc
続いて「gem install」コマンドでChefクライアントを含むchefパッケージをインストールする。
# gem install chef
これで、Recipeを実行するchef-soloコマンドやCookbook管理ツールであるknifeコマンドなどが利用できるようになる。
リポジトリとCookbookの作成
続いて、Cookbookの作成方法について紹介していこう。Chefでは、Cookbookなどのリソースを格納するディレクトリを「リポジトリ」と呼ぶ。まずは適当なディレクトリにリポジトリとして使用するディレクトリを作成する。
$ mkdir chef-repo
次に、リポジトリ内にCookbookを格納するディレクトリを作成する。
$ mkdir chef-repo/cookbooks
Cookbookはテキストファイルとディレクトリから構成される。手動で作成することも可能だが、「knife cookbook create <Cookbook名>」コマンドを利用することでそのひな形を自動生成できる。たとえば「setup-user」というCookbookを作成するには、以下のようにする。
$ cd chef-repo/cookbooks $ knife cookbook create setup-user -o .
knifeコマンドの「-o」オプションは、Cookbookを作成するディレクトリを指定するものだ。ここでは「.」を指定してカレントディレクトリに作成するよう指示している。なお、これを省略した場合は「/var/chef/cookbooks/」以下にCookbookが作成される。
Cookbookの構造
「knife cookbook create」コマンドで作成されたCookbookには、表1のようなディレクトリおよびファイルが用意される。
ファイル/ディレクトリ名 | 説明 |
---|---|
CHANGELOG.md | Cookbookの変更履歴を記述する |
README.md | Cookbookの概要などを記述する |
attributes | Cookbookで使用するデフォルトのAttributeを記述したファイルを格納するディレクトリ |
definitions | 「Definition」と呼ばれる、リソースを組み合わせて新たなリソースを作成するための設定ファイルを格納するディレクトリ |
files | Cookbook内で利用されるファイルを格納するディレクトリ |
libraries | 「Library」と呼ばれる、Chefの機能を拡張するためのRubyコードを格納するディレクトリ |
metadata.rb | Cookbookに関する情報(メタデータ)を記述するファイル |
providers | 「Provider」と呼ばれる、リソースに対する処理を実行するための設定ファイルを格納するディレクトリ |
recipes | Recipe本体を格納するディレクトリ |
resources | 「Resource(リソース)」と呼ばれる、Recipe内で使われる設定対象を定義するための設定ファイルを格納するディレクトリ |
templates | Cookbook内で利用されるテンプレートファイルを格納するディレクトリ |
このように並べると複雑そうに見えるが、多くの場合使われるのはこの一部のみで、Chefの機能を拡張したり新しいリソースタイプを定義しようとしない限りはdefinitionsやlibraries、providers、resourcesといったディレクトリは不要だ。不要なディレクトリについては削除して構わない。
ユーザーの管理を行うRecipeを作成する
それでは、実際にRecipeを作成して実行してみよう。ここで作成するのは、指定したグループおよびユーザーを作成するというものだ。Recipeは通常、knifeコマンドで作成されたCookbookのディレクトリ内にあるrecipesディレクトリ内に記述する。このディレクトリ内に「default.rb」というファイルが作成されているはずなので、今回はこのファイルにRecipeの内容を記述していく。
$ vi setup-user/recipes/default.rb
Recipeの一般的な記述ルールは以下のとおりだ。
<リソースタイプ> <リソース名> do <属性> <その値> : : action <リソースに対し実行するアクション> end
「リソースタイプ」は、設定対象とするリソースの種別を指定するものだ。たとえばユーザーを作成するなら「user」、グループを作成するなら「group」となる。デフォルトのChefで利用できるリソース一覧はドキュメントのCookbooks項目にまとめられているので、そちらを参照してほしい。
また、「リソース名」は設定対象とするリソースを識別するための名前だ。この値はリソースタイプによって意味が異なるが、多くの場合そのリソースの名前(たとえば設定対象がユーザー/グループであればそのユーザー名/グループ名、ファイルであればそのファイルパス、サービスであればそのサービス名)を指定する。
「属性」は、そのリソースに対する設定値を指定するもので、また「action」はそのリソースに対してどのような処理(作成する、削除する、有効にする、無効にする、など)を実行するかを指示するものだ。属性やactionはリソースごとに指定できる値が異なるので、実際にRecipeを作成する際はドキュメントを参照してほしい。
なお、Recipeファイルは先にも述べたとおりRubyスクリプトとして解釈される。そのため、その基本的な文法はRubyそのままだ。行中の「#」から行末まではコメントとして解釈され、文字列は「"」もしくは「'」で囲むことで指定する。変数や関数の定義および利用も可能だ。
さて、今回記述する内容は、下記の太字で示した部分だ。default.rbファイル内にはあらかじめCookbook名やRecipe名、コピーライトなどの文言がヘッダとして記述されているので、それに続いてRecipe内で実行する処理を記述していくことになる。
# # Cookbook Name:: setup-user # Recipe:: default # # Copyright 2013, YOUR_COMPANY_NAME # # All rights reserved - Do Not Redistribute # group "taro" do gid 1000 action :create end user "taro" do home "/home/taro" password '$6$OmC3KootOURrqOaP$63rwQ2bSE8op8wXa.ZWzgxm/iGvePTzEL5lOntmkPyYh5Qwh4lWs2DtyoEHcvsbYV5Q6a2ezzrZueb2ydrkhz0' shell "/bin/bash" uid 1000 gid "taro" supports :manage_home => true action :create end
ここではまず、「group」で「taro」というgroup型のリソースを定義している。actionには「:create」(リソースの作成)を指定し、「gid」属性ではグループIDを指定している。つまり、ここで指定した処理内容は「taro」というグループを作成し、そのグループIDを1000とする、というものになる。
続く「user」では、user型のリソースを定義している。user型のリソースもgroup型と同様、リソース名が作成するユーザー名となる。また、ホームディレクトリやパスワード、利用するシェル、ユーザーID、所属するグループなどの属性も指定している。なお、password属性の値にはハッシュ化したパスワードを指定する。ハッシュ化されたパスワードは「grub-crypt」コマンドで生成できる。
# grub-crypt --sha-512 Password: ←ハッシュ化したいパスワードを入力 Retype password: ←再度同じパスワードを入力 $6$OmC3KootOURrqOaP$63rwQ2bSE8op8wXa.ZWzgxm/iGvePTzEL5lOntmkPyYh5Qwh4lWs2DtyoEHcvsbYV5Q6a2ezzrZueb2ydrkhz0 ↑ハッシュ化されたパスワードが表示される
なお、ChefのRecipeではファイル内に記述した順番で処理が実行される。そのため、userの設定よりも先にgroupの設定を記述しておく必要がある。
chef-soloコマンド用の設定ファイルを作成する
作成したCookbookは、chef-soloコマンドで実行できる。chef-soloコマンドの挙動はコマンドラインオプションのほか、設定ファイルで指定でき、そのパスはデフォルトでは/etc/chef/solo.rbファイルとなっている。このファイルは自動的には作成されないので、手動で作成しておく必要がある。
# mkdir /etc/chef # vi /etc/chef/solo.rb
このファイルの内容についてはsolo.rbに関するドキュメントを参照してほしいが、今回はCookbookを格納しているディレクトリの指定のみを行うこととする。たとえば「/home/hylom/chef-repo/cookbooks」というディレクトリ内にCookbookを格納している場合、以下のように記述すれば良い。
cookbook_path ["/home/hylom/chef-repo/cookbooks"]
solo.rbファイルを作成したら、chef-soloコマンドでCookbookを実行してみよう。なお、実行にはroot権限が必要だ。実行するRecipe名は「-o」オプションで指定できる。Recipe名は「<Cookbook名>::<Recipe名>」という形で指定する。Recipe名を省略してCookbook名のみを指定することも可能だ。その場合、recipesディレクトリ内の「default.rb」というRecipeが実行される。
以下の例は、先ほど作成したsetup-userのdefault.rbというRecipeを実行したものだ。
# chef-solo -o setup-user Starting Chef Client, version 11.6.0 Compiling Cookbooks... Converging 2 resources Recipe: setup-user::default * group[taro] action create - create group[taro] * user[taro] action create - create user user[taro] Chef Client finished, 2 resources updated
出力結果からは、Recipeファイルに記述したとおりにグループとユーザーが作成されていることが分かる。ちなみに、同じCookbookを再度実行すると、次のようにメッセージが表示される。
# chef-solo -o setup-user Starting Chef Client, version 11.6.0 Compiling Cookbooks... Converging 3 resources Recipe: setup-user::default * group[taro] action create (up to date) * user[taro] action create * You must have ruby-shadow installed for password support!
ここでは「ruby-shadow」が必要と表示されているが、gemコマンドでこのパッケージをインストールすればこのメッセージは表示されなくなる。
# gem install ruby-shadow
実行するRecipeを指定する
先の例では-oオプションで実行するRecipeを指定したが、通常は実行するRecipeをファイルに記述し、そのファイルをコマンドラインオプションで指定するのが一般的だ。実行するRecipe一覧はJSON形式で記述し、chef-soloコマンドの-jオプションでそのファイルを指定する。
たとえば、先の「setup-user」Recipeを実行する場合、JSONファイルは以下のようになる。
{ "run_list": [ "recipe[setup-user]" ] }
/etc/chef/solo.jsonというファイルにこれを記述した場合、次のようにしてchef-soloを実行する。
# chef-solo -j /etc/chef/solo.json
なお、ここで指定したJSONファイルではRecipeやCookbookに与えるパラメータ(Attribute)を記述することも可能だ。こちらについては後述する。
パッケージをインストールさせる
続いては、Chefのデフォルトで用意されているリソースタイプを使ったRecipeをいくつか紹介しておこう。まず、yum経由でパッケージをインストールするには「yum_package」リソースタイプを利用する。
yum_package <パッケージ名> do action :install end
たとえば「sudo」パッケージをyumでインストールするRecipeは以下のようになる。
yum_package "sudo" do action :install end
また、RPMファイルからパッケージをインストールするには、「rpm_package」リソースタイプを利用する。
rpm_package <パッケージ名> do source <RPMファイルのパス名> action :install end
ただし、rpm_packageリソースタイプではローカルにあるファイルしかインストールできない。リモートにあるRPMファイルをインストールしたい場合、リモートからファイルをダウンロードする「remote_file」リソースタイプと組み合わせて利用する必要がある。
remote_file <ダウンロードしたファイルの保存先パス名> do source <ダウンロードするURL> end
以下のRecipeは、「http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm」というURLで公開されているRPMファイルをダウンロードしてインストールするものだ。
remote_file "#{Chef::Config[:file_cache_path]}/epel-release-6-8.noarch.rpm" do source "http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm" not_if "rpm -qa | grep -q '^epel-release'"" action :create notifies :install, "rpm_package[epel-release]", :immediately end rpm_package "epel-release" do source "#{Chef::Config[:file_cache_path]}/epel-release-6-8.noarch.rpm" action :nothing end
「#{Chef::Config[:file_cache_path]}」はChefの設定データベース内で「file_cache_path」というキーに格納されている値を参照するという意味だ。ここにはファイルキャッシュとして使用するディレクトリのパス名が格納されている。また、「not_if」は指定した文字列を実行し、その結果が真でなければ処理を実行するよう指定するものだ。ここでは「epel-release」パッケージがインストールされていなければ処理を実行するよう指定している。さらに、「notifies」属性はそのリソースに対する処理が実行された際に、続けて実行するリソースを指定するものだ。ここでは「rpm_package」型の「epel-release」というリソースについて、即座に(immediately)インストールを実行するよう指定している。
「epel-release」リソースのactionに「:nothing」が指定されていることも注目したい。これが指定されたリソースは通常何の処理も実行されない。つまり、このRecipeはepel-releaseパッケージがインストールされていない場合にのみパッケージをダウンロードしてインストールする、という内容になる。
テンプレートを使う
次は、設定ファイルを指定したディレクトリに作成するRecipeを紹介しよう。内容としては、httpdパッケージをインストールしてhttpdサービスを起動し、/var/www/testというディレクトリと/etc/httpd/conf.d/mysite.confという設定ファイルを作成する、というものだ。
設定ファイルを作成する場合、そのひな形はtemplates/defaultディレクトリ内に格納する。また、テンプレートファイルはeRuby形式(拡張子は.erb)で記述する。今回の例では、templates/default/mysite.conf.erbとして設定ファイルを作成する。
NameVirtualHost *:8000 <VirtualHost *:8000> ServerName <%= @hostname %> DocumentRoot /var/www/test ErrorLog logs/test-error_log CustomLog logs/test-access_log common </VirtualHost>
eRuby形式についての詳細は割愛するが、「<%」と「%>」 で囲まれた部分がRubyコードとして実行され、また「<%=」と「%>」で囲まれた部分はその実行結果に置き換えられる(「<% print」と「%>」で囲んだものと等価)。つまり、このテンプレートファイルでは「<%= @hostname %>」の部分がhostname変数の値に置き換えられる、ということになる。
この設定ファイルを利用するRecipeは次のようになる。
# httpdパッケージをインストールする yum_package "httpd" do action :install end # httpdサービスを起動する service "httpd" do action :start end # /var/www/testディレクトリを作成する directory "/var/www/test" do owner "root" group "root" mode 0755 action :create end # mysite.conf.erbというテンプレートから # /etc/httpd/conf.d/mysite.confという設定ファイルを作成する template "/etc/httpd/conf.d/mysite.conf" do source "mysite.conf.erb" owner "root" group "root" mode 0644 action :create variables({ # テンプレートに与える変数の値を指定する :hostname => `/bin/hostname`.chomp }) end
設定内容はコメントに記載したとおりだが、注目したいのが「/etc/httpd/conf.d/mysite.conf」リソースでの「variables」属性だ。ここではテンプレートに与えるhostname変数の値を指定しているのだが、Rubyの「`」(バッククォート)構文を使って/bin/hostnameコマンドを実行し、その結果の行末の改行を取り除いたものを変数に格納している。このように、Rubyの機能を利用してRecipeを記述できるのがChefの特徴の1つだ。
コマンドを実行する
「execute」リソースタイプでは、特定のコマンドを実行するリソースを作成できる。実行するコマンドは「command」属性で指定する。また、actionには「:run」もしくは「:nothing」が指定できる。
execute <リソース名> do command <実行するコマンド> action <実行するアクション> end
たとえば、sedコマンドを使って「/etc/yum.repos.d/epel.repo」という設定ファイルを編集するようなRecipeは、以下のように記述できる。
execute 'epel.repo' do command "/bin/sed -i -e 's/^enabled\s*=\s*1/enabled=0/g' /etc/yum.repos.d/epel.repo" action :nothing subscribes :run, "rpm_package[epel-release]", :immediately end
ここで指定されている「subscribes」属性は、指定したリソースが実行された後に特定のactionを実行する、ということを指定するものだ。この例の場合、「rpm_package[epel-release]」というリソースが実行された場合、即座にこのリソースに対し「:run」というアクションを実行する、ということを意味している。