1.はじめに

こんにちは!
先月ドワンゴは歌舞伎座に引っ越したので"銀座"にあうように人生初の美容院に行ったけど、結果は床屋で切ったのと変わりなかった氏家です。

前回はChefとはなんぞや、というところで終わってしまいましたが、今回は導入編で、

- 最新のChef Solo 11.6.0、Knife Solo 0.3.0 限定
- 導入から実行するまでの、迷わない セットアップ手順 及びファイル構造の新定番!

を提案したいと思います。

それは、私がChef Soloを導入しようとしたときに引っかかった

  • インストールして使い始めるまでのとっつきにくさ
  • 開発環境と本番環境をどうCookbookで表せばいいのか
  • 用途の違う複数のサーバーや、複数のプロジェクトを、どう管理するのがよいか
  • 開発メンバーにも秘密にしたい秘匿情報は…
といった問題をどう解決したか、そして少しでもChef導入の手助けになればと思っています。

※素のOSにChefを導入する前提の話になります。Vagrantとの連携前提の話については割愛させてください。

2.Chef Server と Chef Solo

おさらいですが、Chef ServerとChef Solo・Knife Soloの違いを図にしてみたいと思います。

af0ee4891c5d844f2a1fd2039f7cd0fe023197e2
Chef Serverの場合は、セットアップするサーバーの情報をChef Serverに登録、Chef Client側で通信先のChef Serverを登録しておいて"chef-client"を実行

be6936ccfa6f3a5c52de65f6c55b9442a7c96f41
Chef Soloは、サーバーの情報もすべてファイルとして保存しておき、Cookbook含めそれらのファイルをセットアップサーバーにコピーして、セットアップサーバーにSSHでログインして"chef-solo"を実行

3a086bd8c3b58caab59ea527c4268b8c246ca95e
Knife Soloを使うと、上記Chef Solo実行時に手動で行ったファイルコピーをすべて代わりにやってくれるので、SSHでログインすることなく"chef-solo"が実行される

Chef Serverはセットアップ情報をChef Server自身が管理しますが、Chef Soloの場合はセットアップ情報はファイルとして管理するので、それをgit管理しておくこともできます。
開発者目線で考えると、gitで情報を管理できるしChef Serverをインストールする必要もないChef Soloの方が扱いやすいかと思います。

3.ゼロから始める、Knife Soloを実行するまでのインストール手順

さて、本題です。

Chefを解説しているホームページを見ていると、いろんな人がいろんなやり方でChefを実行するためのインストール手順をまとめてくれています。が、情報が古いChef Serverの手順だったKnife Soloを使ってないという要因で、手順が人によっていろいろ異なっています。

また、Chefはruby上で実行されますが、rubyは既にインストール済みの前提で、手順は省かれてる場合も多いように見受けられます。

そこで、rubyも入ってない素のOS上から、実際にKnife Soloを使うまでの手順を、最新のChef Solo 11.6.0、Knife Solo 0.3.0 にて解説していきます。

「これを見れば誰でもChefを始めることができる!」

※Chef自体はrubyが入ってればどの環境でも動きますが、WindowsでCygwinにて動かそうとすると、現状ではCygwinのrubyのパフォーマンスが悪いせいか他の環境に比べ実行時間が多めにかかります。ご注意ください。


3−1.Chef Solo、Knife Soloを使うのに必要なソフトウェア

Knife Soloを実行する作業マシン

  • ruby(chefを実行するために必要)
  • rubygems(knife soloをインストールするために必要)
  • Chef Solo
  • Knife Solo

Chef Soloが実行される、セットアップされるサーバー

  • ruby(chefを実行するために必要)
  • Chef Solo

意外と盲点なのは、セットアップ対象先のサーバーにはKnife Soloは必要ないことです。なるべく不要なものは入れないようにしましょう。

3−2.Knife Soloを実行する作業マシンのインストール手順
  1. curl -L https://www.opscode.com/chef/install.sh | bash
  2. /opt/chef/embedded/bin/gem install knife-solo --no-ri --no-rdoc

これだけです。

1. で行ってるのは、chefの公式サイトからrpmやdebといったChef Soloのパッケージをダウンロードしてインストールしています。そしてここ重要な点ですが、このパッケージの中にはrubyも含まれています

元からrubyがインストールされてたり、yumでインストールするように書かれてるサイトもありますが、現在のChef Soloはruby1.9.2以降でないと一部正しく動作しません
(特にRedhat系の場合、yumでインストールされるrubyは現状1.8系)

