Chef ProvisioningとVagrantでSerfクラスタ環境を作成する #getchef #vagrant #serf

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

はじめに

本稿では、Chef Provisioning と Vagrant (VirtualBox) を用いて、テスト用の Serf クラスタを作成してみます。非常に簡単な例なので、そのまま実環境に流用できるものではありません。ただ、Chef Provisioning の利用例や Serf クラスタの挙動をつかむには十分だと思います。本稿で使用したソースコードはすべて https://github.com/cl-lab-k/chef-serf-cluster にあります。

Serf、Vagrantとは

Serf とは、米 HashiCorp 社が開発するクラスタ管理ツールです。軽量なエージェントと簡単な設定ファイルのみで動作し、非常に手軽にクラスタを構築することができます。

Vagrant とは、こちらも米 HashiCorp 社が開発する、仮想環境を自動で構築するツールです。簡単な設定ファイルで非常に手軽に仮想仮想を作成することができます。

Chef Provisioningとは

Chef Provisioning (旧称:Chef Metal)とは、米 Chef 社が開発するクラスタ管理フレームワークです。Chef の Recipe でマシンを管理することと同じように、クラスタを管理することができます。

事前準備

実験は Debian GNU/Linux 8.1 上で行いました。以下のパッケージやプラグインをインストールしています。

Chef-DK 0.6.0 に同梱の Chef Client 12.3.0 には、/opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rbfixes access keys attribute in knife show の修正を適用しています。本実験では Vagrant プラグインの vagrant-hostsupdater を使っているので、厳密には必要としていません。詳細については後述します。

ソースコードの取得

https://github.com/cl-lab-k/chef-serf-cluster/tree/sample_blog を clone します。


% git clone https://github.com/cl-lab-k/chef-serf-cluster -b sample_blog
Cloning into 'chef-serf-cluster'...
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 29 (delta 0), reused 29 (delta 0), pack-reused 0
Unpacking objects: 100% (29/29), done.
Checking connectivity... done.
Note: checking out 'a29fdafe14e0acb17d08bb9c8b68d2f136e53c5e'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name


% chef-serf-cluster
%

以降はこのツリー内で作業を行います。

Chef-Server VM の作成

Chef Provisioning を用いて、Chef-Server 12 用の仮想マシンを作成します。provisioning/chef-server ディレクトリに移動します。


% cd provisioning/chef-server
% ls -la
合計 16
drwxr-xr-x 2 dai dai 120 6月 12 15:30 .
drwxr-xr-x 4 dai dai 100 6月 12 15:30 ..
-rw-r--r-- 1 dai dai 73 6月 12 15:30 Berksfile
-rw-r--r-- 1 dai dai 405 6月 12 15:30 Rakefile
-rw-r--r-- 1 dai dai 971 6月 12 15:30 chef-server.rb
-rw-r--r-- 1 dai dai 181 6月 12 15:30 destroy.rb
%

設定は chef.json ファイルで行えますが、通常は変更する必要はないでしょう。
一連の作業は基本的に rake タスクとして定義してあります。デフォルトでは berks vendor サブコマンドを実行して chef-server Cookbook を取得し、Chef Provisioning によって Chef-Server 12 用の VirtualBox VM を Vagrant で作成します。

chef-serf-cluster-sample-chef-server

Rakefile を見ての通り、Chef Provisioning は Chef ローカルモードを利用しています(chef-client -z)。


% rake
berks vendor cookbooks
Resolving cookbook dependencies...
Fetching cookbook index from https://supermarket.chef.io...
Using chef-server (3.1.1)
Using packagecloud (0.0.18)
Using chef-server-ingredient (0.4.0)
Vendoring chef-server (3.1.1) to cookbooks/chef-server
Vendoring chef-server-ingredient (0.4.0) to cookbooks/chef-server-ingredient
Vendoring packagecloud (0.0.18) to cookbooks/packagecloud
CHEF_DRIVER=vagrant chef-client -z chef-server.rb
[2015-06-12T15:54:31+09:00] WARN: No config file found or specified on command line, using command line options.
tarting Chef Client, version 12.3.0
resolving cookbooks for run list: []
Synchronizing Cookbooks:
Compiling Cookbooks...
[2015-06-12T15:54:32+09:00] WARN: Node XXXXXXXX has an empty run list.
Converging 1 resources
Recipe: @recipe_files::/tmp/chef-serf-cluster/provisioning/chef-server/chef-server.rb
* machine[chef-server] action converge
:
:
:
Running handlers:
Running handlers complete
Chef Client finished, 13/15 resources updated in 187.109386539 seconds
- run 'chef-client -l auto' on chef-server


Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 244.394462204 seconds
%

