はじめに
次の現場でChefを使う可能性が出てきたので、ちょっと真面目にレシピを書き残しておく。
前回は以下の事を記載しました。
- 初心者によるchef-solo,vagrant,berkshelf等で作るrails+mysqlの環境
- 初心者によるchef-solo,vagrant,berkshelf等で作るrails+mysqlの環境 その2
これらの記事はまとまった記事では無いので、今回はしっかりまとめた記事を書きます。
最終的には企業レベルで使える(と思われる)レシピの書き方を記事にしていきます。
※本記事はMacを使って確認をしてます。
※本記事はChef-Solo
について書いてます。
事前準備
事前に以下のものを入れておいてください。
- VirtualBox
- Vagrant
最終的にはAWSのAmazon-Linux
へ環境構築する想定で進めますので、上記を入れずに直接Amazon-Linux
を使用しても問題ありません。
Docker
などの仮想OSを使っても問題ありません。
→DockerでChefを流す実験をしてみました。
- Homebrew
- Ruby
- Bundle(rubyのGem)
Homebrew
は無理に入れる必要はありませんが、Macで行う場合は入れておいた方が良いです。
Ruby
ははじめに入っているものでも問題ありません。
$ gem install bundle
こちらを行なっておいてください。
仮想OSの準備
Chefは冪等性(平たく言うと、何度実施しても同じ結果になる事)が保証されていますが、いきなり本番でテストするのは危険が伴います。
(なぜなら、自分が作ったレシピに冪等性が保証されていると言う確証が無いためです。)
ですので、仮想OSもしくはAWSの無料枠などを準備します。
以下にVagrantを使った仮想OS構築の手順を記載しておきます。
mkdir opscode-centos65
cd opscode-centos65
vagrant init opscode-centos-6.5 http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box
起動前に設定内容を書き換えます。
(SSHでアクセスできれば良いので、適当に設定します。)
vi Vagrantfile
内容を以下のように書き換えます。
Vagrant.configure(2) do |config|
# コメントアウトを外します。(必要に応じてIPアドレスを変更してください。)
config.vm.network "private_network", ip: "192.168.33.10"
end
仮想OSを起動してSSHでアクセスできるようにします。
※以下の例はchef-centos65
と言う名前でアクセス出来るようにしてます。
$ vagrant up
$ vagrant ssh-config --host chef-centos65 >> ~/.ssh/config
念のためSSHでアクセスできる事を確認します。
アクセス後にsudo
が使用できることを確認します。
$ ssh chef-centos65
$ sudo ls
$ exit
仮想OSは環境構築のテストでしか使用しないので、最後のSSH接続+SUDOができていればどのような環境でもOKです。
Chefリポジトリの準備
ここからが本番です。
仮想OSのディレクトリとは別にChef
用のディレクトリを用意します。
$ mkdir chef-rep
$ cd chef-rep
必要なプログラムをインストールします。
$ vi Gemfile
以下の内容を記載します。
source "https://rubygems.org"
gem 'chef', '~> 12.0'
gem 'knife-solo'
# 公開されているレシピを使用するために必要
gem 'berkshelf'
# 暗号化が必要な場合は以下のGemも入れる。(今回は使わない。)
gem 'knife-solo_data_bag'
プログラムをインストールします。
他のプログラムに影響しないように、vendor
へ入れます。
$ bundle install --path=vendor/bundle
Chef
のリポジトリを作成する。
$ bundle exec knife solo init .
これでChef
のリポジトリが出来上がりました。
以下の処理はこのディレクトリを使って行なっていきます。
Berkshelfの設定
Chef
はクックブックの中にレシピを作成していきます。
構図のイメージとしては以下のような感じです。
・カレーライスのクックブック
・標準レシピ
・和風カレーライスレシピ
・インドカレーレシピ
・タイカレーレシピ
・鍋のクックブック
・ポン酢で作るおいしい鍋のレシピ
・闇鍋の作り方レシピ
これらのクックブックを作っていくのですが、自分でこれらのクックブックを全て作るのは大変です。
クックパッド
のようにレシピがまとまってれば、それを使いたいものです。
その要望に応えたものがBerkshelf
になります。
今回はこれを使います。
では、早速設定しましょう。
$ vi Berksfile
すでにファイルが作成されていると思います。
以下のように変更します。
source "https://supermarket.getchef.com"
cookbook 'yum-epel'
# rbenvのクックブック内にレシピがあるので、不要かも・・・。
cookbook 'ruby_build'
cookbook 'rbenv'
# 例えばimagemagickのクックブックを使いたい場合は以下のように設定する。
cookbook 'imagemagick'
定義したクックブックをインストールします。
$ bundle exec berks install
~/.berkshelf/cookbooks/
にクックブックが作成されてます。
環境構築 その1 冪等性について
ここまで設定した内容を実際に仮想OSへ反映させます。
サーバーを指定する場合はnodes
の中に記載します。
# chef-centos65は「ssh chef-centos65」でアクセスできる名前をつけます。
$ vi nodes/chef-centos65.json
実施するレシピを記載します。
クックブックのみを指定した場合は、クックブックの中のdefault.rb
が使われます。
とりあえずBerkshelf
で落としてきたrbenv
をインストールする設定を書きます。
{
"run_list": [
"rbenv"
]
}
実行する前に、対象サーバーがChef-Solo
を実行できるように対象サーバーへChef-Solo
をインストールします。
$ bundle exec knife solo prepare chef-centos65
実際にrbenv
を入れます。
$ bundle exec knife solo cook chef-centos65
コンソールを見ていると、自分が設定していないyum
などがインストールされていく様子がみられます。
chef
の素晴らしいところはこのような依存関係を解決してくれるところです。
実際にインストールされているか確認してみます。
$ ssh chef-centos65
$ rbenv -v
rbenv 0.4.0-210-gf0e8bdc
次にインストールできるRubyのバージョンを確認します。
$ rbenv install --list
rbenv: no such command `install'
しかし、installコマンドは使えません。
ruby_build
を入れ忘れたためです。
先ほどのnodes
ファイルを書き直します。
# SSHから抜けます。
$ exit
$ vi nodes/chef-centos65.json
これで先ほどのファイルを以下のように書き直します。
{
"run_list": [
"rbenv",
"ruby_build"
]
}
再度環境設定を行います。
$ bundle exec knife solo cook chef-centos65
さて、今度はどうでしょうか?
$ ssh chef-centos65
$ rbenv install --list
Available versions:
1.8.6-p383
...
topaz-dev
今度は入っている事が確認できました。
このように、設定を変更してcook
を行っても環境が壊れる事なく同じ結果が反映されます。
試しにvagrant destroy
とvagrant up
をしてcook
を再度行っても同じ結果になります。
これが冪等性
です。
この性質があるため、複数台のサーバーに対してcook
を行うと同じ設定のサーバーを量産する事ができます。
環境構築 その2 レシピを作る
では肝心のruby
コマンドは使えるでしょうか?
$ ruby -v
-bash: ruby: コマンドが見つかりません
残念ながらまだrubyは使えません。
$ rbenv install [rubyバージョン]
上記のコマンドを行えばruby
は入りますが、サーバー内で何かしらの作業を行ってしまうとchef
の良さがなくなります。
もし5台のサーバーに対して環境構築を行う場合には、5台のサーバーに入って上記のコマンドを打たなければなりません。
これはchef
の利便性や冪等性の観点から見ても、NGであることは明らかです。
これを解決するためのレシピを作りましょう。
# SSHから抜けます。
$ exit
現状を確認します。
$ ls -l cookbooks
$ ls -l site-cookbooks
確認するとcookbooks
に様々なファイルが存在してることがわかります。
これはknife
の中身を見るとわかります。
$ cat .chef/knife.rb
cookbook_path ["cookbooks", "site-cookbooks"]
node_path "nodes"
role_path "roles"
environment_path "environments"
data_bag_path "data_bags"
#encrypted_data_bag_secret "data_bag_key"
knife[:berkshelf_path] = "cookbooks"
Chef::Config[:ssl_verify_mode] = :verify_peer if defined? ::Chef
この中のknife[:berkshelf_path] = "cookbooks"
でBerkshelf
が使用するクックブックを指定しているためです。
また、cookbook_path ["cookbooks", "site-cookbooks"]
にクックブックのパスが定義されていることからsite-cookbooks
に自分のレシピを作れば良いことがわかります。
早速作っていきましょう!
$ bundle exec knife cookbook create ruby -o site-cookbooks/
ruby
のバージョンを指定するためのクックブックとしてruby
を作成します。
作成したら中身を記載します。
$ vi site-cookbooks/ruby/recipes/default.rb
内容を記載します。
# インストールするバージョンを指定します。
rbenv_ruby "2.2.3" do
ruby_version "2.2.3"
# グローバルとして使用するかを定義します。
global true
end
node
に追加します。
$ vi nodes/chef-centos65.json
最後にruby
を追加します。
{
"run_list": [
"rbenv",
"ruby_build",
"ruby"
]
}
サーバーに反映させます。
$ bundle exec knife solo cook chef-centos65
ruby
をインストールするため実施には時間がかかります。
$ ssh chef-centos65
$ ruby -v
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
$ exit
ruby
がインストールされました。
環境構築 その3 依存関係
このレシピにはいくつか問題があります。
例えばnodes/chef-centos65.json
の順番を間違えてしまったり、書き忘れた場合はどうなるでしょうか?
{
"run_list": [
"ruby",
"rbenv",
"ruby_build"
]
}
または以下のケース
{
"run_list": [
"ruby"
]
}
このように記載されても実行できるように、依存関係を記載しておきます。
# 使用するレシピを事前に記載しておきます。
include_recipe "rbenv"
include_recipe "ruby_build"
# レシピも指定する場合は以下の記述になる。
#include_recipe "rbenv::default"
#include_recipe "ruby_build::default"
# インストールするバージョンを指定します。
rbenv_ruby "2.2.3" do
ruby_version "2.2.3"
# グローバルとして使用するかを定義します。
global true
end
さらに使用するレシピのデフォルト値を読み込むために以下を記載します。
depends "rbenv"
depends "ruby_build"
これでnode
が以下のような状態になっていても実行されるようになります。
{
"run_list": [
"ruby"
]
}
シンプルになりましたね!
実際に仮想OSを作り直して、上記の状態でcook
をしても同じ状態になることを確認してください。
参考
include_recipeだけでは読み込み先のattributesが読み込まれない
注釈
実際に使用する際は、ここまで行う必要はほとんどありません。
しかし、こういう方法もあるということを覚えておくとレシピの書き方に幅が出てきます。
環境構築 その4 汎用化
さて、このレシピにはまだ問題があります。
それはRubyのバージョンが2.2.3
というように固定されてしまっていることです。
今のままでは良いかもしれませんが、将来的に新しいRubyのバージョンを入れる事を考えるとこのままではよろしくありません。
各レシピに設定値を受け渡せれれば問題が解決します。
chef
では以下の記述を行う事で設定値を記載できます。
{
"ruby": {
"version": "2.2.3"
},
"run_list": [
"ruby"
]
}
上記で設定した値を使用して構築できるようにレシピを書き直します。
include_recipe "rbenv"
include_recipe "ruby_build"
rbenv_ruby node["ruby"]["version"] do
ruby_version node["ruby"]["version"]
global true
end
ではサーバーに反映してみましょう!
(反映を確認する場合は、Rubyのバージョンを2.2.2
などにすると確認ができます。)
$ bundle exec knife solo cook chef-centos65
このままでも良いのですが、せっかくrbenv
を使っているのに固定されたバージョンしか設定できないのは不便です。
以下のようなパラメーターを受け付けれるように変更しましょう。
{
"ruby": {
"versions": [
{
"version": "2.2.3",
"global": "true"
},
{
"version": "2.2.2"
}
]
},
"run_list": [
"ruby"
]
}
レシピも変更します。
ついでに必ず使うgemであるbundle
も入れておきます。
include_recipe "rbenv"
include_recipe "ruby_build"
node['ruby']['versions'].each { |v|
# 指定バージョンのrubyをインストール
rbenv_ruby v['version'] do
ruby_version v['version']
global true if v["global"]
end
# bunldleを設定する。
rbenv_gem "bundler" do
ruby_version v['version']
end
}
設定を反映させてみましょう!
$ bundle exec knife solo cook chef-centos65
レシピの初期値
このレシピを使うときには必ずruby
のバージョンを指定する必要があります。
しかし、実際にレシピを使う時にバージョンはあまり重要ではない局面もあります。
そういう時にまでバージョンを指定を強制すると使い勝手の悪いレシピが出来上がってしまいます。
{
"run_list": [
"ruby"
]
}
この状態でも実行できるように以下のファイルを作成します。
ここで指定した値がデフォルトで使用されます。
default["ruby"]["versions"] = [{"version" => "2.2.3", "global" => true}]
参考
ところでrbenv_ruby
コマンドとかってどこに定義されているのだろうか・・・。
レシピに存在しないのに使える・・・というところが謎である。。。
まとめ
いかがだったでしょうか?
基本的な使い方に関してはこの記事の内容だけで十分使えると思います。
しかし、ここまでの内容ではまだ企業で使うためには不十分な内容となってます。
本当は環境構築のその5以降も書いていこうと思ったのですが、思ったよりも長くなってしまったので今回の記事を基本編として次回の記事を応用編で書いていこうと思います。
応用編を実践すればより使いやすいChefのレシピが書けるようになると思いますので、ご期待ください!
次は・・・
これからはじめるChefのチュートリアル 応用編を書きましたのでこちらも続けてやってみてください!
筆者について
筆者はTownsoftを屋号に活躍(?)しているフリーランスです。
お仕事の依頼なども受けてますので、お気軽にご連絡ください。