Chef-solo + Capistranoで簡単サーバ構成管理

サーバに対して何台も同じような設定をしていると、そんな刺身にたんぽぽのせるような仕事やってられるかー!となりますよね?特に最近だとクラウドや仮想化技術が身近になってきたので、環境をイメージコピーで構築する手法も増えているのではないかと思いますが、一方で、ハードやOSレベルでも技術が進化していくので、OSより上のレイヤー(ミドルウェアやアプリケーション)とOS以下のレイヤー(ハードウェアやOS)を粗結合にしておくことが重要だと思います。

OSより上のレイヤーのシステムの構成管理を自動化ツールとしてPuppetが有名でしたが、最近だとChefがRubyでスクリプトが書けて便利です。

ChefはChef-server, Chef-client, Chef-solo という3つの構成に分かれています。しかしChef-serverとChef-clientを利用した構成は構成がやや複雑になるので、中央で各サーバに対して指示を出す部分をCapistranoで行い、各サーバにはChef-soloを導入して各サーバ内でChef-soloを実行するという極力シンプルな構成にすることにしました。Chefが実行するスクリプトをRecipeと呼びますが今回はそれらのコードもGit上で管理する前提としています。構築の手順は以下の通り。

中央サーバにCapistranoをインストール
$ gem install capistrano
構成管理の対象サーバの設定

環境構築の自動化を目指しているので、これらのサーバはOSがインストールばかりのまっさらな環境であることを想定しています

  • ネットワーク / sshの設定

中央サーバからsshの公開鍵認証でアクセスできるようにします。/etc/sysconfig/network-scriptsや~/.ssh/authorized_keysなど適宜設定します。

  • Rubyとgitのインストール
$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p125.tar.gz
$ ./configure
$ make 
$ make install
$ yum install git

ここまでは、タンポポのせ作業ですが、我慢しましょう(^^;

Capistranoの設定
  • deploy.rbの準備

deploy.rbの設定例です。

role :web, "web001-desk001"                          # Your HTTP server, Apache/etc
role :app, "web001-desk001"                          # This may be the same as your `Web` server
role :db,  "db001-desk001", :primary => true # This is where Rails migrations will run
role :db,  "db001-desk001"

# Install Application and Middleware using Chef. (This task has to be execute by root user.)
set :chef_repo_path, '/usr/local/src/chef-repo'

namespace :chef do
  task :init do
    sync_chef_repo
    install_chef_solo
  end 

  task :sync_chef_repo do
    run "git clone git@github.com:twodollarz/chef-repo.git #{chef_repo_path}"
  end
  task :install_chef_solo do
    run "gem install chef"
  end

  task :default do
    chef_solo_base
    chef_solo_web
    chef_solo_db
  end

  task :chef_solo_base do
    run "chef-solo -j #{chef_repo_path}/config/base.json -c #{chef_repo_path}/solo.rb" 
  end

  task :chef_solo_web, :roles => [ :web ] do
    run "chef-solo -j #{chef_repo_path}/config/web.json -c #{chef_repo_path}/solo.rb" 
  end

  task :chef_solo_db, :roles => [ :db ] do
    run "chef-solo -j #{chef_repo_path}/config/db.json -c #{chef_repo_path}/solo.rb" 
  end
end
  • ssh_configの活用
role :web, "web001-desk001"

のように簡単にsshの接続先を指定していますがsshの細かい設定(接続ユーザや利用する鍵など)はdeploy.rbの方に細かく指定するのではなくssh_configで設定すると管理が楽になります。

  • role

webサーバやDBサーバなど、インストール内容が異なるサーバ群をroleとして管理します。このあたりは普通のデプロイと同じです。

  • 初期化処理(chef:init)

サーバの初期設定時に1回だけ実行することを想定しています。

$ cap chef:init

初期化処理では、chefのrecipeをgit cloneしています(ここではrecipeの詳細について説明しません)。その後chef-soloをgemを使ってインストールします。このようにchefのインストール自体も自動化しています。

  • 通常処理(chef)

アプリケーションやミドルウェアのインストールを行います。

$ cap chef

実行するのはこれだけ。
deploy.rbの中で、以下のように全サーバに共通のtask(chef_solo_base)とroleごとのtask(chef_solo_web, chef_solo_db)を実行しています。

  task :default do
    chef_solo_base
    chef_solo_web
    chef_solo_db
  end

例えばchef_solo_baseを実行するときに利用されるbase.jsonは以下のような内容になっています。このように各roleごとにインストールパッケージを選ぶことが出来るようになっています。

{
  "run_list": [ "recipe[build-essential]", "recipe[emacs]", "recipe[git]", "recipe[screen]", "recipe[tmux]", "recipe[vim]"  ]
}

ここまで枠組みを準備してしまえば、あとはRoleにサーバを追加していくだけで、Chefがタンポポをのせてくれるようになります。結果、サーバの追加と設定まで今まで半日かかっていた作業が実作業は3分ほどでできるようになりました。