これで https://chef-server.example.jp (192.168.33.10) に Chef-Server 12 用の VM が起動しました。本実験では必要ないため、Chef Manage アドオンをインストールしていないので Web UI はありません。

Serf ノード用 VM の作成

さらに Chef Provisioning を用いて、Serf のノードとなる VM を作成します。provisioning/vms ディレクトリに移動します。


% cd ../..
% cd provisioning/vms
% ls -la
合計 8
drwxr-xr-x 2 dai dai 80 6月 12 15:30 .
drwxr-xr-x 4 dai dai 100 6月 12 15:30 ..
-rw-r--r-- 1 dai dai 3030 6月 12 15:30 Rakefile
-rw-r--r-- 1 dai dai 1390 6月 12 15:30 vms.rb
%

設定は Chef-Server 用 VM の場合と同じく chef.json ファイルで行えますが、通常は変更する必要はないでしょう。
こちらも作業は rake タスクとして定義してあります。デフォルトでは Chef-Server にて User と Organization を作成し、両者の鍵を取得、knife.rb を作成、knife ssl fetch を実行して鍵取得(参考: Chef 12の新機能: knife ssl check/fetch)、Chef Provisioning によって Serf ノード用の VirtualBox VM を Vagrant で 3台作成します。

chef-serf-cluster-sample-vms

作成した Serf ノード用 VM の Node Object は Chef-Server 12 に登録されます。Rakefile では Chef Provisioning は Chef ローカルモードを利用しています(chef-client -z)が、これはあくまで Chef Provisioning が必要としているものなので、混同しないようにしてください。


% rake
ssh -i ~/.chef/vms/.vagrant/machines/chef-server/virtualbox/private_key vagrant@chef-server.example.jp 'sudo chef-server-ctl user-create testuser TEST USER testuser@example.jp testpswd --filename testuser.pem'
scp -i ~/.chef/vms/.vagrant/machines/chef-server/virtualbox/private_key vagrant@chef-server.example.jp:testuser.pem /tmp/chef-serf-cluster/provisioning/vms/../../.chef/testuser.pem
:
:
:
knife ssl fetch
WARNING: Certificates from chef-server.example.jp will be fetched and placed in your trusted_cert
directory (/tmp/chef-serf-cluster/.chef/trusted_certs).
:
:
:
CHEF_DRIVER=vagrant chef-client -z vms.rb
[2015-06-12T16:28:02+09:00] WARN: No cookbooks directory found at or above current directory. Assuming /tmp/chef-serf-cluster/provisioning/vms.
Starting Chef Client, version 12.3.0
resolving cookbooks for run list: []
Synchronizing Cookbooks:
Compiling Cookbooks...
[2015-06-12T16:28:03+09:00] WARN: Node testuser has an empty run list.
Converging 3 resources
Recipe: @recipe_files::/tmp/chef-serf-cluster/provisioning/vms/vms.rb
* machine[node101] action converge
:
:
:
- run 'chef-client -l auto' on node103


Running handlers:
Running handlers complete
Chef Client finished, 3/3 resources updated in 173.498869383 seconds

  • node101 (192.168.33.101)
  • node102 (192.168.33.102)
  • node103 (192.168.33.103)

の 3台の Serf 用 VM が起動しました。

さて、これらの VM に knife ssh などからアクセスするためには、仕掛けが必要です。なぜなら、これらの VM の Attribute "ipaddress" はすべて 10.0.2.15 だからです。

本実験では vagrant-hostsupdater を用いて、ホスト側の /etc/hosts を更新するようになっています。


