Chef ProvisioningとDockerでSerfクラスタ環境を作成する #getchef #docker #serf
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
はじめに
本稿では、Chef Provisioning と Docker を用いて Serf クラスタを作成してみます。なお、Serf クラスタの挙動については深く掘り下げず、Chef Provisioning と Docker の利用例を主目的とします。本稿で使用したソースコードはすべて https://github.com/cl-lab-k/chef-serf-docker-cluster にあります。
なお、Chef Provisioning と Docker の組み合わせとして knife-container がありましたがプロジェクト終了しており、現在は chef-provisioning-docker の利用が推奨されています。
Chef Provisioning とは
Chef Provisioning (旧称:Chef Metal)とは、米 Chef 社が開発するクラスタ管理フレームワークです。Chef の Recipe でマシンを管理することと同じように、クラスタを管理することができます。
- 参考: [和訳] Chef Provisioning (旧Chef Metal): Infrastructure As Code
- 参考: Chef ProvisioningとVagrantでSerfクラスタ環境を作成する
Docker とは
Docker は米 Docker 社が開発する Linux コンテナ管理ソフトウェアです。さまざまな既存ソフトウェアを組み合わせて、アプリケーションとその依存関係にあるものをパッケージングする方法を提供します。
Serf とは
Serf とは、米 HashiCorp 社が開発するクラスタ管理ツールです。軽量なエージェントと簡単な設定ファイルのみで動作し、非常に手軽にクラスタを構築することができます。
事前準備
実験は Debian GNU/Linux 8.1 上で行いました。以下のパッケージやプラグインをインストールしています。
- Chef-DK 0.6.0
- Vagrant 1.7.2
- Vagrant プラグイン: vagrant-hostsupdater 0.0.11
- Vagrant プラグイン: vagrant-cachier 1.2.0
- VirtualBox 4.3.28
ソースコードの取得
https://github. com/cl-lab-k/chef-serf-docker-cluster/tree/sample_blog を clone します。
% git clone https://github.com/cl-lab-k/chef-serf-docker-cluster -b sample_blog Cloning into 'chef-serf-docker-cluster'... : % cd chef-serf-docker-cluster %
以降はこのツリー内で作業を行います。
VM の作成
Vagrant を用いて、今回の実験用の仮想マシンを作成します。
% ls -la 合計 28 drwxr-xr-x 6 dai dai 260 7月 1 12:50 ./ drwxrwxrwt 29 root root 780 7月 1 12:51 ../ drwxr-xr-x 2 dai dai 60 6月 29 18:01 .chef/ drwxr-xr-x 8 dai dai 300 7月 1 12:51 .git/ -rw-r--r-- 1 dai dai 177 6月 30 13:26 .gitignore -rw-r--r-- 1 dai dai 47 7月 1 12:47 Berksfile -rw-r--r-- 1 dai dai 818 7月 1 12:51 README.md -rw-r--r-- 1 dai dai 598 7月 1 12:49 Rakefile -rw-r--r-- 1 dai dai 483 6月 30 15:53 Vagrantfile -rw-r--r-- 1 dai dai 1114 6月 30 12:11 chefignore -rw-r--r-- 1 dai dai 324 7月 1 12:47 metadata.rb drwxr-xr-x 3 dai dai 60 6月 30 18:26 provisioning/ drwxr-xr-x 2 dai dai 60 7月 1 11:10 recipes/ %
作成する VM の設定は Vagrantfile ファイルで行えます。初期設定では、Box は chef/ubuntu-14.04、IP アドレスは 192.168.33.101、メモリは 4096M の設定となっています。一連の作業は基本的に rake タスクとして定義してあります。デフォルトでは berks vendor サブコマンドを実行して chef-dk Cookbook と docker Cookbook を取得し、Vagrant によって VirtualBox VM を作成します。起動した VM には Chef Zero Provisioner を用いて chef-serf-docker-cluster::default Recipe を適用し、Docker のインストールと起動、vagrant ユーザの所属グループの変更、chef-provisioning-docker のインストールを行い、Chef Provisioner Docker 用のファイルを VM に転送します。
% rake berks vendor cookbooks Resolving cookbook dependencies... Fetching 'chef-serf-docker-cluster' from source at . Fetching cookbook index from https://supermarket.chef.io... Using chef-dk (3.1.0) Using chef-serf-docker-cluster (0.1.0) from source at . Using dmg (2.2.2) Using docker (0.40.0) Vendoring chef-dk (3.1.0) to cookbooks/chef-dk Vendoring chef-serf-docker-cluster (0.1.0) to cookbooks/chef-serf-docker-cluster Vendoring dmg (2.2.2) to cookbooks/dmg Vendoring docker (0.40.0) to cookbooks/docker vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'chef/ubuntu-14.04'... ==> default: Matching MAC address for NAT networking... ==> default: Checking if box 'chef/ubuntu-14.04' is up to date... ==> default: Setting the name of the VM: chef-serf-docker-cluster_default_1435722731564_70369 : : : ==> default: Running provisioner: chef_zero... default: Installing Chef (latest)... Generating chef JSON and uploading... ==> default: Running chef-zero... ==> default: stdin: is not a tty ==> default: [2015-07-01T03:53:25+00:00] INFO: Started chef-zero at chefzero://localhost:8889 with repository at /tmp/vagrant-chef/abb1ed29161e26e71af16852033b75af : : : scp -i /tmp/chef-serf-docker-cluster/.vagrant/machines/default/virtualbox/private_key -P 2222 -r /tmp/chef-serf-docker-cluster/provisioning/docker vagrant@127.0.0.1: Berksfile 100% 54 0.1KB/s 00:00 docker.rb 100% 731 0.7KB/s 00:00 Rakefile 100% 1022 1.0KB/s 00:00 %
VM 内で Chef Provisioning Docker を実行
作成した VM にログインします。
% vagrant ssh Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-24-generic x86_64) * Documentation: https://help.ubuntu.com/ Last login: Tue Oct 21 14:52:42 2014 from 10.0.2.2 vagrant@vagrant:~$
先程 VM に scp したディレクトリがあるので、そちらに移動します。
vagrant@vagrant:~$ cd docker vagrant@vagrant:~/docker$ ls -la total 20 drwxr-xr-x 2 vagrant vagrant 4096 Jul 1 03:55 . drwxr-xr-x 5 vagrant vagrant 4096 Jul 1 03:55 .. -rw-r--r-- 1 vagrant vagrant 54 Jul 1 03:55 Berksfile -rw-r--r-- 1 vagrant vagrant 1022 Jul 1 03:55 Rakefile -rw-r--r-- 1 vagrant vagrant 731 Jul 1 03:55 docker.rb vagrant@vagrant:~/docker$
ここでは chef-provisioning-docker を用いて、Docker 社の公式レポジトリの ubuntu:14.04 イメージから Serf 用イメージを作成し、それを基に 5つ Serf 用コンテナを起動します。起動した Serf 用コンテナは自動的にクラスタを構成します。
なお、この chef-serf-docker-cluster/provisioning/docker は VM 内でなくとも実機上で実行が可能ですので、興味があれば各自試してみてください。
こちらも作業は rake タスクとして定義してあります。ただし、この VM には Ubuntu 公式の rake パッケージが入っていないため、代わりに chef-dk 同梱の /opt/chefdk/embedded/bin/rake を実行してください。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/rake berks vendor cookbooks Resolving cookbook dependencies... Fetching cookbook index from https://supermarket.chef.io... Installing logrotate (1.9.2) Installing serf (0.9.0) Vendoring logrotate (1.9.2) to cookbooks/logrotate Vendoring serf (0.9.0) to cookbooks/serf
まず Berkshelf で必要な serf Cookbook の取得を行っています。
CHEF_DRIVER=docker chef-client -z docker.rb
[2015-07-01T06:37:17+00:00] WARN: No config file found or specified on command line, using command line options.
Starting Chef Client, version 12.3.0
resolving cookbooks for run list: []
Synchronizing Cookbooks:
Compiling Cookbooks...
[2015-07-01T06:37:18+00:00] WARN: Node vagrant.vm has an empty run list.
Converging 6 resources
Recipe: @recipe_files::/home/vagrant/docker/docker.rb
* machine_image[serf] action create
- create node serf at chefzero://localhost:8889
- add normal.serf = {"version"=>"0.6.4", "agent"=>{"discover"=>"docker"}}
- add normal.tags = nil
- add normal.chef_provisioning = {"reference"=>{"driver_url"=>"docker:unix:///var/run/docker.sock", "driver_version"=>"0.7", "allocated_at"=>"2015-07-01 06:37:18 UTC", "host_node"=>"chefzero://localhost:8889/nodes/", "container_name"=>"serf", "image_id"=>nil, "docker_options"=>{:base_image=>{:name=>"ubuntu", :repository=>"ubuntu", :tag=>"14.04"}}}}
- update run_list from [] to ["recipe[serf::default]"]
- update node serf at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.base_image = {:name=>"ubuntu", :repository=>"ubuntu", :tag=>"14.04"}
- remove normal.chef_provisioning.reference.docker_options.base_image
- add normal.chef_provisioning.reference.container_id = "92528fce3e3e8c4933d5062770ea2ff39339fd1a5138ddf643f042c0fe7aabb0"
- generate private key (2048 bits)
- create directory /etc/chef on serf
- write file /etc/chef/client.pem on serf
- create client serf at clients
:
- update node serf at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.base_image = {:name=>"ubuntu", :repository=>"ubuntu", :tag=>"14.04"}
- remove normal.chef_provisioning.reference.docker_options.base_image
- write file /etc/chef/client.rb on serf
- write file /tmp/detect.sh on serf
- create new file /home/vagrant/.chef/package_cache/chef_12.4.0-1_amd64.deb
- update content in file /home/vagrant/.chef/package_cache/chef_12.4.0-1_amd64.deb from none to 2d66c2
- (file sizes exceed 10000000 bytes, diff output suppressed)
- upload file /home/vagrant/.chef/package_cache/chef_12.4.0-1_amd64.deb to /tmp/chef_12.4.0-1_amd64.deb on serf
- run 'dpkg -i "/tmp/chef_12.4.0-1_amd64.deb"' on serf
- update node serf at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.base_image = {:name=>"ubuntu", :repository=>"ubuntu", :tag=>"14.04"}
- remove normal.chef_provisioning.reference.docker_options.base_image
- run 'chef-client -l auto' on serf
- create data bag machine_image at chefzero://localhost:8889
- create data bag item serf at chefzero://localhost:8889
- add reference = {"driver_url"=>"docker:unix:///var/run/docker.sock", "driver_version"=>"0.7", "allocated_at"=>1435732754, :docker_options=>{:base_image=>{:name=>"chef_serf", :repository=>"chef", :tag=>"serf"}, :from_image=>true}}
- add run_list = ["recipe[serf::default]"]
- delete node serf at chefzero://localhost:8889
chef-provisioning-docker を利用して、Serf 用の Docker イメージを作成します。この際、Cookbook の適用には Chef-Zero を用いています。
* machine[serf101] action converge
- create node serf101 at chefzero://localhost:8889
- add normal.tags = nil
- add normal.chef_provisioning = {"reference"=>{"driver_url"=>"docker:unix:///var/run/docker.sock", "driver_version"=>"0.7", "allocated_at"=>"2015-07-01 06:39:14 UTC", "host_node"=>"chefzero://localhost:8889/nodes/", "container_name"=>"serf101", "image_id"=>nil, "docker_options"=>{:command=>"/usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json"}}, "from_image"=>"serf"}
- update node serf101 at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.command = "/usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json"
- remove normal.chef_provisioning.reference.docker_options.command
- add normal.chef_provisioning.reference.container_id = "0d30ec1cade296c551f3e10e04dd87155cc8ef0619251970e326c6ab5c319717"
- create client serf101 at clients
:
- update node serf101 at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.command = "/usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json"
- remove normal.chef_provisioning.reference.docker_options.command
- write file /etc/chef/client.rb on serf101
- update node serf101 at chefzero://localhost:8889
- add normal.chef_provisioning.reference.docker_options.command = "/usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json"
- remove normal.chef_provisioning.reference.docker_options.command
- run 'chef-client -l auto' on serf101
引き続き、chef-provisioning-docker を利用して、Serf 用の Docker イメージからコンテナを作成していきます。ここでも Chef-Zero を活用しています。
: : : Running handlers: Running handlers complete Chef Client finished, 6/6 resources updated in 147.028399848 seconds vagrant@vagrant:~/docker$
5つのコンテナを起動し終わりました。なお、ここではコンテナを並列で作成する machine_batch を利用していません。競合が発生するのか、コンテナをうまく作成できないためです。
Docker コンテナの確認
起動した Docker コンテナの状態を確認してみましょう。
vagrant@vagrant:~/docker$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 50cef5723d6b 1d0fdba605f4a6586e71cc6f44a614dcd4c4ea050f2570e5e6ad6113b46831c1:latest "/usr/bin/serf agent 5 minutes ago Up 5 minutes serf105 6a1933e26dd8 5cd3d1fca06db403b5c094404816171657db2b8b05941d41e89f6966813ba3ef:latest "/usr/bin/serf agent 5 minutes ago Up 5 minutes serf104 4937c32cfdb8 c2373783c2391c00dfe2d3781db09e1cc37ef5776d47c0d2714152c27ac09606:latest "/usr/bin/serf agent 5 minutes ago Up 5 minutes serf103 093f23862636 537610a1963c31bbb84b654be8ab2fbcd8aebc286b645bd1a952161b5d5de358:latest "/usr/bin/serf agent 5 minutes ago Up 5 minutes serf102 547ae9fa3ca6 f05b9c1c058c0be2d2f94c7dc11eccf52389994352deeeeb7948ae5ab4515f17:latest "/usr/bin/serf agent 5 minutes ago Up 5 minutes serf101 vagrant@vagrant:~/docker$
vagrant@vagrant:~/docker$ ps auxwwwf | tail -6 root 2234 1.4 0.5 760480 21208 ? Ssl 06:35 0:08 /usr/bin/docker -d --ip-forward=true --log-level=info --tls=true root 4395 0.2 0.1 13732 7564 ? Ssl 06:39 0:00 \_ /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json root 4934 0.2 0.1 13732 6968 ? Ssl 06:39 0:00 \_ /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json root 5463 0.2 0.1 13732 7404 ? Ssl 06:39 0:00 \_ /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json root 6010 0.2 0.1 13732 7164 ? Ssl 06:39 0:00 \_ /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json root 6551 0.2 0.1 13732 6944 ? Ssl 06:39 0:00 \_ /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json vagrant@vagrant:~/docker$
このように、Serf 用コンテナが 5つ起動しています。
コンテナ内で serf members を行う rake タスクを用意してあるので、実行してみます。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/rake members docker exec 50cef5723d6b serf members 6a1933e26dd8 172.17.0.45:7946 alive 093f23862636 172.17.0.29:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 50cef5723d6b 172.17.0.53:7946 alive 4937c32cfdb8 172.17.0.37:7946 alive docker exec 6a1933e26dd8 serf members 6a1933e26dd8 172.17.0.45:7946 alive 093f23862636 172.17.0.29:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 4937c32cfdb8 172.17.0.37:7946 alive 50cef5723d6b 172.17.0.53:7946 alive docker exec 4937c32cfdb8 serf members 4937c32cfdb8 172.17.0.37:7946 alive 093f23862636 172.17.0.29:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive 50cef5723d6b 172.17.0.53:7946 alive docker exec 093f23862636 serf members 4937c32cfdb8 172.17.0.37:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive 50cef5723d6b 172.17.0.53:7946 alive 093f23862636 172.17.0.29:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive docker exec 547ae9fa3ca6 serf members 4937c32cfdb8 172.17.0.37:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive 50cef5723d6b 172.17.0.53:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 093f23862636 172.17.0.29:7946 alive vagrant@vagrant:~/docker$
このように、Serf クラスタが構成されています。
手動で 1つ Serf コンテナを追加してみましょう。
まず、chef-zero を別ターミナルからフォアグラウンドで起動しておきます。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/chef-zero --host 172.17.42.1 >> Starting Chef Zero (v4.2.2)... >> WEBrick (v1.3.1) on Rack (v1.6.1) is listening at http://172.17.42.1:8889 >> Press CTRL+C to stop
別のターミナルで docker run を実行します。
--name=serf191 はコンテナの名前を指定、--detach はバックグラウンドで実行、chef:serf は利用するイメージ、/usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json はコンテナで実行するコマンドです。
vagrant@vagrant:~/docker$ docker run --name=serf191 --detach chef:serf /usr/bin/serf agent -config-file /opt/serf/config/serf_agent.json 1d0d56f27a73e8e73d5eabe7499c04a2a80676b28ec57c1e67930bd3b455c955 vagrant@vagrant:~/docker$
先程のように rake members で Serf クラスタメンバを確認してみます。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/rake members : docker exec 547ae9fa3ca6 serf members 1d0d56f27a73 172.17.0.59:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 093f23862636 172.17.0.29:7946 alive 4937c32cfdb8 172.17.0.37:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive 50cef5723d6b 172.17.0.53:7946 alive vagrant@vagrant:~/docker$
このように今起動したコンテナによってメンバが 1つ増えていることがわかります。
コンテナを停止し、
vagrant@vagrant:~/docker$ docker stop 1d0d56f27a73e8e73d5eabe7499c04a2a80676b28ec57c1e67930bd3b455c955 1d0d56f27a73e8e73d5eabe7499c04a2a80676b28ec57c1e67930bd3b455c955 vagrant@vagrant:~/docker$
クラスタメンバを確認します。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/rake members : docker exec 547ae9fa3ca6 serf members 50cef5723d6b 172.17.0.53:7946 alive 1d0d56f27a73 172.17.0.59:7946 failed 547ae9fa3ca6 172.17.0.21:7946 alive 093f23862636 172.17.0.29:7946 alive 4937c32cfdb8 172.17.0.37:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive vagrant@vagrant:~/docker$
failed と検出されています。
停止したコンテナを再起動し、
vagrant@vagrant:~/docker$ docker start 1d0d56f27a73e8e73d5eabe7499c04a2a80676b28ec57c1e67930bd3b455c955 1d0d56f27a73e8e73d5eabe7499c04a2a80676b28ec57c1e67930bd3b455c955 vagrant@vagrant:~/docker$
クラスタメンバを確認します。
vagrant@vagrant:~/docker$ /opt/chefdk/embedded/bin/rake members : docker exec 547ae9fa3ca6 serf members 1d0d56f27a73 172.17.0.60:7946 alive 547ae9fa3ca6 172.17.0.21:7946 alive 093f23862636 172.17.0.29:7946 alive 4937c32cfdb8 172.17.0.37:7946 alive 6a1933e26dd8 172.17.0.45:7946 alive 50cef5723d6b 172.17.0.53:7946 alive vagrant@vagrant:~/docker$
元通り alive となってクラスタに復帰していることがわかります。
まとめ
本稿では、chef-provisioning-docker を用いて Serf クラスタを作成してみました。Serf ノード 1つにつき 1VM を使う方法に比べて、Docker を用いると非常に軽量かつ高速に Serf クラスタを作成できることがつかんでいただけたと思います。
本実験ではイベントハンドリングを行わない簡単な Serf クラスタ環境の作成のみを行いました。また、Chef-Server ではなく Chef-Zero ですべてを賄っています。イベントハンドラや Chef-Server の利用は今後の課題です。
