Itamaeとは?
- クックパッド社@ryot_a_raiさん作のプロビジョニングツール
- RubyDSLで表現できるChefをライトにしたもの
- 先日クックパッド社で開催されたInfrastructure as Code 現状確認会内でv1.0.0がリリースされたそう(Serverspecもv2.0.0がリリースされたそう)
- クックパッド社では既に一部のプロダクションで利用しているらしい
- 詳しくはryot_a_raiさんが発表されたスライドにて↓
From Itamae - Infra as Code 現状確認会
前準備
- VagrantなどでCentOSの環境を用意しておく
- ローカルでbundlerを使えるようにしておく
ゴール
- Ruby2.1.1(確認用)と2.1.2がインストールされている
- グローバルは2.1.2とする
- bundlerがインストールされている
- 確認はServerspecで
プロビジョニング
Itamaeを使う準備をする
Itamae用のディレクトリ作成(特に決められた構成はない)
$ mkdir -p itamae/recipes/remote_files itamae/nodes
$ cd itamae
必要なGemをインストール
itamae/Gemfile
source 'https://rubygems.org'
gem 'itamae'
gem 'rake'
gem 'serverspec'
$ bundle install --path=vendor/bundle
レシピを書く
itamae/recipes/ruby_build.rb
package "epel-release"
package "gcc"
package "openssl-devel"
package "libyaml-devel"
package "readline-devel"
package "zlib-devel"
package "git"
RBENV_DIR = "/usr/local/rbenv"
RBENV_SCRIPT = "/etc/profile.d/rbenv.sh"
git RBENV_DIR do
repository "git://github.com/sstephenson/rbenv.git"
end
remote_file RBENV_SCRIPT do
source "remote_files/rbenv.sh"
end
execute "set owner and mode for #{RBENV_SCRIPT} " do
command "chown root: #{RBENV_SCRIPT}; chmod 644 #{RBENV_SCRIPT}"
user "root"
end
execute "mkdir #{RBENV_DIR}/plugins" do
not_if "test -d #{RBENV_DIR}/plugins"
end
git "#{RBENV_DIR}/plugins/ruby-build" do
repository "git://github.com/sstephenson/ruby-build.git"
end
node["rbenv"]["versions"].each do |version|
execute "install ruby #{version}" do
command "source #{RBENV_SCRIPT}; rbenv install #{version}"
not_if "source #{RBENV_SCRIPT}; rbenv versions | grep #{version}"
end
end
execute "set global ruby #{node["rbenv"]["global"]}" do
command "source #{RBENV_SCRIPT}; rbenv global #{node["rbenv"]["global"]}; rbenv rehash"
not_if "source #{RBENV_SCRIPT}; rbenv global | grep #{node["rbenv"]["global"]}"
end
node["rbenv"]["gems"].each do |gem|
execute "gem install #{gem}" do
command "source #{RBENV_SCRIPT}; gem install #{gem}; rbenv rehash"
not_if "source #{RBENV_SCRIPT}; gem list | grep #{gem}"
end
end
itamae/recipes/remote_files/rbenv.sh
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init --no-rehash -)"
ノードを定義する
itamae/nodes/node.json
{
"rbenv": {
"versions": ["2.1.1", "2.1.2"],
"global": "2.1.2",
"gems": ["bundler"]
}
}
実行する
$ bundle exec itamae ssh -h centos -u vagrant -j nodes/node.json recipes/ruby_build.rb
期待通りプロビジョニングされているかServerspecで確認する
specを書く
itamae/spec/centos/ruby_buld_spec.rb
require 'spec_helper'
describe command('source /etc/profile.d/rbenv.sh; which rbenv') do
let(:disable_sudo) { true }
its(:stdout) { should match %r{/usr/local/rbenv/bin/rbenv} }
end
describe file('/etc/profile.d/rbenv.sh') do
it { should be_file }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
it { should be_mode 644 }
its(:content) { should match(/^export RBENV_ROOT="\/usr\/local\/rbenv"$/) }
its(:content) { should match(/^export PATH="\${RBENV_ROOT}\/bin:\${PATH}"$/) }
its(:content) { should match(/^eval "\$\(rbenv init --no-rehash -\)"$/) }
end
describe file('/usr/local/rbenv/plugins') do
it { should be_directory }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
it { should be_mode 755 }
end
describe file('/usr/local/rbenv/plugins/ruby-build') do
it { should be_directory }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
it { should be_mode 755 }
end
%w(2.1.1 2.1.2).each do |versoin|
describe command("source /etc/profile.d/rbenv.sh; rbenv versions | grep #{versoin}") do
let(:disable_sudo) { true }
its(:stdout) { should match(/#{Regexp.escape(versoin)}/) }
end
end
describe command('source /etc/profile.d/rbenv.sh; rbenv global') do
let(:disable_sudo) { true }
its(:stdout) { should match(/2.1.2/) }
end
specを実行する
$ bundle exec rake spec spec/centos/ruby_buld_spec.rb
実行結果
Command "source /etc/profile.d/rbenv.sh; which rbenv"
stdout
should match /\/usr\/local\/rbenv\/bin\/rbenv/
File "/etc/profile.d/rbenv.sh"
should be file
should be owned by "root"
should be grouped into "root"
should be mode 644
content
should match /^export RBENV_ROOT="\/usr\/local\/rbenv"$/
content
should match /^export PATH="\${RBENV_ROOT}\/bin:\${PATH}"$/
content
should match /^eval "\$\(rbenv init --no-rehash -\)"$/
File "/usr/local/rbenv/plugins"
should be directory
should be owned by "root"
should be grouped into "root"
should be mode 755
File "/usr/local/rbenv/plugins/ruby-build"
should be directory
should be owned by "root"
should be grouped into "root"
should be mode 755
Command "source /etc/profile.d/rbenv.sh; rbenv versions | grep 2.1.1"
stdout
should match /2\.1\.1/
Command "source /etc/profile.d/rbenv.sh; rbenv versions | grep 2.1.2"
stdout
should match /2\.1\.2/
Command "source /etc/profile.d/rbenv.sh; rbenv global"
stdout
should match /2.1.2/
Finished in 1.81 seconds (files took 0.50842 seconds to load)
19 examples, 0 failures
期待通りプロビジョニングされていることが確認できました! Itamae、 Serverspecステキ!!
所感
- スライドにも記載があったが、実行にかかる時間はChefよりも速い
- 構成がシンプルで登場人物も少ないので学習コストは非常に低いと感じた
- リソースやアトリビュートはChefと近いものが多いので、Chefの知識は活かせる
- chef-soloをchef-zeroに移行...とかいう話も無縁
- ChefのDataBagに相当する機能が使えるようになるとさらに嬉しい
- 業務で導入したいのでまずは色々と使ってみる
こんな方は是非試してみるべき(個人的な見解)
- PuppetやChef、Ansible等プロビジョニングのツールに触れたことがない方
- Chefを導入したものの、高機能過ぎてちょっと手に余ると思っている方
- 上記理由でChefからAnsibleに乗り換えたけど、やっぱりRubyDSLで書きたい方
- Chefの有識者がおらず気づいたら自分一人で保守してる(Chefおじさん的な存在になっている)方
謝辞
参考サイト
追記
Itamaeで提供されているリソースをまとめたものも記事にしましたので、こちらもご参考になれば幸いです。
Itamaeチートシート