% tail -5 /etc/hosts
192.168.33.10 chef-server # VAGRANT: d60dbc76afe87633bd242cc480b13aa6 (chef-server) / c8c040ff-f349-48dd-803f-a7f0ef94b223
192.168.33.10 chef-server.example.jp # VAGRANT: d60dbc76afe87633bd242cc480b13aa6 (chef-server) / c8c040ff-f349-48dd-803f-a7f0ef94b223
192.168.33.101 node101 # VAGRANT: a0983c432e6838a69f708f6068a0f772 (node101) / 966fb052-054b-4d9b-91de-5fc38314cc4b
192.168.33.102 node102 # VAGRANT: 9226e6d398b35f9723c957b821058308 (node102) / 6b994ee1-5c04-4537-bed3-68465f1dc7c3
192.168.33.103 node103 # VAGRANT: 9425c22ebf37ecc98719c19e104cf5ed (node103) / 14175974-6183-47aa-9b7e-b3043ec64cf2
%

これにより、knife コマンドはそのまま利用可能です。


% knife ssh "name:node101" -x vagrant -P vagrant uptime
node101 07:49:49 up 21 min, 1 user, load average: 0.00, 0.01, 0.02

もし、vagrant-hostsupdater を用いるなどして名前解決できるようにしていない場合、knife コマンドは 10.0.2.15 に接続に行ってしまいます。


% knife ssh "name:node101" -x vagrant -P vagrant hostname
WARNING: Failed to connect to 10.0.2.15 -- Errno::ETIMEDOUT: Connection timed out - connect(2) for "10.0.2.15" port 22
%

このように接続できません。eth1 の IPアドレスに対してアクセスするには -a network.interfaces.eth1.addresses.keys.rotate.first のようにする必要があります(参考: えー、これでもいけました。。。が普通にオススメできない。 `knife zero chef_client "name:*" -x vagrant --sudo -a network.interfaces.eth1.addresses.keys.rotate.first`)。しかし、Chef-DK 0.6.0 に同梱の Chef Client 12.3.0 はこの -a オプションの引数の取り扱いにバグがあり、このように指定しても接続できないのです(参考: Pass name by knife cil attribute)。


% % knife ssh "name:node101" -x vagrant -P vagrant -a network.interfaces.eth1.addresses.keys.rotate.first hostname
FATAL: 1 node found, but does not have the required attribute to establish the connection. Try setting another attribute to open the connection using --attribute.
%

この修正は Chef Client の HEAD には取り込まれていますが、12.3.0 には取り込まれていません。よって手で修正する必要があります。


% sudo cp -a /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb.orig
% sudo vi /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb
% diff -u /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb.orig /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb
--- /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb.orig2015-06-12 16:44:44.584210095 +0900
+++ /opt/chefdk/embedded/apps/chef/lib/chef/knife/core/generic_presenter.rb 2015-06-12 16:45:47.427309197 +0900
@@ -181,7 +181,7 @@
# Must check :[] before attr because spec can include
# `keys` - want the key named `keys`, not a list of
# available keys.
- elsif data.respond_to?(:[])
+ elsif data.respond_to?(:[]) && data.kind_of?(Chef::Node)
data = data[attr]
elsif data.respond_to?(attr.to_sym)
data = data.send(attr.to_sym)
%

この修正を施した後ならば、接続が可能となります。


% knife ssh "name:node101" -x vagrant -P vagrant -a network.interfaces.eth1.addresses.keys.rotate.first hostname
192.168.33.101 node101

先に述べた通り、本実験では vagrant-hostsupdater を利用しているため、この修正は厳密には必要ありません。

ポリシー

トップディレクトリに移動します。ここには Recipe、Role、Attribute 等、chef-serf-cluster Cookbook のポリシーがあります。


