Kentaro Kuribayashi's blog

Software Engineering, Management, Books, and Daily Journal.

capistrano + chef-soloで構成管理する

問題

VMをぽこぽこ作りながらあれこれツールを入れて試してみたりしたいという時に、chefを使って構成管理はしたいけど、chef-serverを入れるのは面倒、というか、構成パッケージの記述・インストールだけできればいいという要求からするとオーバスペックなように感じるのだし、また、ホストの管理にはcapistranoを使っているので、cap実行側のみで処理が完結する方がよいという場合もあろうかと思う。

前提

  • デプロイ先ホストには、公開鍵認証でログインできるものとする(capを使うので)
  • デプロイ先ホストでは、既にgit, chef-soloが使える状態であるものとする(そこまではなんらかの方法でがんばる)

解決案

そこで、chef-soloという、chef-serverなし、スタンドアロンにレシピの実行を行うコマンドをcapで実行するようにしてみる方法を試してみた。例として、GrowthForecastを動かすホストの構成管理を取り上げる。ディレクトリ構成は以下の通り。

├── .carton
├── .git
├── .gitignore
├── .gitmodules
├── Capfile
├── Makefile.PL
├── carton.lock
├── config
│   ├── chef.json
│   ├── deploy.rb
│   └── supervisor.ini
├── local
└── modules
    ├── GrowthForecast
    └── chef-repo

chef-repoを作成、submoduleにする

今回はGrowthForecastに必要なパッケージ(Alien::RRDtoolを入れるのに必要なパッケージ)のみですが、それに限らず、共通に使いまわせるレシピをあれこれと書いていきます。とりあえず、以下のようなrole構成にしました(リストの階層が継承関係を示す)。

  • role[base]: 大元のrole
    • role[supervisor]: supervisor使う用
    • role[perlbrew] (opscodeで共有されているものに手を入れたもの): Perlのプロダクトを動かす用
      • role[growthforecast]: GrowthForecastに特化したrole。今回はこれを使う。

chef-soloの実行

chef-soloを実行するタスクは以下の通り。

namespace :chef do
  task :solo do
    sudo <<-"EOS", :pty => true
      chef-solo -c #{deploy_to}/current/modules/chef-repo/config/solo.rb -j #{deploy_to}/current/config/chef.json
    EOS
  end
end

また、上記コード内のconfig/chef.jsonは以下の通り。

{
    "run_list": [ "role[growthforecast]" ]
}

chef-soloの設定ファイルは、chef-soloリポジトリ内にconfig/solo.rbとして置かれている。

require 'pathname'

basedir = Pathname.new(__FILE__).realpath.dirname.parent

file_cache_path "/tmp/chef-repo"
cookbook_path   (basedir + "cookbooks").to_s
role_path       (basedir + "roles").to_s
  • config/chef.jsonにrun_listを、
  • modules/chef-solo/config/solo.rbにchef-soloの設定を書くようにする

というふたつのルールを導入することで、capのタスクを一般的に使えるようにしている。同様のディレクトリ構成を取ることで、他のプロダクトについても

$ cap chef:solo

と実行することで、config/chef.jsonに書かれた、個々のアプリケーションの構成に必要なroleでもって、かつ、chef-reposに蓄積された共通のレシピを利用して、構成管理を行える。

問題点

  • submoduleにしている関係上、全てのホストにchef-repos一式が存在することになって、冗長な感じがする。
  • capを叩かないと構成変更を更新することができない(自動的に更新したりしない)
  • 実行速度が遅かったりする?

冗長であるとはいえ、複雑なchef-serverを介することなくcap + gitrepositoryだけで管理できるのは、気軽でよい感じがする。

その他

chefには直接関係ないが、以下のようなことも行っている

  • GrowthForecastを直接使うのではなく、そのGitリポジトリをsubmoduleにする。
  • Perlモジュールの管理はcartonで行うことにする。モジュールの増減は、システム側というよりはアプリの都合でころころ変わることになるだろうので、chefでやるよりcartonだとか、rubyでいえばbundlerだとかでやる方がいい感じがする。
    • carton:installというタスクを用意した
  • growthforecastの起動にはsupervisorを用いている。
    • supervisor:(start|stop|restart)などといったタスクを用意した