開発環境の構築には以下のソフトウェアが必要です。
それぞれダウンロード、インストールしてください。
https://www.virtualbox.org/wiki/Downloads
http://www.vagrantup.com/downloads.html
※ Vagrant の box やプラグインは環境変数 VAGRANT_HOME のパスに配置されます。
ゲストマシンに Chef をインストールしてくれるプラグインです。
$ vagrant plugin install vagrant-omnibus |
VirtualBox の Guest Additions をホストの VIrtualBox に合わせて更新してくれます。
$ vagrant plugin install vagrant-vbguest |
USB メモリの chefs_meeting01 ディレクトリをコピーしてください。
※ コピー先のパスに特殊文字などが含まれてはいけません。
ベース box を Vagrant の管理下に置きます。
$ vagrant box add <box の名前> <box のファイルパス> |
box の名前: sl65-x64-chefs_meeting01
box のファイルパス: /path/to/sl65-x64-chefs_meeting01.box
※ 登録された box ファイルは $VAGRANT_HOME/boxes に配置されます。
Vagrantfile の存在するディレクトリでコマンドを実行してください。
$ cd chefs_meeting01/ $ vagrant up |
ユーザー: vagrant
パスワード: vagrant
※ 起動した VM は VirtualBox のデフォルトの仮想マシンフォルダに配置されます。
chef コマンドで Cookbook のひな形を生成します。
$ cd workspace $ chef generate cookbook <Cookbook 名> |
コマンドを実行すると、以下のファイルが作成されます。
Kitchen の設定ファイル
テスト用のインスタンスや実行するプロビジョニングの設定を記述
Berksfile の設定ファイル
Kitchen でプロビジョニングを行うときの依存する Cookbook を記述
Cookbook の説明書
ChefClient に転送しないファイルを定義
Cookbook の基本情報を記述
Cookbook がデフォルトで実行する Redipe
スクリプトを実行し .project を生成します。
$ cd <Cookbook 名> $ gen-eclipse |
Eclipse で .project をインポートします。
File -> Import… -> General -> Existing Projects into Workspace
/home/vagrant/workspace/<Cookbook 名> |
Package Explorer の ▽ を選択 -> Filters…
「.*resources」のチェックを外す
Chef では、インスタンスのあるべき状態を Recipe(ソースコード)として記述します。
Recipe は「ファイルのダウンロード」や「コマンドの実行」など、さまざまな機能をもつ Resource で構成されています。
たとえば、 以下の Recipe は 「bash により “/tmp” ディレクトリで “tar zxf archive.tar.gz” というスクリプトを実行する “extract tar file” という名前の Resource」 を定義したものです。
bash “extract tar file” do cwd “/tmp” code “tar zxf archive.tar.gz” action :run end |
ここでは Chef 標準の “bash” という Resource の :run という Action を呼び出すように定義していて、“cwd” や ”code” という Attribute によりそのときのパラメータを指定しています。
指定できる Action や Attribute は Resource ごとに異なるので、Chef のドキュメントを確認してください。bash Resource であれば [ http://docs.opscode.com/resource_bash.html ] です。
Recipe は基本的に上から順に、記述した順で Resource が実行されます。
たとえば、「ファイルをダウンロード」してから「ダウンロードしたファイルを解凍」したい場合は、次のように記述できます。
remote_file “/tmp/archive.tar.gz” do source “http://path/to/archive.tar.gz” action :create end bash “extract tar file” do cwd “/tmp” code “tar zxf archive.tar.gz” action :run end |
Recipe を書くにはまず、「具体的に何をしたいのか」を明らかにし、それに対し「どの Resource を使用するか」を決めなければなりません。そうしてからようやく具体的なソースコードを記述できます。
たとえば、あるアプリケーションのコマンドにパスを通すために、/etc/profile.d/application.sh に 「export PATH=$PATH:/opt/application/bin」 という内容のファイルを配置したいとします。
これはどの Resource を使えば実現できるでしょうか。
Chef のドキュメントを調べると、file Resource というのが見つかります。これは、任意の内容のテキストファイルを任意のパスに配置する Resource のようです。使えそうですね。
では、file Resource の詳細を確認しましょう。
Chef のドキュメント [ http://docs.opscode.com/resource_file.html ] を参照すると、次のようなシンタックスになっていることが確認できます。
file <配置先のファイルパス> do content <配置するファイルの内容> end |
充分に理解できたら、いよいよ具体的なソースコードを実装します。
file “/etc/profile.d/application.sh” do content “export PATH=$PATH:/opt/application/bin” end |
書けました。
このように Recipe を書くには、
具体的に何をしたいのかを明らかにする
使えそうな Resource を探し、仕様を確認する
Recipe を記述する
という手順を繰り返していきます。
まずは、手動で Jenkins をインストールする場合の手順を確認します。
http://pkg.jenkins-ci.org/redhat/ から rpm をダウンロード
ダウンロードした rpm を yum コマンドでインストール
/etc/sysconfig/jenkins を編集して、実行ユーザーを変更
利用するディレクトリのオーナーを変更
サービスを登録・起動
これを Recipe にしてみましょう。
デフォルトの Recipe なので、chef generate cookbook で生成された recipes/default.rb を編集します。
execute Resource [ http://docs.opscode.com/resource_execute.html ]
remote_file Resource [ http://docs.opscode.com/resource_remote_file.html ]
※ ダウンロードしたファイルなどは /tmp ではなく Chef::Config[:file_cache_path] に保存する
package Resource [ http://docs.opscode.com/resource_package.html ]
ruby_block Resource [ http://docs.opscode.com/resource_ruby_block.html ]
Chef::Util::FIleEdit [ http://rubydoc.info/gems/chef/Chef/Util/FileEdit ]
※ execute Resource で sed コマンドを実行してもよい
※ file Resource + lazy でもできるかな?
ruby_block Resource [ http://docs.opscode.com/resource_ruby_block.html ]
FileUtils#chown_R [ http://docs.ruby-lang.org/ja/1.9.3/method/FileUtils/m/chown_R.html ]
※ execute Resource で chown コマンドを実行してもよい
service Resource [ http://docs.opscode.com/resource_service.html ]
作成した Recipe が期待したとおりに動いているか確認してみましょう。
確認には Kitchen を利用すると便利です。
Kitchen とは、任意の環境をインスタンスとして起動し、プロビジョニングを行い、その結果を検証できるツールです。
インスタンスは何度でも簡単に作り直すことができるので、気軽に Chef の Recipe を試すことができます。
まずは Kitchen を使用するために、設定ファイル .kitchen.yml を編集しましょう。
driver: name: docker provisioner: name: chef_solo platforms: - name: centos-6.4 driver_config: image: centos forward: 8080 suites: - name: default run_list: - recipe[tar::default] - recipe[java::default] - recipe[jenkins::default] attributes: java: install_flavor: oracle jdk_version: 7 java_home: /usr/java/default oracle: accept_oracle_download_terms: true |
軽量・高速な docker を使用します。
Kitchen から docker を利用するには kitchen-docker のインストールが必要です。
$ chef gem install kitchen-docker |
※ 今回の環境には既にインストールされています。
centos をベース image とします。
利用できる image 名は Dockerfile の FROM 句で指定できるものと同じです。
詳細は Dockerfile のリファレンスを参照してください。
[ https://docs.docker.com/reference/builder/#from ]
forward ではホストマシンにフォワーディングするポート番号を指定します。
今回は jenkins が使用する 8080 ポートに http://localhost:8080 でアクセスできるように指定しています。
詳細は kitchen-docker のドキュメントを参照してください。
[ https://github.com/portertech/kitchen-docker ]
プロビジョニング時に実行する Recipe を指定します。
Jenkins の動作には Java が必要なので、今回は Opscode Community の java Cookbook を使うことにします。
また、java Cookbook の動作には tar が必要なので、これも Opscode Community の tar Cookbook を使います。
自作した jenkins Cookbook も忘れずに指定しましょう。
※ プロビジョニングに外部の Cookbook が必要な場合は後述する Berksfile の編集が必要です。
プロビジョニング時に Recipe にわたす Attribute を指定します。
Attribute とは Recipe に対するパラメータのようなものです。
今回は Opscode コミュニティの java Cookbook の Attribute を指定しています。
詳細は java Cookbook の README.md を参照してください。
[ http://community.opscode.com/cookbooks/java ]
プロビジョニング時に必要な外部の Cookbook は、このファイルをもとに berkshelf によって解決されます。Opscode Community の Cookbook であれば、次のように名前を指定するだけでOKです。
source "https://api.berkshelf.com" cookbook ‘tar' cookbook 'java' metadata |
※ Opscode Community 以外の依存
Git リポジトリ
git: ‘https://path/to/cookbook.git’
ローカルファイルシステム
path: ‘/path/to/cookbook’
では、作成した Cookbook を Kitchen のインスタンスへプロビジョニングしてみましょう。
$ kitchen setup |
プロビジョニングが成功したら、Kitchen インスタンスにログインしてみてください。
[vagrant] $ kitchen login kitchen@localhost’s password: kitchen [kitchen] $ sudo service jenkins status jenkins (pid 17656) を実行中... [kitchen] $ exit |
ログインできたら、jenkins サービスが実行されているか確認してみましょう。
/etc/sysconfig/jenkins の内容も確認してみてください。
うまくいっていない場合は、Cookbook を修正し、次のコマンドでプロビジョニングをやり直します。
$ kitchen converge |
以前プロビジョニング結果が原因でプロビジョニングがうまくいかない場合は、次のコマンドで Kitchen インスタンスを破棄してから、再度プロビジョニングを実行します。
$ kitchen destroy $ kitchen setup |
※ kitchen の主なサブコマンド
# インスタンスの生成 kitchen create # Chef-Solo のインストールと初回のプロビジョニング kitchen setup # プロビジョニング kitchen converge # Serverspec の実行 kitchen verify # ssh ログイン kitchen login # インスタンスの破棄 kitchen destroy |
docker コマンドでコンテナのポートを確認します。
$ docker ps CONTAINER ID PORTS e35e89ea252a 0.0.0.0:49153->22/tcp, 0.0.0.0:49154->8080/tcp |
コンテナの 8080 ポートがホストの 49154 にフォワーディングされていることが確認できます。
Firefox などの Web ブラウザで http://localhost:49154/ にアクセスしてみてください。
Jenkins の画面が表示されていれば成功です。
Recipe にパラメータを与える仕組みです。
プロビジョニング時に Attribute を指定することで、Recipe の動作などを変化させることができるようになります。
Attribute は Key - Value 形式のデータで、指定する方法はプロビジョニングツールにより異なります。
Kitchen の場合
.kitchen.yml の attributes に、YAML形式で定義
attributes: application: version: ‘2.0.0’ |
Vagrant の場合
Vagrantfile の chef.json に、Hash で定義
config.vm.provision :chef_solo do |chef| chef.json = { application: { version: ‘2.0.0’ } } |
Knife の場合
knife node edit で Json ファイルを編集
"normal": { “application”: { “version”: {} } } |
※ 実際の運用では knife を使っていないので、間違っているかもしれません。
Recipe から Attribute の値を参照するには、node オブジェクトを利用します。
Attribute が以下のように設定されている場合、
attributes: { application: { version: ‘2.0.0’ } } |
Recipe では以下のように node オブジェクトを経由して、目的の Attribute の値を取得できます。
application_version = node[‘application’][‘version’] |
Attribute にはプロビジョニングツールで値が指定されなかった場合のデフォルト値を指定しておくことができます。
デフォルト値は専用のファイルに記述します。ファイルは chef コマンドで生成できます。
chef generate attribute ./ default |
./attributes/default.rb というファイルが生成されますので、以下のようにデフォルト値を定義します。
default[‘application’][‘version’] = ‘1.0.0’ |
Cookbook の設計次第で何を Attribute にするかは変わります。
今回は次の項目を Attribute にしてみましょう。
Jenkins のバージョン
特定のバージョンの Jenkins をインストールしたくなるかもしれない
Jenkins の rpm ファイルの URL
将来的に、rpm ファイルの URL が変更されるかもしれない
Jenkins の実行ユーザー
特定のユーザーで Jenkins を実行したくなるかもしれない
一般的には、「node[<Cookbook の名前>][<任意の Attribute 名>]」とすることが多いようです。
Recipe の中で自身の Cookbook 名を取得するには cookbook_name Method [ http://docs.opscode.com/dsl_recipe_method_cookbook_name.html ] を利用できます。
※ ./attribute/defualt.rb では利用できません。
対応が完了したら、.kitchen.yml に作成した Attribute を追記して、Kitchen インスタンスでプロビジョニングを行い確認してみましょう。
$ kitchen destroy $ kitchen setup $ kitchen login kitchen@localhost’s password: kitchen |
Chef 専用の静的テストツールで、ChefDK に含まれています。
Cookbook のルートディレクトリでコマンドを実行すると、Chef のお作法に違反したコードなどが検出されます。
$ foodcritic ./ |
警告のメッセージには Foodcritic のルール番号が含まれています。
Foodcritic の公式サイトには各ルールの詳細な情報と修正例が記載されていますので、参考にしてください。
Recipe は Ruby で記述されているので、Ruby の汎用的な静的テストツールを利用することもできます。
複雑なコードを検出する静的テストツール
$ reek ./ |
重複コードを検出する静的テストツール
$ flay ./ |
これらのツールは gem でインストールできるものです。
ChefDK の gem にインストールするには、以下のようにコマンドを実行します。
chef gem install <gem パッケージ名> |
※ 今回の環境では reek、flay は既にインストールされています。
Chef 専用の動的テストツールで、ChefDK に含まれています。
主に Recipe が期待通りにコンパイルされているかを確認します。
単純な Recipe の場合は Recipe と ChefSpec のテストコードがほとんどイコールになってしまい、ただ同じことを2度書いているだけになってしまいます。
いま運用している規模では ChefSpec を利用するメリットが見いだせないので利用していません。
サーバーの状態をテストする動的テストツールです。Chef 専用のものではありませんが、Kitchen が標準で対応しています。今回は Kitchen から利用することを前提にすすめます。
Kitchen で Serverspec を実行するには、専用のパスにテストコードを置く必要があります。
まずはテストコードを置くディレクトリを作成しましょう。
$ mkdir -p ./test/integration/default/serverspec/ |
※ “default” の部分は .kitchen.yml で指定した suites の name
テストコードは *_spec.rb というファイル名で配置します。
$ touch ./test/integration/default/serverspec/default_spec.rb |
※ 「*」の部分はなんでもいいようですが、Recipe の名前と同じにするのをよく見かけます。
たとえば、あるパスにファイルが存在することを確認するには、以下のように記述します。
require 'serverspec' include Serverspec::Helper::Exec include Serverspec::Helper::DetectOS describe file('/etc/passwd') do |
Serverspec では簡単な記述でサーバーの状態をテストできるように、様々な Resource が用意されています。
詳細は Serverspec のドキュメント [ http://serverspec.org/resource_types.html ] を参照してください。
Kitchen インスタンスで Serverspec のテストを実行するには次のコマンドを使用します。
$ kitchen verify |
そうすると、Serverspec のテストが実行され、次のようにテスト結果のログが出力されます。
Service "jenkins" should be running
Finished in 0.04041 seconds 1 example, 0 failures |
※ 「kitchen verify」以外に、「kitchen test」というコマンドもあります。
Vagrant で Cookbook をつかう
Vagrantfile は主に次の 3 つのブロックに分かれます。
ベース box やネットワーク、共有ディレクトリなどの設定を記述します。
config.vm.box = ‘sl65-x64-chefs_meeting01’ |
VirtualBox の VM の CPU コア数やメモリサイズなどの設定を記述します。
config.vm.provider :virtualbox do |vbox| vbox.customize [‘modifyvm’, :id, ‘--memory’, '2048'] vbox.customize [‘modifyvm’, :id, ‘--cpus’, '2'] end |
設定項目などは VirtualBox のドキュメント [ https://www.virtualbox.org/manual/ch08.html#vboxmanage-modifyvm ] を参照してください。
Chef で実行する Recipe や Attribute などの設定を記述します。
config.vm.provision :chef_solo do |chef| # cookbook のあるディレクトリの相対パスを指定 chef.cookbooks_path = [‘cookbooks’, ‘site-cookbooks’] # 実行する Recipe を指定 chef.run_list = [ ‘java’, ‘eclipse’ ] # Attribute を指定 chef.json = { java: {}, eclipse: {} } |
一般的に、./cookbooks ディレクトリに Community Cookbook や Git リポジトリから取得した Cookbook を配置し、./site-cookbooks ディレクトリにこの Vagrantfile で起動する VM 専用の Cookbook を配置します。
まずは、作成した jenkins Cookbook を site-cookbnooks ディレクトリに配置しましょう。
作成した jenkins Cookbook を現在の開発環境に対して実行できるように Vagrantfile を編集してみてください。
編集が完了したら、vagrant のコマンドで、現在の開発環境に対してプロビジョニングを行います。
$ vagrant provision |
うまくいけば、現際の開発環境上で Jenkins サービスが起動していて、http://localhost:8080 で Jenkins にアクセスできるようになります。