% cd ../..
% ls -la
合計 24
drwxr-xr-x 9 dai dai 300 6月 12 16:57 .
drwxrwxrwt 86 root root 1920 6月 12 16:57 ..
drwxr-xr-x 5 dai dai 160 6月 12 16:28 .chef
drwxr-xr-x 8 dai dai 280 6月 12 16:57 .git
-rw-r--r-- 1 dai dai 126 6月 12 15:30 .gitignore
-rw-r--r-- 1 dai dai 47 6月 12 15:30 Berksfile
-rw-r--r-- 1 dai dai 805 6月 12 15:30 README.md
-rw-r--r-- 1 dai dai 825 6月 12 16:57 Rakefile
drwxr-xr-x 2 dai dai 60 6月 12 15:30 attributes
-rw-r--r-- 1 dai dai 1022 6月 12 15:30 chefignore
drwxr-xr-x 3 dai dai 60 6月 12 15:30 files
-rw-r--r-- 1 dai dai 298 6月 12 15:30 metadata.rb
drwxr-xr-x 4 dai dai 100 6月 12 15:30 provisioning
drwxr-xr-x 2 dai dai 60 6月 12 15:30 recipes
drwxr-xr-x 2 dai dai 60 6月 12 15:30 roles
%

設定は attributes/default.rbroles/chef-serf-cluster.json にあります。それぞれ serf Cookbook と sudo Cookbook の設定となります。通常は変更の必要はないでしょう。

serf 設定

default['serf']['event_handlers'] は Serf のイベントハンドラ設定のハッシュを配列として定義します。url はイベントハンドラとして実行するスクリプトのダウンロード先です。本実験ではローカルファイルシステムから取得するため、file:// としてあります。event_type はスクリプトを実行するイベントをコンマで連結した文字列です。この例では Serf クラスタに新しいメンバーが参加(member-join)、メンバーとの通信が途絶(member-failed)、メンバーが離脱(member-leave)の場合にスクリプトを実行します。詳しくは Event Handlers を参照してください。

default['serf']['version'] はインストールする Serf のバージョンを指定します。Cookbook のデフォルトでは 2013/12/5 に公開された 0.3.0 という大変古いバージョンなので必ず新しいバージョンを指定します。本実験では 0.4.x で追加された -iface (interface) や -discover といった機能を使っています。詳しくは Configuration を参照してください。

default['serf']['agent']['discover'] は Serf クラスタの名前を指定します。Serf は mDNS を利用して自動的に Serf のピアを探し出します。そのためお互いの IP アドレスを指定しなくてよいという利点があります。ただし、mDNS (マルチキャスト)が使えるネットワークであることが必要です。

default['serf']['agent']['interface'] は Serf エージェントが待ち受けするインターフェイスを指定します。先の discover はこのインターフェイスで行います。詳しくは Configuration を参照してください。本実験では Vagrant がパブリックな IP アドレスとする eth1 を指定しています。

sudo 設定

Serf Cookbook では、Serf エージェントを serf という一般ユーザの権限で動作するように設定するため、他のユーザの権限が必要な操作が行えません。本実験では、イベントハンドラで実行するスクリプト内で sudo を使って root 権限を得るようにしています。serf ユーザが sudo を介してイベントハンドラのスクリプトを実行できるように設定します。

default['authorization']['sudo']['sudoers_defaults'] では、Ubuntu 14.04 の /etc/sudoers のデフォルトと同じになるよう設定に加えて、SERF_EVENT 環境変数を保持する設定をしています。通常、sudo は指定以外の環境変数を削除するため、イベントハンドラの処理に必要な SERF_EVENT 環境変数まで消去してしまいます。これを防ぎます。sudo の設定に関しては root 権限で sudo -V を実行すると確認できます。

default['authorization']['sudo']['users'] では、serf ユーザを sudo 可能ユーザとして追加しています。

default['authorization']['sudo']['groups'] では、Ubuntu 14.04 の /etc/sudoers のデフォルトと同じになるよう設定を行っています。

default['authorization']['sudo']['passwordless'] では、パスワードを求めないように設定しています。

chef-serf-cluster Cookbook

default Recipe

files/default/sample.conf から /etc/sample.conf を作成します。このファイルが Serf イベントハンドラで更新する対象になります。
Serf イベントハンドラで Chef-Client を実行して収束を行い、ファイルを更新するのではないという点に注目してください。なぜなら、Serf クラスタに所属しているメンバーに変化があったからといって、Chef-Server が保持しているインベントリに変化があるとは限らないからです。やりようによってはそのような処理も可能でしょう。しかし本実験では簡便さのため、Serf イベントハンドラに Chef-Server を介入させないこととしました。