また、rubyを使ったプログラムを同じサーバーで動かす場合、バージョン依存によるしがらみが発生してしまうかもしれません。

そのため現状のChef Soloのインストールは、rubyを含むパッケージでインストールするのがお薦めです。Chef用のrubyと、通常のパッケージで入れたruby共存することも可能です。


そして2. でKnife Soloを、rubyのgemにてインストールしています。(現状Knife Soloのパッケージはないため)ここで気をつけなければいけないのは、1.でインストールされたruby配下(/opt/chef/embedded/)のgemにてインストールを実行することです。

3−3.Chef Soloにてセットアップが実行されるサーバーでのインストール手順
  1. ※Knife Soloをインストールした作業マシン上で
    knife solo prepare root@[セットアップ先サーバーのIPアドレス]

これだけです。
これならインストールは簡単ですね。ただこれはセットアップ先のサーバーにてインターネットにつないでChef Soloのパッケージをダウンロード・インストールしています。そのため対象のサーバーがインターネットに繋がってないとエラーになってしまう欠点があります。(本番のDBサーバーだと外と疎通できないこともよくあります)

その場合は下記のページよりパッケージをダウンロードし、対象のサーバーに手動で転送してインストールすることでChef Soloがインストールできます。
http://www.opscode.com/chef/install/
※このパッケージもrubyが同梱されています

4.Chefのディレクトリ配置
4−1.Chef Solo 基本ディレクトリの作成

さて「いざ始めてみようか」となると、まずはChefを実行するためのベースとなるディレクトリを、Knife Soloコマンドを使ってひな形から作成することになります。
作業マシン上で、Cookbook等を作るディレクトリを決めて、そのディレクトリの中で
knife solo init [リポジトリ名]
と打つと、基本となるディレクトリが自動的に作られます。

[リポジトリ名]/
    cookbooks/      ※セットアップ手順などを書いたレシピ(recipe)はこの中に入ります
    data_bags/      ※主にパスワード情報などを保存しておく場所
    nodes/          ※Chefを実行するサーバーの情報はここに書いておきます
    roles/          ※役割(Webサーバー、DBサーバーなど)ごとの定義を書きます
    site-cookbooks/ ※特定のサイトに依存するcookbookはこちらの方に入れます
(注)上記コマンドはやってるのはあくまでひな形を元にディレクトリを作ってるだけなので、例えばgitにひな形を登録して 初回時pullするって運用でいくのであれば、上記コマンドを実行する必要ありません。

基本は上記のディレクトリ構成のままで構わないのですが、(最初始めたばかりだとそこまで気にしませんが)、Chef Soloはセットアップ手順やサーバーの情報含め、全てをテキストファイルで管理するので、どこにどういったファイルを置くかのディレクトリ配置が重要です。
ここをおざなりにすると後で管理が複雑でめんどくさくなってきます。

なぜ複雑になってしまうか。冒頭でも少し書きましたが、
  • 開発環境と本番環境の設定をどう区分けしたらいいのか
  • WebサーバーとDBサーバーで用途は違うけど共通の設定もある。どうしよう
  • AプロジェクトとBプロジェクトどっちも並行でやることになった。
    A用Cookbook、B用Cookbookというように分けるべきか。
    でもそうするとほぼ同じCookbookが複数できてしまう
といった問題が、後々になって気づいて出てくるんです。はい。

4−2.おすすめのディレクトリ構成

そこで上記の問題を解決する、私の考えた、現状おすすめな構成がこれです!

cookbooks/
    プロジェクト共通のcookbook各種
Aプロジェクト/
    .chef/knife.rb
    data_bags/
    nodes/
        192.168.1.1.json
        192.168.1.2.json
    roles/
        common.rb
        web.rb
        db.rb
        _dev.rb
        _production.rb
    site-cookbooks/
        プロジェクト固有のcookbook各種
Bプロジェクト/
    ....

4−2−1.Cookbooks

cookbooksディレクトリに入れるCookbookは、特定のプロジェクトに依存しない汎用的に書かれたものだけを置きます。
逆に特定のプロジェクトに依存するものはsite-cookbooksに入れます。そうすることで複数のプロジェクトでCookbookを共有してくことも可能になります。

そして例えば次のようなこともできます。

cookbooks/
    apache/
        recipe/
            default.rb
        template/
            default/
                httpd.conf.erb
Aプロジェクト
    site-cookbooks/
        apache/
            template/
                default/
                    httpd.conf.erb  ※こちらが優先される
