以下はChefDKハンズオン with すごい広島用の資料です。
準備
本ハンズオンはTest Kitchenのdriverにdocker_cliを利用します。
準備物としてダウンロードをするものが多いので事前に準備をしておくとよりスムーズに進みます。
MacOSX
- ChefDK 0.4.0
- Virtualbox
- Boot2docker 1.5.0
Homebrew Caskをインストールしていれば簡単です。
$ brew cask update
$ brew cask install chefdk virtualbox boot2docker
- https://downloads.chef.io/chef-dk/mac/#/
- https://docs.docker.com/installation/mac/
- https://www.virtualbox.org/wiki/Downloads
Windows
サポートできない恐れがあります。
仮想環境でDockerの動作するLinux環境を用意しておくと安全です。
- ChefDK 0.4.0
- Virtualbox
- Boot2docker 1.5.0
各ファイルは下記よりダウンロードしてください。
- https://downloads.chef.io/chef-dk/windows/#/
- https://docs.docker.com/installation/windows/
- https://www.virtualbox.org/wiki/Downloads
Linux
- ChefDK 0.4.0
- docker 1.5.0
多様なのでUbuntuのものを代わりに貼っておきます。
Debianで試した時の環境設定メモもあります。
その他インストールしておくと良いかもしれないもの
- vagrant
- git
Test Kitchenのデフォルトのdriverはvagrantなので、vagrant使用したい場合はvagrantをインストールしてください。最後にvagrantのプロビジョンを行う例もあります。
gitはなくても良いですが、各時点でコミットしておくと変化がよくわかって便利です。
共通
今回はubunutu-14.04を使っています。
事前にイメージをダウンロードしておくとスムーズに参加できます。
Test Kitchenでは複数のOSを同時にテストすることもできます。
好みに応じてダウンロードしてください。
$ docker pull ubuntu:14.04
$ docker pull centos:6.6
ChefDKに付いているもの
ChefDKはChef Development Kitの略で、RubyとChefとCookbookの開発に使えるサブツールで構成されています。
今回はVersion0.4.0を想定しています。
添付されているツールは以下の通りです。
- chef
- chefコマンド ChefDKのためのコマンド。ジェネレータの実行やChefDKに添付されているRubyを操作したりするのに使う。
- Test Kitchien サーバの統合テストのためのツール。Chef以外でも使える
- ChefSpec Chefのレシピを単体テストするためのツール
- Foodcritic Chefのレシピの書き方をチェックしてくれる
- Berkshelf cookbookの依存関係を解決してくれるツール。bundlerみたいなもの。
目標
今回の目標は以下の通りです。
- ChefDKに添付されているツールを一通り使う
- Webサーバを立てる
特にTest KitchenはChef以外の構成管理ツールでも使えて有用です。
ChefDK添付のRubyを使う
ChefDKにはRubyが添付されています。
このRubyを利用するようにシェルを設定します。
$ eval "$(chef shell-init `basename $SHELL`)"
Powershellの場合は以下を使用します。
$ chef shell-init powershell | Invoke-Expression
rubyコマンドがchefdkに添付されているものになっているはずです。
$ which ruby
/opt/chefdk/embedded/bin/ruby
chefコマンドについて
chefコマンドはChefDKに添付されているコマンドです。
Chef本体に添付されているものではありません。
引数なしでchefコマンドを実行するとヘルプを見ることができます。
ChefDKに添付されているRubyに対して操作ができたり、雛形を生成するgeneraterを実行できたりします。
クックブックの作成
ChefではCookbookという単位で開発していきます。Rubygemsで言うgemがcookbookにあたります。
今回はmywebというクックブックを作成します。
$ chef generate cookbook myweb
$ cd myweb
chefコマンドにはgenerateサブコマンドがあり、cookbookの雛形を生成できます。
cookbookだけでなくrecipeやtemplateなども雛形が作成できます。
cookbookを生成すると下記のようなディレクトリ構成になります。
.
├── .gitignore
├── .kitchen.yml
├── Berksfile
├── README.md
├── chefignore
├── metadata.rb
├── recipes
│ └── default.rb
├── spec
│ ├── spec_helper.rb
│ └── unit
│ └── recipes
│ └── default_spec.rb
└── test
└── integration
└── default
└── serverspec
├── default_spec.rb
└── spec_helper.rb
metadata.rbにはcookbookのメタ情報を記述します。
chefignoreにはcookbookを配布する際に含めないファイルを記述します。
.kitchen.ymlはTest Kitchenの設定ファイルです。
他にもレシピファイル、インテグレーションテストのファイルやユニットテストファイルが生成されています。
git initされている状態なのでコミットをしておきましょう。
$ git add .
$ git commit -m 'initial commit'
Dockerの動作確認
今回はDockerの中でChefを実行します。
Vagrantを使うと時間がかかってしまうので、今回はDockerを使うことにしました。
では、Dockerが使えることを確認しておきましょう。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
正常に動作していればこのような出力になります。
boot2dockerを使用していて、動作していないのであれば、boot2dockerのstatusを確認しておきましょう。
$ boot2docker status
running
boot2dockerが動作しているのに失敗する場合は環境変数の設定が漏れている恐れがあります。
$ $(boot2docker shellinit)
で、そのシェルだけは一時的に環境変数を設定できます。
Test Kitchenの設定
折角なので、テスト駆動開発風に進めていきます。
まず、統合テストをしましょう。
統合テストをするにはTest Kitchenを利用します。
Test Kitchenがchefを実行するマシンを作成し、ServerSpecなどで記述したテストを実行してくれます。
Test Kitchenの設定は.kitchen.yml
に記述します。
.kitchen.yml
を少し書き換えます。
---
driver:
name: docker_cli
require_chef_omnibus: false
run_command:
- curl -L https://www.chef.io/chef/install.sh | bash
publish:
- 8100:80
# no_cache: true
provisioner:
name: chef_zero
platforms:
- name: ubuntu-14.04
# - name: centos-6.6
suites:
- name: default
run_list:
- recipe[myweb::default]
attributes:
今回はdriverにはdocker_cliを使います。
driverをインストールする必要があります。
$ bundle init
$ echo "gem 'kitchen-docker_cli'" >> Gemfile
$ bundle install
driverにはデフォルトでvagrantが使われます。
AWSのec2やGoogle Cloud Platformのgceなども使うこともできます。
production環境になるべく近い環境を使うのがオススメです。
問題がないようであればコミットしておきます。
$ git add .
$ git commit -m 'setup .kitchen.yml'
.kitchen.yml
の中身について補足します。
require_chef_omnibus
はchefを自動でインストールするかどうかです。
false
にしてrun_command
で手動でインストールしているためfalse
にしています。
run_commandはインスタンスを作成する際の追加コマンドを記述できます。
今回このようにしているのはDockerのキャッシュが効いて、一回のサイクルが短かく済むからです。
またno_cache
をtrue
にするとdockerのキャッシュを使わないようにもできます。
最初から作りなおしたいときに利用してください。
publish
は、今回webサーバを立てるので、80番ポートを8100でアクセスできるようにしています。
Test Kitchenを使うと同時に複数のOSのテストができます。
今回はひとつだけにしています。
同時にやってみたい場合はcentosのコメントアウトを外してみてください。
provisionerにはchef-zeroが指定されています。
他のprovisonerを使用することもできます。
SaltStackやAnsibleも使用することができるそうです。
Test Kitchenを使う
Test Kitchenを使用してみましょう。
テストを実行するには下記のtestサブコマンドを使用します。
実行に時間がかかる場合もあります。
$ kitchen test default-ubuntu-1404
第2引数のdefault-ubuntu-1404
はインスタンス名です。
突然登場しましたが、インスタンス名を確認するにはlistサブコマンドを使用します。
$ kitchen list
Instance Driver Provisioner Last Action
default-ubuntu-1404 DockerCli ChefZero <Not Created>
test-kitchenでは「platformsの数」*「suitesの数」だけ同時にテストを行うことができますが、インスタンス名を指定することで、特定のインスタンスだけテストを実行できます。
省略した場合はすべてのインスタンスでテストを実行できます。
インスタンス名はplaftformとsuiteを組み合わせて生成されます。
また、正確には第2引数がマッチするものだけをテストします。
例えば以下のように複数のplatformがあるとします。
$ kitchen list
Instance Driver Provisioner Last Action
default-ubuntu-1404 DockerCli ChefZero <Not Created>
default-centos-66 DockerCli ChefZero <Not Created>
ubuntuのみテストしたい場合は
$ kitchen test ubuntu
とします。何も指定しない場合は両方実行されます。
Test Kitchenのもっと詳しい使い方は
などを読んでみてください。
特にテストの章は大切です。
Serverspec
それではServerspecをかいていきましょう。
期待することはポート80でListenしていることですね。
一応、nginxが走っていること、自動で起動することも確認しておきます。
require 'spec_helper'
describe 'myweb::default' do
describe service('nginx') do
it { should be_enabled }
it { should be_running }
end
describe port('80') do
it { should be_listening.with('tcp') }
end
end
テストの実行はkitchen test
です。
このテストは失敗します。
出力は長いですがよくみると下記のような出力があると思います。
Finished in 0.27516 seconds (files took 0.39046 seconds to load)
3 examples, 3 failures
このテストを成功するためにはなにをすればいいでしょうか?
test-kitchenは失敗するとインスタンスが作成されたままで残っています。
kitchen login
でサーバにログインできます。
テストを通るようにしてみましょう。
$ kitchen login
remote$ apt-get -y install nginx
remote$ service nginx start
remote$ exit
この状態で再度テストのみを実行してみます。
$ kitchen verify
成功したでしょうか。
kitchen test
はインスタンスをつくりなおしてテストを実行しますが、kitchen verify
はインスタンスはそのままでテストの実行だけできます。
この状態で再度kitchen test
を実行するとテストは失敗します。
インスタンスが削除されて、nginxのインストールがなかったことになるからです。
ChefSpec
今度は単体テストフレームワークのChefSpecに挑戦してみましょう。
さきほど手動で行った作業は
- nginxのインストール
- nginxの起動
でした。
ChefSpec化してみましょう。
require 'spec_helper'
describe 'myweb::default' do
let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
it { expect(chef_run).to install_package('nginx') }
it { expect(chef_run).to start_service('nginx') }
end
it
の行に注目してください。
- install_package('nginx')
- start_service('nginx')
で、作業していることを確認できます。
このテストの実行にはrspecコマンドを使います。
$ rspec
ChefSpecの使い方を学ぶにはexampleを見るのがよいです。
ChefSpecの良いところはtest-kitchenの実行に比べて早いことです。
chefを実行するよりも早いと思うので文法チェックなどに使えます。
無理して書く必要はないですが、メタプログラミングしたり、リファクタリングする際にあると有効な場合もあります。
レシピの作成
今度はChefSpecが成功するように実際にレシピを書いていきます。
package 'nginx'
service 'nginx' do
action [:start]
end
Chefではレシピにリソースを記述して、サーバの設定をします。
packageやserviceはリソースを作成する命令です。
作成するリソースのプロパティを設定する場合はdo
とend
で囲んだ部分で行います。
packkage 'nginx'
ではpackage[nginx]
リソースを作成して、
service 'nginx'
ではservice[nginx]
リソースをaction
が:start
になるように設定しています。
レシピを作成したらrspec
を再度実行してみましょう。
テストに成功するはずです。
test-kitchenでのchefの実行
kitchen test
が失敗している状態なので、インスタンスが残っています。
そのままのインスタンスを使ってchefを実行したい場合はkitchen converge
が使えます。
うまくいったらそのままServerspecの実行をしてみましょう。
kitchen verify
で実行します。
boot2dockerを使用している場合はまずIPを確認します。
$ boot2docker ip
192.168.59.103
http://192.168.59.103:8100 にアクセスするとウェブサーバが動いていることが確認できると思います。8100なのは.kitchen.yml
のpublishで設定をしたからです。
インスタンスを作りなおしてもうまくいくのか確認してみましょう。
kitchen test -d never
を使います。
-d never
を使用するとテストに成功してもインスタンスを残すことができます。
さて、ウェブサーバーが動いているのを確認してみましょう。
うまくいったらコミットしておきましょう。
$ git add .
$ git commit -m 'install nginx'
Chefのテンプレートを使う
index.htmlを自分好みのものに変えてみたいと思います。
index.htmlの作成にはテンプレート機能を使ってみます。
テンプレートはcookbookに用意したファイルのテンプレートに変数を埋めてファイルを作成する機能です。
テンプレートの雛形を作成するのにchefコマンドが使えます。
$ chef generate template index.html
そうすると、templates/default/index.html.erb
が作成されます。
自由にファイルを作成しましょう。
折角なので変数を参照してみます。
Hello <%= node.name %>
node.nameにはTest Kitchenの
作成したテンプレートのリソースを作る必要があります。
レシピに内容を追加しましょう。
template '/usr/share/nginx/html/index.html' do
owner 'www-data'
group 'www-data'
mode 644
end
Test Kitchenを実行して反映してみましょう。
kitchen converge
をつかうのが良いでしょう。
実行に成功したら http://192.168.59.103:8100 にアクセスしてみましょう。
作成したhtmlが表示されるでしょうか。
されてませんね。
Foodcritic
Foodcriticを使うとrecipeの中の良くない書き方をみつけて教えてくれます。
$ foodcritic .
FC006: Mode should be quoted or fully specified when setting file permissions: ./recipes/default.rb:13
recipes/default.rb
の13行目に良くない書き方があるようです。
先頭にあるFC006
が問題の種類で、 http://acrmp.github.io/foodcritic/#FC006 を見ると詳しい解説が書いてあります。
modeは文字列で "644"
とかくか頭に00をつけて00644
で書けとあります。
template '/usr/share/nginx/html/index.html' do
owner 'www-data'
group 'www-data'
mode '644'
end
と、なおしてkitchen converge
として再度確認してみましょう。
うまくうごいたら、コミットしておきましょう。
$ git add .
$ git commit -m 'create default index.html'
node
テンプレートの中で<%= node.name %>
としました。
nodeの中にはohaiというツールがサーバ情報を取得して保存されています。
/tmp/kitchen/nodes/
に保存されています。
中身をみてみましょう。
$ kitchen login
remote $ cat /tmp/kitchen/nodes/default-ubuntu-1404.json
OSの種類、CPUの情報、実行したrun_listの情報、Chefの情報…様々な情報が記録されていることがわかると思います。これの情報はテンプレートやレシピの中でも利用できます。
閲覧したら exit しましょう。
remote $ exit
Berkshelf
Berkshelfは他のクックブックを取得するのに使われます。
nginxのコミュニティクックブックを利用してみましょう。
mywebクックブックがnginxクックブックに依存していることをmetadata.rbに記述して上げます。
name 'myweb'
maintainer 'The Authors'
maintainer_email 'you@example.com'
license 'all_rights'
description 'Installs/Configures myweb'
long_description 'Installs/Configures myweb'
version '0.1.0'
depends 'nginx'
追加したのは最後のdepends 'nginx'
のみです。
BerkshelfはBerksfile
に利用するクックブックを記述しますが、
metadata
と記述してあるため、このmetadata.rbの情報を利用してくれます。
コミュニティクックブックを使えばnginxの設定はいろいろ楽ができます。
ohaiプラグインがはいっていたり、attributesを与えるだけで設定を変更できたりします。
recipe/default.rb
を書き換えてみます。
packgeリソースとserviceリソースを削除して、nginxクックブックのdefalutレシピを使うように変更してみます。
include_recipe 'nginx::default'
template '/usr/share/nginx/html/index.html' do
owner 'www-data'
group 'www-data'
mode '644'
end
http://192.168.59.103:8100 にアクセスすると403になります。
コミュニティクックブックによってドキュメントルートが代わってしまっているようです。
attributesを使うことでドキュメントルートを変更することができます。
.kitchen.ymlで変更してみます。
---
driver:
name: docker_cli
require_chef_omnibus: false
run_command:
- curl -L https://www.chef.io/chef/install.sh | bash
publish:
- 8100:80
# no_cache: true
provisioner:
name: chef_zero
platforms:
- name: ubuntu-14.04
# - name: centos-6.6
suites:
- name: default
run_list:
- recipe[myweb::default]
attributes:
nginx:
default_root: /usr/share/nginx/html
以下が追加されています。
nginx:
default_root: /usr/share/nginx/html
kitchen converge
を実行して、
http://192.168.59.103:8100 にアクセスするとindex.htmlが表示されるでしょうか。
こちらでも稀に見れないことがあったので、もしダメだったら kitchen test -d never
を試してみてください。
nginxクックブックではどんなattributesが使えるのかなどは下記を参照してください。
必要があればソースコードも見ると勉強になります。
そういえば、include_recipeを使うとChefSpecが動いてくれないので、つらいです。
include_recipeは使い方を考えたほうが良いかもしれないとおもってたりします。
宿題
つくったcookbookでvagrantのプロビジョンをしてみよう
例
Vagrantfileを作ります。
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "opscode-ubuntu-14.04"
config.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box"
config.vm.provision "shell", inline: <<-SHELL
curl -L https://www.chef.io/chef/install.sh | bash
SHELL
config.vm.provision "chef_zero" do |chef|
chef.cookbooks_path = "cookbooks"
chef.add_recipe "myweb"
chef.json = {
"nginx" => {
"default_root" => "/usr/share/nginx/"
}
}
end
end
次にBerksfileをつくります。
source "https://supermarket.chef.io"
cookbook 'myweb', git: '../myweb' # mywebのディレクトリを指定する
$ berks
$ berks vendor cookbooks
$ vagrant up
attributesをcookbook側で指定しちゃう手もあるにはありますが、もにゅもにゅ。(よりよい方法を調査中)
ここからの発展
- packerでディスクイメージを作成する
- CIで実行する
- どこかのサーバで使用する
どこかのサーバを使用する場合はknife-zeroやknife-soloといった選択肢もありますし、
cookbookをアップロードしてchef-zeroやchfe-soloのオプションでrun-listやattributesを指定できます。
説明してないこと
- chef install
- chef update
- chef push
- chef verify
- chef gem
- chef exec
まとめ
ChefをつかっているかどうかはさておきTest Kitchenはとても便利です。
- 最初から実行しなおしたい
- kitchen test -d never
- Chef(プロビジョナ)をもう一度実行したい
- kitchen converge
- Serverspecを実行したい
- kitchen verify
- インスタンスを削除したい
- kitchen destroy
- インスタンスにログインしたい
- kitchen login
とりえあず、上記ぐらいを最初は覚えておけば良いと思います。