file:///var/lib/serf/scripts/chef-apply.sh です。Serf イベントハンドラとして実行されるスクリプトです。sudo を介して chef-apply を実行します。chef-apply は Recipe を実行するためだけの極小の Chef Client で、Chef-Server との通信は行いません。シェルスクリプトの代わりとして Chef DSL の恩恵にあずかることができます(参考: [和訳] Bashスクリプトをchef-applyに変換する)。

/var/lib/serf/scripts/update-sample-conf.rb は chef-apply が実行する Recipe です。Chef::Util を利用して /etc/sample.conf を更新します(参考: Chefのレシピでsed的な事を実施)。serf エージェントからの通知を環境変数と標準入力で受け取り、Serf クラスタに新しいメンバーが参加した場合はファイルに追加、Serf クラスタのメンバーから通信が途絶したか Serf クラスタからメンバーが離脱した場合はファイルから削除します。

ポリシーを Chef-Server アップロード

先程作成した Serf 用 Node は run_list が空のため、何も起きません。Node に適用するためのポリシーを Chef-Server にアップロードします。こちらも作業はrake タスクとして定義してあります。デフォルトでは berks vendor サブコマンド、berks upload サブコマンドを実行して Cookbook をアップロードし、knife role サブコマンドを実行して Role をアップロード、Serf 用 Node の収束を行います。この時点では run_list を設定していないため、収束を行っても Node に特に変化はありません。


% rake
berks vendor cookbooks
Resolving cookbook dependencies...
Fetching 'chef-serf-cluster' from source at .
Fetching cookbook index from https://supermarket.chef.io...
Using sudo (2.7.1)
Using serf (0.9.0)
Using chef-serf-cluster (0.1.0) from source at .
Using logrotate (1.9.1)
Vendoring chef-serf-cluster (0.1.0) to cookbooks/chef-serf-cluster
Vendoring logrotate (1.9.1) to cookbooks/logrotate
Vendoring serf (0.9.0) to cookbooks/serf
Vendoring sudo (2.7.1) to cookbooks/sudo
berks upload --no-ssl-verify --force
Uploaded chef-serf-cluster (0.1.0) to: 'https://chef-server.example.jp:443/organizations/testorg'
Uploaded logrotate (1.9.1) to: 'https://chef-server.example.jp:443/organizations/testorg'
Uploaded serf (0.9.0) to: 'https://chef-server.example.jp:443/organizations/testorg'
Uploaded sudo (2.7.1) to: 'https://chef-server.example.jp:443/organizations/testorg'
knife role from file roles/chef-serf-cluster.json
Updated Role chef-serf-cluster!
knife ssh 'name:*' -x vagrant -P vagrant 'sudo chef-client'
node101 Starting Chef Client, version 12.3.0
node103 Starting Chef Client, version 12.3.0
node102 Starting Chef Client, version 12.3.0
node101 resolving cookbooks for run list: []
node103 resolving cookbooks for run list: []
node101 Synchronizing Cookbooks:
node101 Compiling Cookbooks...
node101 [2015-06-12T08:44:24+00:00] WARN: Node node101 has an empty run list.
node101 Converging 0 resources
node101
node101 Running handlers:
node101 Running handlers complete
node101 Chef Client finished, 0/0 resources updated in 1.267921461 seconds
node103 Synchronizing Cookbooks:
node103 Compiling Cookbooks...
node103 [2015-06-12T08:44:24+00:00] WARN: Node node103 has an empty run list.
node103 Converging 0 resources
node103
node103 Running handlers:
node103 Running handlers complete
node103 Chef Client finished, 0/0 resources updated in 1.315651996 seconds
node102 resolving cookbooks for run list: []
node102 Synchronizing Cookbooks:
node102 Compiling Cookbooks...
node102 [2015-06-12T08:44:29+00:00] WARN: Node node102 has an empty run list.
node102 Converging 0 resources
node102
node102 Running handlers:
node102 Running handlers complete
node102 Chef Client finished, 0/0 resources updated in 6.391368714 seconds
%

Serf 用 VM にポリシーを適用

では、Serf 用 VM に 1つずつポリシーを適用していきます。