この場合、cookbookのレシピは共通のものを使用するけど、httpd.confのテンプレートはAプロジェクトの方を参照します。

このような構成を可能にするためには、「.chef/knife.rb」の中で
cookbook_path ["cookbooks", "site-cookbooks"]
 を
cookbook_path ["../cookbooks", "site-cookbooks"]
のように書き換えることで対応できます。

4−2−2.Role

サーバー構築する際は大概、WebサーバーとDBサーバーは構築するかと思います。
Webサーバー・DBサーバー共通のセットアップをする場合があれば、それぞれ個別のセットアップをする場合もあります。それらを定義できるのがRoleです。

私の場合は、"common.rb"、"web.rb"、"db.rb"というように定義を分け、Webサーバーでは"common.rb"と"web.rb"を、DBサーバーは"common.rb"と"db.rb"を参照するようにしています。

4−2−3.Environment

開発環境と本番環境で設定を分けたい場合どうするか。
Chef ServerではEnvironmentという概念があって、開発用・本番用の設定をそこに定義することが出来ます。
しかし残念ながら Chef Solo 11.6.0、Knife Solo 0.3.0では 一部Environmentの機能に対応したものの、実践で使うにはまだ機能が十分ではありません。

そのため私はRoleを使って便宜上Environmentと同様の定義をしています。

ちょっとだけChefをHackしてるので複雑そうに見えますが、結果はシンプルです。先のRoleの話も含め、実際に書いてることをリストアップします。
● nodes/192.168.1.1.json (dev用サーバーとする場合)
"run_list":[
    "role[web]"
],
"chef_environment": "_dev"  ※この書き方はChef 11.8でサポートされそうで、それを先取りしています
● roles/web.rb
#nodes/*.jsonで定義されてる”chef_environment”の値を取得する
@environment = "_default"
node_index = ARGV.index('-j') 
if node_index
    require 'json'
    node_json = JSON.parse(File.read(ARGV[node_index+1]))
    if node_json["chef_environment"]
        @environment = node_json["chef_environment"]
    end
end
#共通の設定、dev用設定を記述
env_run_lists = {}
env_run_lists["_default"] = [
    "role[common]",   ※web,db共通のセットアップ・設定
    "recipe[apache]", ※web.rbなのでapacheをインストール
]
env_run_lists["_dev"] = env_run_lists['_default'] + [
    "role[_dev]"      ※dev環境用の設定roleを読み込み
]
default_attributes(
    "apache" => {
        "port" => "443"
    }
)
run_list(env_run_lists[@environment])
● roles/common.rb
run_list([
    "recipe[共通で実行するレシピ]"
])
default_attributes(
    "apache" => {
        "server_name" => "www.example.com"
        "port" => "80"
    }
)
● roles/_dev.rb
default_attributes(
    "apache" => {
        "server_name" => "www.dev.example.com"
    }
)

ミソはweb.rbで nodeのjsonファイルを直接開いて、chef_environmentの値を取得してることですね。
上記のようにroleを分けることで、
  • 共通で実行するレシピと Web,DBといった用途ごと
  • 開発と本番
  • 実行するレシピ
  • 設定値
を分離することが出来ます。


Chef Serverの場合はAプロジェクト・BプロジェクトごとにChef Serverを立てることが多いでしょうが、Chef Soloの場合はこのようにうまくディレクトリ構成を工夫することで、複数プロジェクト分をまとめて管理でき、テキストファイル=gitに登録できるメリットもあります。
こうしてみると開発者はもちろん、インフラ構築者でも意外とChef Solo使えるのではないでしょうか。(もちろんChef Serverにもメリットはたくさんあるので用途に応じて使い分けるのがよさそうですね)

そして今回でも書ききれないことが出てしまったので、また次に回したいと思います。
  • 本番環境のようにインターネットにつながらない環境下前提のレシピの書き方
  • attributeの書き方及びはまりどころ
是非楽しみにしていてください。

そしてドワンゴでは、現在エンジニアを積極的に採用中です。 特に、このような環境構築自動化のノウハウを持ったインフラエンジニアは大歓迎です。 ご興味がある方は是非コチラからご応募ください!ニコニコ入社一時金制度もやっています。

(補足)
本文中で「サーバー」に「セットアップ」という用語を多用してますが、Cookbookのレシピを実行することは正確には「セットアップ」ではなく、ノード(サーバー)をあるべき姿に「収束(converge)させる」こと(何度実行しても同じ結果(サーバー状態)になること)になります。