% knife node run_list add node101 "role[chef-serf-cluster]"
node101:
run_list: role[chef-serf-cluster]

適用したら、収束を行います。


% rake converge
knife ssh 'name:*' -x vagrant -P vagrant 'sudo chef-client'
node103 Starting Chef Client, version 12.3.0
node101 Starting Chef Client, version 12.3.0
node102 Starting Chef Client, version 12.3.0
node102 resolving cookbooks for run list: []
node101 resolving cookbooks for run list: ["sudo", "chef-serf-cluster", "serf"]
node102 Synchronizing Cookbooks:
node102 Compiling Cookbooks...
node102 [2015-06-12T08:46:55+00:00] WARN: Node node102 has an empty run list.
node102 Converging 0 resources
node102
node102 Running handlers:
node102 Running handlers complete
node102 Chef Client finished, 0/0 resources updated in 1.317966938 seconds
node101 Synchronizing Cookbooks:
node101 - sudo
node101 - chef-serf-cluster
node101 - serf
node101 - logrotate
node101 Compiling Cookbooks...
node101 Converging 27 resources
node101 Recipe: sudo::default
:
:
:
node101 Running handlers:
node101 Running handlers complete
node101 Chef Client finished, 27/29 resources updated in 17.392931293 seconds

VM を確認してみます。


% rake serf_members
knife ssh 'name:*' -x vagrant -P vagrant 'serf members'
node101 node101 192.168.33.101:7946 alive
node102 bash: serf: command not found
node103 bash: serf: command not found
rake aborted!
Command failed with status (127): [knife ssh 'name:*' -x vagrant -P vagrant '...]
/tmp/chef-serf-cluster/Rakefile:38:in `block in '
Tasks: TOP => serf_members
(See full trace by running task with --trace)


% rake sample_conf
knife ssh 'name:*' -x vagrant -P vagrant 'cat /etc/sample.conf'
node101 server {
node101 server 192.168.33.101;
node101 #server
node101 }
node102 cat: /etc/sample.conf: No such file or directory
node103 cat: /etc/sample.conf: No such file or directory
rake aborted!
Command failed with status (1): [knife ssh 'name:*' -x vagrant -P vagrant '...]
/tmp/chef-serf-cluster/Rakefile:43:in `block in '
Tasks: TOP => sample_conf
(See full trace by running task with --trace)

まず、node101 のみが Serf クラスタのメンバーとなっていて、/etc/sample.conf ファイルにも node101 のみが記載されています。

chef-serf-cluster-sample-install-1

次に node102 にポリシーを適用します。


% knife node run_list add node102 "role[chef-serf-cluster]"
node102:
run_list: role[chef-serf-cluster]


% rake converge
knife ssh 'name:*' -x vagrant -P vagrant 'sudo chef-client'
node101 Starting Chef Client, version 12.3.0
node102 Starting Chef Client, version 12.3.0
node103 Starting Chef Client, version 12.3.0
node102 resolving cookbooks for run list: ["sudo", "chef-serf-cluster", "serf"]
node102 Synchronizing Cookbooks:
node102 - sudo
node102 - chef-serf-cluster
node102 - serf
node102 - logrotate
node102 Compiling Cookbooks...
node102 Converging 27 resources
node102 Recipe: sudo::default
:
:
:
node102 Running handlers:
node102 Running handlers complete
node102 Chef Client finished, 27/29 resources updated in 18.963169116 seconds

VM を確認します。


% LC_ALL=C TERM=xterm-color rake serf_members
knife ssh 'name:*' -x vagrant -P vagrant 'serf members'
node103 bash: serf: command not found
node102 node102 192.168.33.102:7946 alive
node102 node101 192.168.33.101:7946 alive
node101 node101 192.168.33.101:7946 alive
node101 node102 192.168.33.102:7946 alive
rake aborted!
Command failed with status (127): [knife ssh 'name:*' -x vagrant -P vagrant '...]
/tmp/chef-serf-cluster/Rakefile:38:in `block in '
Tasks: TOP => serf_members
(See full trace by running task with --trace)


% rake sample_conf
knife ssh 'name:*' -x vagrant -P vagrant 'cat /etc/sample.conf'
node103 cat: /etc/sample.conf: No such file or directory
node102 server {
node102 server 192.168.33.102;
node102 server 192.168.33.101;
node102 #server
node102 }
node101 server {
node101 server 192.168.33.101;
node101 server 192.168.33.102;
node101 #server
node101 }
rake aborted!
Command failed with status (1): [knife ssh 'name:*' -x vagrant -P vagrant '...]
/tmp/chef-serf-cluster/Rakefile:43:in `block in '
Tasks: TOP => sample_conf
(See full trace by running task with --trace)

node102 も追加されました。なお、/etc/sample.conf の更新は chef-client コマンドの収束によって行われたのではありません。Serf イベントハンドラによって呼び出された chef-apply コマンドによって行われています。事実、chef-client コマンドによって適用されるポリシーはファイルを置くだけで、chef-apply コマンドによって適用されるポリシーがファイルを更新します。

chef-serf-cluster-sample-install-2

node103 にもポリシーを適用します。


% knife node run_list add node103 "role[chef-serf-cluster]"
node103:
run_list: role[chef-serf-cluster]


% rake converge
knife ssh 'name:*' -x vagrant -P vagrant 'sudo chef-client'
node101 Starting Chef Client, version 12.3.0
node103 Starting Chef Client, version 12.3.0
node102 Starting Chef Client, version 12.3.0
node103 resolving cookbooks for run list: ["sudo", "chef-serf-cluster", "serf"]
node103 Synchronizing Cookbooks:
node103 - chef-serf-cluster
node103 - serf
node103 - logrotate
node103 Compiling Cookbooks...
node103 Converging 27 resources
node103 Recipe: sudo::default
:
:
:
node103 Running handlers:
node103 Running handlers complete
node103 Chef Client finished, 27/29 resources updated in 17.828711263 seconds
%

node103 も追加されました。


% rake serf_members
knife ssh 'name:*' -x vagrant -P vagrant 'serf members'
node101 node103 192.168.33.103:7946 alive
node101 node101 192.168.33.101:7946 alive
node101 node102 192.168.33.102:7946 alive
node103 node101 192.168.33.101:7946 alive
node103 node102 192.168.33.102:7946 alive
node103 node103 192.168.33.103:7946 alive
node102 node102 192.168.33.102:7946 alive
node102 node101 192.168.33.101:7946 alive
node102 node103 192.168.33.103:7946 alive


% rake sample_conf
knife ssh 'name:*' -x vagrant -P vagrant 'cat /etc/sample.conf'
node103 server {
node103 server 192.168.33.101;
node103 server 192.168.33.102;
node103 server 192.168.33.103;
node103 #server
node103 }
node102 server {
node102 server 192.168.33.102;
node102 server 192.168.33.101;
node102 server 192.168.33.103;
node102 #server
node102 }
node101 server {
node101 server 192.168.33.101;
node101 server 192.168.33.102;
node101 server 192.168.33.103;
node101 #server
node101 }
%

chef-serf-cluster-sample-install-3

Serf 用 VM の停止と復帰

試しに node102 を halt してみます。


% vagrant halt node102
==> node102: Removing cache buckets symlinks...
==> node102: Attempting graceful shutdown of VM...
==> node102: Removing hosts
%

VM を確認します。


% rake serf_members
knife ssh 'name:*' -x vagrant -P vagrant 'serf members'
WARNING: Failed to connect to node102 -- SocketError: getaddrinfo: Name or service not known
node101 node101 192.168.33.101:7946 alive
node101 node102 192.168.33.102:7946 left
node101 node103 192.168.33.103:7946 alive
node103 node103 192.168.33.103:7946 alive
node103 node101 192.168.33.101:7946 alive
node103 node102 192.168.33.102:7946 left


% rake sample_conf
knife ssh 'name:*' -x vagrant -P vagrant 'cat /etc/sample.conf'
WARNING: Failed to connect to node102 -- SocketError: getaddrinfo: Name or service not known
node103 server {
node103 server 192.168.33.101;
node103 server 192.168.33.103;
node103 #server
node103 }
node101 server {
node101 server 192.168.33.101;
node101 server 192.168.33.103;
node101 #server
node101 }

node102 は Serf クラスタから離脱した left ステータスとなり、/etc/sample.conf からも削除されました。

node102 を up します。


% vagrant up node102
Bringing machine 'node102' up with 'virtualbox' provider...
==> node102: Checking if box 'chef/ubuntu-14.04' is up to date...
==> node102: Clearing any previously set forwarded ports...
==> node102: Fixed port collision for 22 => 2222. Now on port 2201.
==> node102: Clearing any previously set network interfaces...
==> node102: Preparing network interfaces based on configuration...
node102: Adapter 1: nat
node102: Adapter 2: hostonly
==> node102: Forwarding ports...
node102: 22 => 2201 (adapter 1)
==> node102: Running 'pre-boot' VM customizations...
==> node102: Booting VM...
==> node102: Waiting for machine to boot. This may take a few minutes...
node102: SSH address: 127.0.0.1:2201
node102: SSH username: vagrant
node102: SSH auth method: private key
node102: Warning: Connection timeout. Retrying...
==> node102: Machine booted and ready!
==> node102: Checking for guest additions in VM...
==> node102: Checking for host entries
==> node102: adding to (/etc/hosts) : 192.168.33.102 node102 # VAGRANT: 9226e6d398b35f9723c957b821058308 (node102) / 6b994ee1-5c04-4537-bed3-68465f1dc7c3
==> node102: Setting hostname...
==> node102: Configuring and enabling network interfaces...
==> node102: Mounting shared folders...
node102: /vagrant => /tmp/chef-serf-cluster/.chef/vms
node102: /tmp/vagrant-cache => /var/local/kitchen/vagrant/cache/chef/ubuntu-14.04
==> node102: Configuring cache buckets...
==> node102: Skipping Yum cache bucket as the guest machine does not support it
==> node102: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> node102: to force provisioning. Provisioners marked to run always will still run.
%

VM を確認します。


% rake serf_members
knife ssh 'name:*' -x vagrant -P vagrant 'serf members'
node101 node101 192.168.33.101:7946 alive
node101 node102 192.168.33.102:7946 alive
node101 node103 192.168.33.103:7946 alive
node102 node101 192.168.33.101:7946 alive
node102 node102 192.168.33.102:7946 alive
node102 node103 192.168.33.103:7946 alive
node103 node103 192.168.33.103:7946 alive
node103 node101 192.168.33.101:7946 alive
node103 node102 192.168.33.102:7946 alive
%


% rake sample_conf
knife ssh 'name:*' -x vagrant -P vagrant 'cat /etc/sample.conf'
node101 server {
node101 server 192.168.33.101;
node101 server 192.168.33.103;
node101 server 192.168.33.102;
node101 #server
node101 }
node103 server {
node103 server 192.168.33.101;
node103 server 192.168.33.103;
node103 server 192.168.33.102;
node103 #server
node103 }
node102 server {
node102 server 192.168.33.102;
node102 server 192.168.33.103;
node102 server 192.168.33.101;
node102 #server
node102 }
%

node102 が Serf クラスタに復帰し、/etc/sample.conf に追加されました。ファイルの更新はすべて Serf イベントハンドラから呼び出された chef-apply によって行われています。

chef-serf-cluster-sample-serf

まとめ

本稿では、Chef ProvisioningとVagrant (VirtualBox)を用いて、非常に簡単な Serf クラスタを作成してみました。Chef Provisioning を使った Chef-Server/Client 環境の作成、Serf クラスタの動作、chef-apply の活用例について、概要をつかんでいただけたと思います。

当初は Nginx によるロードバランサを作成する予定だった実験は、簡便のためにかなり縮小し、Serf クラスタはノードがすべて対等となりました。/etc/sample.conf ファイルの内容は当初の計画のなごりです。Serf ノードの役割を個別に割り当てれば、そのような動作も可能となるでしょう。また本文中でも述べた通り、Serf イベントハンドラとして chef-client コマンドを起動する方法については仕組みが複雑になるため断念しました。Serf から chef-client を起動して収束を行うためのよい方法がないかは今後の課題です。

参考文献

Chef-Provisioning

Serf

Chef

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA