ようへいの日々精進XP

よかろうもん

redis-router を試してみる

redis-router を試してみる

概要

redis-router という python で書かれた redis のインターフェースがあるらしいので試す。きっかけは @sawanoboly さんのツイート。

redis-router とは


セットアップ

検証環境

  • Debian 7(Wheezy)
  • localhost にポートで分けた redis server を 2 個起動する

トポロジ

今回は以下のような構成で検証を行う。

f:id:inokara:20130630115106p:plain

※図は blockdiag を使って生成しました。

redis server の準備

redis server を localhost に chef を使ってセットアップする。

sudo chef-solo -c ~/path/to/solo.rb -j ~/path/to/localhost.json

cookbook はこちらの cookbook を使い、JSON ファイルは以下のように設定した。

インストール後、redis の tar ball に含まれていた redis.conf を /usr/local/etc/ 以下にコピーして以下のルールに従い起動するポート番号ごとに設定を行う。また、合わせてデーモンモードで起動する為の設定も合わせて行う。

ホスト ポート番号
localhost 6379
localhost 6380

以下は設定変更箇所。

port 6379

port に続く番号を任意の番号に設定する。また、デーモンモードで起動する為に以下のように設定する。

daemonize yes

それぞれの redis.conf を設定後、以下のようにして redis server を起動する。

sudo /usr/local/bin/redis-server /usr/local/etc/6379.conf
sudo /usr/local/bin/redis-server /usr/local/etc/6380.conf

特にエラーも無く起動した場合には redis-cli -p 6379 及び redis-cli -p 6380 として、それぞれの redis server にアクセス出来ることを確認しておく。

pip のインストール

redis-router は python のモジュールとして提供される為、python モジュールの管理ツールである pip を利用してインストールする。尚、検証環境には pip がインストールされていないため pip のインストールから行う。

sudo apt-get install python-pip

あら、簡単。

redis-router のインストール

READMEを見ると install libketama/ketama_python first. とあるので、以下のようにインストールする。

python_ketama のインストールが終わったら、本命の redis-router のインストール。これは、先ほどインストールした pip にて行う。

pip install redis-router

redis-router を試すにあたって...

検証環境において redis-router のインストール後、README に書かれている内容を試すにあたり以下のパッケージを追加でインストールする必要があった。環境によってはすでにインストールされているかもしれないので適宜読み替えること。

Debian パッケージ。

sudo apt-get install libevent-dev

pip パッケージ。

sudo pip install gevent
sudo pip install greenlet
sudo pip install ketama
sudo pip install flask

telnet でアクセスしてみる

tcp インターフェースサーバーを起動

README の通り、以下のような python コードを書いてみる。

どうやらこれで redis-router の tcp インターフェースを立ち上げることが出来るらしい。

以下のように起動する。

python tcp.py &

アクセスしてみる

アクセスしてみる。

telnet localhost 5000

以下のように telnet を介してキーと値の登録が出来る。

Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
set hoge test1
True
get hoge
test1
^]

登録の状態を redis-cli で確認する。

$ redis-cli -p 6379 get hoge
"test1"
$ redis-cli -p 6380 get hoge
(nil)

6379 ポートで起動している redis server に書き込まれている。

http リクエストでアクセスしてみる

http インターフェースサーバーの起動

telnet と同様に README の通り、以下のような python コードを書いてみる。

また、/etc/redis_router/servers.config を設定する必要があるので、下記のように redis server の IP と Listen しているポート、重み付けを設定する。

# IP address:port weight
127.0.0.1:6379 100
127.0.0.1:6380 100

このファイルのパスは /usr/local/lib/python2.7/dist-packages/redis_router/http_interface.py に直書きされているので、修正することで任意のパスに記載することが可能かと思われる。(以下、抜粋)

以下のように起動する。

python httpd.py &

アクセスしてみる

curl を使って先ほど登録したデータを取得してみる。

curl -X POST --data "command=get&arguments=hoge" http://localhost:5001

POST メソッドで以下のようなレスポンスが返ってくる。

127.0.0.1 - - [2013-06-29 16:04:42] "POST / HTTP/1.1" 200 25 "-" "curl/7.26.0"
{
  "response": "test1"
}

ついでにデータを登録してみる。

curl -X POST --data "command=set&arguments=huga,test2" http://localhost:5001

下記のようなレスポンスが返ってくる。

127.0.0.1 - - [2013-06-29 16:07:44] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}

登録したデータを確認してみる。

curl -X POST --data "command=get&arguments=huga" http://localhost:5001

以下の通り、ちゃんと登録されている。

127.0.0.1 - - [2013-06-29 16:08:52] "POST / HTTP/1.1" 200 25 "-" "curl/7.26.0"
{
  "response": "test2"
}

redis-cli でも確認。

$ redis-cli -p 6379 get huga
(nil)
$ redis-cli -p 6380 get huga
"test2"

今度は 6380 ポートで起動している redis server に登録された。


バランシング

検証環境において redis-router 配下には 2 つの redis server がそれぞれ別ポートで稼働しているところで、重み付けを変えてアクセスしてみる。

同比(1:1)

/etc/redis_router/servers.config を以下のように記載する。

127.0.0.1:6379 100
127.0.0.1:6380 100

リクエストを投げてみる。

$ curl -X POST --data "command=set&arguments=huga,test2" http://localhost:5001 
127.0.0.1 - - [2013-06-29 16:15:38] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}
$ curl -X POST --data "command=set&arguments=hoge,test1" http://localhost:5001
127.0.0.1 - - [2013-06-29 16:15:48] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}

確認してみる。

$redis-cli -p 6379
redis 127.0.0.1:6379> keys *
1) "hoge"
redis 127.0.0.1:6379> quit
$ redis-cli -p 6380
redis 127.0.0.1:6380> keys *
1) "huga"
redis 127.0.0.1:6380> quit

1:2

/etc/redis_router/servers.config を以下のように記載する。

127.0.0.1:6379 100
127.0.0.1:6380 200

リクエストを投げてみる。

$ curl -X POST --data "command=set&arguments=hoge,test1" http://localhost:5001
127.0.0.1 - - [2013-06-29 16:30:31] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}
$ curl -X POST --data "command=set&arguments=huga,test2" http://localhost:5001
127.0.0.1 - - [2013-06-29 16:30:40] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}
$ curl -X POST --data "command=set&arguments=aho,test3" http://localhost:5001
127.0.0.1 - - [2013-06-29 16:30:54] "POST / HTTP/1.1" 200 22 "-" "curl/7.26.0"
{
  "response": true
}

確認してみる。

$ redis-cli -p 6379
redis 127.0.0.1:6379> keys *
1) "aho"
redis 127.0.0.1:6379> quit
$ redis-cli -p 6380
redis 127.0.0.1:6380> keys *
1) "huga"
2) "hoge"

ちゃんと 1:2 の割合で登録されている。


まとめ

という感じで駆け足で redis-router を弄ってみた感想は下記の通り。(追記していく予定)

  • redis server へのアクセスを簡素化してより redis をより使い易い KVS にしてくれてる
  • レスポンスが JSON 形式なので加工とかもしやすそう
  • redis-router を書き込み用のポートと読み込み用のポートを別々で起動することで読み込みの負荷分散を手軽に行えそう

Ansible と serverspec のパラレルテストを試してみる

概要

  • こちら の中で Ansible という python で書かれた構成管理ツールを知った
  • そう言えば、前回の Chef Casual Talks Vol.3 で @urasoko さんも言っていたような...
  • ということで試してみる
  • Ansible についてはこちら

インストール

環境

Ansible をインストールする cookbook

  • あえて chef の cookbook で導入
  • cookbook はこちら

knife ec2 server create

knife ec2 server create -S xxxxxx -i ~/pathto/xxxxxx -r "recipe[ansible_install_cookbook::ansible]" -I ami-39b23d38 --flavor t1.micro --region ap-northeast-1 --availability-zone ap-northeast-1a -G quick-start-8 -x ec2-user -N Ansible -VV

この辺はお約束。

Debian Wheezy には...

python のパッケージ管理ツールはどんなものがあるか知らなかったが pip というツールが良いらしいのでそれをまずインストールする。

sudo apt-get install pip

続いて python のバージョンを確認しつつ、pythonx.x-dev をインストールする。

sudo apt-get install python2.7-dev

そして pip install を実行する。

sudo pip install ansible

はじめの一歩

Ansible について

  • Chef とか puppet と比較してシンプルな構成(を目指して作られている)
  • Chef だと cookbook にあたるものが Ansible だと Playbooks と呼ばれる YAML で記述されたファイルとなる
  • Chef だと resource にあたるものが Ansible だと module と呼ばれるようだ(package とか cookbook_file とかにあたる)

対象ホストを ANSIBLE_HOSTS に追加する

まずは ansible で設定を適用するホストを環境変数ANSIBLE_HOSTS で指定するファイルに設定する。

vim /tmp/ansible_host

以下のように記述する。

[test]
ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com

環境変数ANSIBLE_HOSTS を適用する。

export ANSIBLE_HOSTS=/tmp/ansible_host

対象ホストでコマンドを実行してみる

-m でモジュールを指定、-uリモートホスト(対象ホスト)のユーザー名を指定、--private-key で鍵ファイルを指定して...以下のように実行する。

ansible all -m ping -u ec2-user --private-key=~/pathto/xxxxx.pem

以下のような結果が出力される。

ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com | success >> {
    "changed": false, 
    "ping": "pong"
}

Playbooks を試してみる

Playbooks を以下のように記載する。

vim /tmp/mysql.yml

以下のように ansible-playbook を実行する。

ansible-playbook /tmp/mysql.yml --private-key=~/pathto/xxxxxxx.pem

以下のように出力された。

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com]

TASK: [install mysql-server] ************************************************** 
changed: [ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com]

PLAY RECAP ******************************************************************** 
ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com : ok=2    changed=1    unreachable=0    failed=0

二回目実行すると...

以下のように出力された。

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com]

TASK: [install mysql-server] ************************************************** 
ok: [ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com] => {"changed": false, "msg": "", "rc": 0, "results": ["mysql-server-5.5-1.3.amzn1.noarch providing mysql-server is already installed"]}

TASK: [mysql is running] ****************************************************** 
changed: [ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com] => {"changed": true, "name": "mysqld", "state": "started"}

PLAY RECAP ******************************************************************** 
ec2-xx-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com : ok=3    changed=1    unreachable=0    failed=0 

並列処理

二台のホストに対して同じ Playbooks を適用する。

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [ec2-xx-xxx-xx-xx1.ap-northeast-1.compute.amazonaws.com]
ok: [ec2-xx-xxx-xx-xx2.ap-northeast-1.compute.amazonaws.com]

TASK: [install mysql-server] ************************************************** 
ok: [ec2-xx-xxx-xx-xx1.ap-northeast-1.compute.amazonaws.com] => {"changed": false, "msg": "", "rc": 0, "results": ["mysql-server-5.5-1.3.amzn1.noarch providing mysql-server is already installed"]}
changed: [ec2-xx-xxx-xx-xx2.ap-northeast-1.compute.amazonaws.com] => {"changed": true, "msg": "", "rc": 0, "results": (略)\n\n"]}

TASK: [mysql is running] ****************************************************** 
ok: [ec2-xx-xxx-xx-xx1.ap-northeast-1.compute.amazonaws.com] => {"changed": false, "name": "mysqld", "state": "started"}
changed: [ec2-xx-xxx-xx-xx2.ap-northeast-1.compute.amazonaws.com] => {"changed": true, "name": "mysqld", "state": "started"}

PLAY RECAP ******************************************************************** 
ec2-xx-xxx-xx-xx1.ap-northeast-1.compute.amazonaws.com : ok=3    changed=2    unreachable=0    failed=0   
ec2-xx-xxx-xx-xx2.ap-northeast-1.compute.amazonaws.com : ok=3    changed=0    unreachable=0    failed=0

Chef には出来ない並列処理が標準で備わっている。

ホストへの適用を serverspec でテストする

serverspec-init

serverspec-init

を実行して二台分の spec ファイルを準備する。

spec ファイル

以下のように作成した。

並列実行

serverspec の @gosukenator さんが以下のように serverspec の並列処理についてツイートされていたので、早速、並列処理で serverspec を実行させてみた。

sudo gem install parallel_tests --no-ri --no-rdoc -V

まずは上記のように parallel_test を導入してからの...以下のようにテストを実行する。

parallel_rspec spec
4 processes for 2 specs, ~ 0 specs per process
....

Finished in 0.55082 seconds
3 examples, 0 failures
..

Finished in 0.69609 seconds
3 examples, 0 failures

6 examples, 0 failures

Took 2.2947824 seconds

全てのテストがクリア。Ansible での適用が正常に動作していることが確認出来た。

ちなみに、従来通りにも実行してみる。

rspec spec

結果は以下の通り。

......

Finished in 1.21 seconds
6 examples, 0 failures

対象のホストが少ないので、従来の方法でのテストとパラレルテストではそれほど処理時間に差は見られないが、@gosukenator さんのブログでは 300 程度のテストを実施した場合には処理時間はパラレルテストの方が二倍ほど速くなるようだ。

まとめ

Ansible と Chef を比較して...

  • 並列処理が出来るのは嬉しい
  • Ansible の方がシンプルかな
  • 色々とディレクトリを作る必要がなくて、設定ファイルの設置場所なども選ばないので自由度が高い
  • べき等性もよろしくやってくれる
  • モジュール(リソース)は Chef の方が充実しているものの Ansible は環境に合わせて自作することが出来るようなので汎用性は高い?
  • Ansible はクライアントレス
  • でも Playbooks の書き方がよくわからない...
  • Chef の方が情報が豊富

とは言え...

  • 個人的にはシンプルな Ansible と機能充実の Chef という棲み分けでどっちも追っていきたい
  • 今まで手作業で頑張ってきたことを自動で出来るなら何でもいいやと思っている

MySQL と MariaDB を単純に比較してみた

概要

  • MySQLMySQL を fork して作られた MariaDB の性能に関して、それぞれインストールしたての状態の性能について比較してみた
  • セットアップとテストの実行は chef の cookbook を使う
  • テストはには mysqlslap を使う

環境

まずは cookbook を書く

MySQL のセットアップ

knife cookbook create mysql_install -o ./

mysql_install/recipes/mysql.rb は以下のような感じで書く。

一応、シンタックスの確認はやっておく。

knife cookbook test mysql_install

MariaDB のセットアップ

knife cookbook create mariadb_install -o ./

yum のリポジトリ追加から始めるにあたり cookbook_file リソースを使うので mariadb_install/files/default/mariadb.repo を以下のように用意する。

インストールのレシピ mariadb_install/recipes/mariadb.rb は以下のように用意する。

ベンチマークの cookbook

knife cookbook create db_benchmark -o ./

以下のようなシェルを書いてからの cookbook_file で設置してから execute で叩かせるのでまずは db_benchmark/files/default/bench.sh とかにしておく。

上記のシェルの場合 --auto-generate-sql-load-type=read とあるので読み込み性能のみのベンチマークを取得する。

レシピは db_benchmark/recipes/bench.rb に書く。

実行!

knife cookbook upload する

cookbook を Hosted Chef に登録する。

kappa@x1carbon:~/git/chef-repo$ knife cookbook upload mysql_install -o ./
Uploading mysql_install  [0.1.0]
Uploaded 1 cookbook.
kappa@x1carbon:~/git/chef-repo$ knife cookbook upload mariadb_install -o ./
Uploading mariadb_install [0.1.0]
Uploaded 1 cookbook.
kappa@x1carbon:~/git/chef-repo$ knife cookbook upload db_benchmark -o ./
Uploading db_benchmark   [0.1.0]
Uploaded 1 cookbook.

knife ec2 でインスタンスを作って cookbook を適用(ベンチマークを実行する)

knife ec2 server create -S xxxxxxx \
-i ~/Dropbox/xxxxxxx \
-r "recipe[mysql_install::mysql]","recipe[db_benchmark::bench]" \
-I ami-39b23d38 --flavor t1.micro --region ap-northeast-1 --availability-zone ap-northeast-1a -G quick-start-8 -x ec2-user -N mysql -VV
knife ec2 server create -S xxxxxxx \
-i ~/Dropbox/xxxxxxx \
-r "recipe[mariadb_install::mariadb]","recipe[db_benchmark::bench]" \
-I ami-39b23d38 --flavor t1.micro --region ap-northeast-1 --availability-zone ap-northeast-1a -G quick-start-8 -x ec2-user -N mariadb -VV

error

一応、cookbook の適用まで終わるんだけど、最後に以下のようなエラーが出てしまう...

ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com [2013-06-22T01:47:13+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
DEBUG: received packet nr 144 type 94 len 92
INFO: channel_data: 0 78b
ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com [2013-06-22T01:47:13+00:00] FATAL: Net::HTTPServerException: 403 "Forbidden"

なんでやろ...ということで、こちらは調査!

再度、実行したところ正常に終了した...

ベンチマークの結果

で、結局、ベンチマークはどうなったかと言うと...

MySQL

Server version: 5.5.31 MySQL Community Server (GPL)
Benchmark                                                          
        Running for engine innodb                                  
        Average number of seconds to run all queries: 4.553 seconds
        Minimum number of seconds to run all queries: 4.511 seconds
        Maximum number of seconds to run all queries: 4.608 seconds
        Number of clients running queries: 3                       
        Average number of queries per client: 3333                

MariaDB

Server version: 5.5.31-MariaDB MariaDB Server
Benchmark
        Running for engine innodb
        Average number of seconds to run all queries: 4.686 seconds
        Minimum number of seconds to run all queries: 4.638 seconds
        Maximum number of seconds to run all queries: 4.732 seconds
        Number of clients running queries: 3
        Average number of queries per client: 3333

ほんの少しだけ MariaDB 遅いのかなと。誤差の範囲かもしれないし、単純に read の性能しか計測していないので一概に優劣はつけられない。

まとめ

  • cookbook 力が足りない...
  • さくっと検証するには ec2 はかなり便利!
  • でも knife ec2 の謎のエラーはなんだろ...
  • 引き続き MySQLMariaDB の比較もやっていきたい

参考

recipe の書き方復習

はじめに

  • 業務でも cookbook を書く機会が減ってしまった
  • 目指せ、脱初心者

not_if と only_if の使い方

execute リソースでよく使う not_ifonly_if について復習してみる。

not_if 従来

execute "create file hoge" do
  command "touch /tmp/hoge"
  not_if (ls /tmp/hoge)
end

一応、これでも動く。

not_if こう書いた方がカッコイイと思う(自分比)

execute "create file hoge" do
  command "touch /tmp/hoge"
  not_if {File.exists?("/tmp/hoge")}
end

上記のように書いた方がなんとなくカッコイイ。

only_if

only_if はあまり使ったことはないけど、以下のような感じかな。

template "/tmp/hoge" do
  mode 00644
  source "hoge.erb"
  only_if "test -f /tmp/ahoaho"
end

/tmp/ahoaho が存在していたら template リソースを使って /tmp/hoge を作成する。

そして not_if と only_if を整理

condition 意味
not_if 否定(〜が〜でなければ)
only_if 肯定(〜が〜であれば)

nothing からの notifies

パッケージをインストール後、設定ファイルを設置したい場合....

package "httpd" do
  action :install
end

template "/etc/httpd/conf.d/common.conf" do
  source "httpd_common.conf.erb"
  mode 00644
  owner "root"
  group "root"
end

個人的には悪くはないと思うけど、上記のように書くと毎回実行されてしまうので nothingnotifies を使う。

template "/etc/httpd/conf.d/common.conf" do
  source "httpd_common.conf.erb"
  mode 00644
  owner "root"
  group "root"
  action :nothing
end

package "httpd" do
  action :install
  notifies :create, resources( :template => "/etc/httpd/conf.d/common.conf" )
end

参考

続く...

knife-xapi を試した(1)

概要

  • XenServer を操作する knife プラグインとして公開されている knife-xapi を試してみる
  • knife-xenserver との比較もちょっとしてみる

試す

インストールは gem install で...。

インストールしても直ぐに使えない件

sudo gem install knife-xapi --no-ri --no-rdoc -V

gem install でのインストールは問題無かったが knife xapi を叩くと以下のようなエラーが出てしまう。

確認してみたところ...

上記のように一部のファイルのパーミッションがオーナーとグループ以外が読めない状態になっている。別の OS でも類似の現象が発生するかを確認してから開発者に連絡してみたい。

とりあえずは、オーナー以外のユーザーでも確認出来るように権限を追加する。

sudo chmod 644 *

ちなみに knife-xapi-0.5.3/lib/chef/knife/ 以下のファイルや knife-xapi-0.5.3/lib/xenapi/ 以下、knife-xapi-0.5.3/lib/xenapi/xenapi/ 以下も同様の状態になっているので権限を付与する。

.chef/knife.rb

以下のように設定する。knife xenserver と異なるのは Xenserver のホストを設定する際に単純に IP アドレスではなく http:// から設定すること。

knife[:xapi_host] = "http://xxx.xxx.xxx.xxx/"
knife[:xapi_username] = "user"
knife[:xapi_password] = "password"

仮想マシンのリストを取得

knife xapi guest list

以下のように仮想マシンの一覧を取得出来る。

Name Label                 State        IP Address  
redis2                     Halted       xxx.xxx.xxx.xxx

knife-xenserver と比較するとちょいと寂しい出力結果。その他にも下記のようなサブコマンドが利用可能。

** XAPI COMMANDS **
knife xapi vdi create NAME (options)
knife xapi vlan list
knife xapi guest start
knife xapi guest stop
knife xapi guest list
knife xapi vdi attach VM_name VDI_name (options)
knife xapi guest delete NAME_LABEL (options)
knife xapi vdi list
knife xapi net list
knife xapi guest create NAME [NETWORKS] (options)
knife xapi vdi delete NAME_LABEL (options)
knife xapi vdi detach NAME_LABEL (options)

knife xenserver との比較

XenServer への接続

XenServer へのアクセス方法が以下の通り knife-xapiknife-xenserver では異なるようだ。

  • knife-xapixenapi を利用している
  • knife-xenserverfog を利用している

chef との連係

chef に関連すると思われる仮想マシンを作成するサブコマンドのオプションで比較してみる。

knife-xapi の場合

knife-xenserver の場合

knife-xapi-j JSON_ATTRIBS が利用出来る程度の違いなので基本的には同じことはできそうだ...ま、同じ knife プラグインだから当たり前って言ったらそうかなw

その他

  • なぜか knife-xapi ではテンプレートの一覧が取得出来ない...

まとめ

  • knife プラグインの一覧をまとめたここを見てると楽しい
  • さらっと触っただけだがテンプレートの一覧取得が出来ないとか一部、knife-xensever と異なる点が見受けられた

次回

  • インストール直後に利用出来なかった問題について調査
  • chef server を利用して VM を構築してみる

xenserver-automater と knife-xenserver と chef-zero で提供する仮想マシン構築自動化(2)

概要

  • 前回の補足
  • xensever-automator について(ハマった点)

前回の補足

実装

実装の内容を図面で起こしてみた。

f:id:inokara:20130605001050p:plain

上記の通り、XenServer を中心として Dom0XenStore から IP アドレス等の情報を取得してサーバーに設定した上で chef server との通信の後で設定が開始する実装。

稼働メッセージ

XenStore を利用することで knife xenserver create vm の起動メッセージに変化があった。

Connecting to XenServer host ${XenServer IP}... 
Creating VM hogehoge... 
Using template ${your_template} [uuid: ${template_uuid}]...
Adding attributes to xenstore... # ← ここが追加になった
VM Name: xxxxxxx
VM Memory: 512 MB 

Waiting server... 
Trying to SSH to ${VM IP}...

xensever-automator について(ハマってる点)

xensever-automator を検証していてところハマってしまった点を紹介。

xe-automater.conf

xe-automater.conf/etc/init/ 以下に設置しなければいけないようだが、対象となる環境(Debian)には初期状態では /etc/init ディレクトリがそもそも無かった...

そもそも /etc/init/ とは /etc/init.d と対を成すものらしく、利用するには upstart コマンドが必要となり現状の環境には適用出来ない。ということで、無理くり /etc/init.d/ 以下に置くようなコマンドを自作してみた。

#!/bin/sh                                             
         
### BEGIN INIT INFO
# Provides: xenserver-automater
# Default-Start: 2 3 4 5                            
# Default-Stop: 0 1 6
# Required-Start:
# Required-Stop:
# Description: XenServer Guest Configuration Automator
### END INIT INFO
                                                      
NAME=xenserver-automater
USER=root
DESC="XenServer Guest Configuration Automator"

case "$1" in
        start )
        echo -n "Starting $DESC: "                    
        /usr/sbin/xe-set-network
        /usr/sbin/xe-set-hostname                     
  ;;
esac

上記のシェルを作成し /etc/init.d 以下に xe-automater というファイル名で設置する。設置後、sudo update-rc.d xe-automator defaults にて起動スクリプトとして登録。

この状態で再起動時 xe-automater が実行されて IP アドレスが仮想ホストに設定されるという算段でいたが IP アドレスが正常に設定されない事象が発生している。

原因として考えられるのが...

  • xe-automater が実行されるより前に Xen 関連のサービスが起動する必要がある?
  • そもそもスクリプトがヘタレである

xe-automater を手動で cd /etc/init.d && sudo ./xe-automater start として実行すると正常に IP が設定されることからスクリプトがヘタレである点と合わせて OS 起動時に Xen 関連のサービスが起動している必要がありそうだ。

まとめ

  • Chef の話というよりも XenServer の話になってきているが引き続き調査していく
  • そもそも init スクリプトの書き方を知らなかった自分が恥ずかしい...

xenserver-automater と knife-xenserver と chef-zero で提供する仮想マシン構築自動化(1)

概要

目標

以下の二点を実現する環境を構築する。

  • knife-xenserver と xenserver-automater を使って IP アドレスを指定した仮想マシンをコマンド一発で構築する
  • 仮想マシンが起動した後、chef client を実行して初期構築を実行する

将来的には

  • 構築が完了した仮想マシンに対して serverspec でテストを実行する

この一連の流れをコマンド一発で行えるようにする。今回は最初の二点について実装してみる。

xenserver-automater とは

  • github ではこちら
  • xenstore に登録された IP アドレス等の設定パラメータを xenstore-read を利用して DomU から取得して DomU 自体に設定するツール
  • DomU とはここでは仮想マシンのこと

準備

登場人物

登場人物がちょっと多いので一覧で整理してみる。

コンポーネント プロダクト 情報
ハイパーバイザー XenServer http://www.citrix.co.jp/
chef server chef-zero https://github.com/jkeiser/chef-zero
chef client の操作、XenServer 操作 knife-xenserver https://github.com/bvox/knife-xenserver
IP アドレスの設定 xenserver-automater https://github.com/krobertson/xenserver-automater
仮想ホスト Debian 6.0.7(Squeeze) http://www.debian.or.jp/

chef server としては chef-zero を利用する。毎回の事ながら手軽に扱える chef-zero は本当に有難い。

事前にやっておくべきこと

以下は既に出来るという前提で進める。

  • chef-zero が利用出来ること(構築に関してはこちら
  • knife xenserver が利用出来ること(設定に関してはこちら

OS のテンプレート作成

テンプレートの対象となる仮想ホストにて xenserver-automater を git clone する。

cd /tmp/
git clone https://github.com/krobertson/xenserver-automater.git

xenserver-automater を README.md に従い所定の位置に配置、権限の設定を行う。

そのままでは動作しなかったので /etc/init/ 以下にコピーした xe-automator.conf のファイル名と権限及び内容を修正する。

mv /etc/init/xe-automator.conf /etc/init.d/xe-automator
chmod 755 /etc/init.d/xe-automator

ファイル名を変更した /etc/init.d/xe-automator を修正する。

vim /etc/init.d/xe-automator

仮想ホストの起動時に /usr/sbin/xe-set-network/usr/sbin/xe-set-hostname が実行されるようにする。

#!/bin/bash

echo "XenServer Guest Configuration Automator"

/usr/sbin/xe-set-network
/usr/sbin/xe-set-hostname

exit 0

※上記方法ではなく README.md 通りに行ってもいけるかもしれないので引き続き、検証を行う。

xenserver-automater の設定が終わった後は仮想ホストを以下のコマンドで仮想マシンを手軽にテンプレート化する。

xe vm-param-set uuid=${仮想ホスト UUID} is-a-template=true

cookbook を chef server にアップロードする

おなじみの knife cookbook upload にて chef-zero の chef server に cookbook をアップロードする。

knife cookbook upload setup -o ./

アップロードした cookbook を念の為確認してみる。

knife cookbook list
hoge_cookbook      0.1.2  0.1.1
redis_2_cookbook   0.2.0
setup              0.1.0

構築してみる

knife-xenserver を実行

knife-xensever の実行に必要にパラメータを整理してみる。

オプション パラメータ 内容
--vm-template ${仮想マシンテンプレート UUID} 仮想マシンテンプレートの元になるテンプレートの UUID を指定する
-x root 仮想マシンをセットアップする際にログインするユーザー名を指定する(通常は root を指定)
-P ${パスワード} -x で指定したユーザーのパスワードをし知恵する
--vm-name my-hostname 仮想マシンのホスト名を指定する
--vm-ip 192.168.1.10 構築する仮想マシンの IP を指定する
--vm-netmask 255.255.255.0 構築する仮想マシンの netmask を指定する
--vm-gateway 192.168.1.254 構築する仮想マシンの netmask を指定する
--vm-dns 192.168.1.254 構築する仮想マシンが利用する DNS サーバーを指定する
--vm-domain inokara.com 構築する仮想マシンのホスト名を指定する
-r "recipe[setup]" 仮想マシン構築後に適用する cookbook を指定する

その他にも -d chef-full 等も場合によっては指定する必要がある。

knife xenserver create vm --vm-template ${仮想マシンテンプレート UUID} -x root -p ${パスワード}
                            --vm-name my-hostname \
                            --vm-ip 192.168.1.10 --vm-netmask 255.255.255.0 --vm-gateway 192.168.1.254 --vm-dns 192.168.1.254 \
                            --vm-domain inokara.com -r "recipe[setup]"

実行後、テンプレートから仮想マシンが構築され、その後 chef server から cookbook を取得して cookbook に基づいて仮想マシンの構築が行われる。

xenserver-automater のやったこと

github のソースコードを見ると解るが xenstore-read という xenstore に登録された、上記の IP アドレス等を取得して /etc/network/interface/etc/hosts /etc/hostname を生成している。

/etc/network/interface

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
        address 192.168.1.10
        netmask 255.255.255.0
        gateway 192.168.1.254
        dns-nameservers  192.168.1.254
        dns-search inokara.com

/etc/hosts

127.0.0.1 localhost
192.168.1.10 my-hostname.inokara.com my-hostname

/etc/hostname

my-hostname.inokara.com

上記を設定した後で /etc/init.d/networking restart を実行して IP アドレス等がアクティブになっている。


まとめ

  • knife-xenserver と chef-zero を使うことで IP アドレスの設定以外を自動化することが出来ていたが、今回の xenserver-automaterを使うことで IP アドレスの設定までも自動化することが出来た
  • 今後、実際の運用で利用する具体的な手順等についても考えてみたい
  • xenserver-automater を実現している xenstore については、そもそも何なのかが理解不足...
  • いまさら XenServer と言わず、引き続き、色々と弄ってみたい

cookbook の書き方諸々〜 redis をソースコードからインストールしたホストを serverspec でテストする 〜

概要

  • 前回セットアップしたホストを serverspec でテストする

参考

今回は下記を参考にさせて頂きました。

serverspec についての認識

  • こちらに書かれているようにサーバー上で稼働しているサービスや開いているポート等をテストするツール
  • 対象となるホストに対して SSH でアクセスするので物理サーバーでも仮想サーバーでもテストが可能

serverspec でテストする

serverspec をインストール

gem でインストールする。

sudo gem install serverspec --no-ri --no-rdoc

SSH 関連の設定

SSH で対象のホストにアクセスする場合、root ユーザーでパスワード入力なしでログインするか sudo でルートになるユーザーでのパスワード入力なしでのログインが必要なことから運用する現場で設定の方法を検討する必要があると思われる。ちなみに、検討中の運用としては...

  • 仮想サーバーの場合には仮想サーバーのテンプレートに予め serverspec を実行するユーザーのアカウントを作成し鍵の設定をしておく
  • 物理サーバーの場合にはサーバー設定の際に serverspec を実行するユーザーのアカウントを作成し鍵の設定を行う

今回は serverspec を実行するユーザーは hoge を使う為 hoge ユーザーで対象ホストにアクセスすることになる。ということで...

対象ホストに hoge ユーザーを作成し、パスワードなしでログイン出来るようにしておく。また、sudo が利用出来るように hoge ユーザーは wheel グループに所属させておき、/etc/sudoers は下記のように設定しておく。

%wheel  ALL=(ALL)       ALL

テストする項目をピックアップ

今回テストする項目としては...(多少冗長かもしれないけど...)

  • redis-server がインストールされている(ファイルとして存在しつつ、実行権限がある)
  • redis-cli がインストールされている(ファイルとして存在しつつ、実行権限がある)
  • redis-server サービス(プロセス)が起動している
  • redis-server の Listen ポート(6379)がオープンになっている

この位はテストしたい。

こちらでテスト出来るリソースを確認することが出来る。

ピックアップした項目を元にしてテストを書く

以下のように書いてみた。

require 'spec_helper'                           
                                                
describe file('/usr/local/bin/redis-server') do 
  it { should be_file }                         
  it { should be_executable }                   
end                                             
                                                
describe file('/usr/local/bin/redis-cli') do    
  it { should be_file }                         
  it { should be_executable }                   
end                                             
                                                
describe service('redis-server') do             
  it { should be_running   }                    
end                                             
                                                
describe port(6379) do                          
  it { should be_listening }                    
end                                             

テストしてみる

sudo が利用出来るユーザーでのテストを行う場合には下記のように実行するとのこと

cd ${serverspec}
ASK_SUDO_PASSWORD=1 rake spec

以下のようにテストが終了する。

/usr/local/rbenv/versions/1.9.3-p392/bin/ruby -S rspec spec/xxx.xx.xx.xx/redis_spec.rb
Enter sudo password:                                                                 
......                                                                               
                                                                                     
Finished in 1.13 seconds                                                             
6 examples, 0 failures                                                               

ちなみにホストの radis-server を停止した状態でテストすると...

/usr/local/rbenv/versions/1.9.3-p392/bin/ruby -S rspec spec/xxx.xx.xx.xx/redis_spec.rb
Enter sudo password:                                                                  
....FF                                                                                
                                                                                      
Failures:                                                                             
                                                                                      
  1) Service "redis-server"                                                           
     Failure/Error: it { should be_running   }                                        
     # ./spec/xxx.xx.xx.xx/redis_spec.rb:14:in `block (2 levels) in <top (required)>' 
                                                                                      
  2) Port "6379"                                                                      
     Failure/Error: it { should be_listening }                                        
     # ./spec/xxx.xx.xx.xx/redis_spec.rb:18:in `block (2 levels) in <top (required)>' 
                                                                                      
Finished in 2.64 seconds                                                              
6 examples, 2 failures                                                                
                                                                                      
Failed examples:                                                                      
                                                                                      
rspec ./spec/xxx.xx.xx.xx/redis_spec.rb:14 # Service "redis-server"                   
rspec ./spec/xxx.xx.xx.xx/redis_spec.rb:18 # Port "6379"                              
rake aborted!                                                                         
/usr/local/rbenv/versions/1.9.3-p392/bin/ruby -S rspec spec/xxx.xx.xx.xx/redis_spec.rb
 failed                                                                               
                                                                                      
Tasks: TOP => spec                                                                    
(See full trace by running task with --trace)                                         

上記のように見事にエラーとなる。

まとめ

  • serverspec を使うことでホストのあるべき状態をテストすることが出来た
  • cookbook のテストというよりもサーバー設定そのものをテストするツールとして幅広く運用を考えていきたい!

cookbook の書き方諸々〜 redis をソースコードからインストールしてみる(1) 〜

概要

  • Chef 推しなのにそもそも cookbook をまともに書けてない自分を戒めたい
  • テーマとして redis サーバーをセットアップする cookbook を書く

参考

検証にあたっては @sawanoboly さんが書かれた、下記を参考にさせて頂きました。

ちなみに redis サーバーについて

普通の cookbook

まずはこう書く

redis_1_cookbook/site-cookbook/recipes/resdis_install.rb

# Debian 系と redhat 系の分岐が必要だけど...
package "tcl8.5" do
  action :install
end

# 作業用ディレクトリの作成
directory "/usr/local/src/redis" do
  action :create
  not_if "ls -d /usr/local/src/redis"
end

# 最新のソースコードを取得
remote_file "/usr/local/src/redis/redis-2.6.13.tar.gz" do
  source "http://redis.googlecode.com/files/redis-2.6.13.tar.gz"
  not_if "ls /usr/local/bin/redis-server"
end

# ソースコードのアーカイブを展開して make && make test && make install
bash "install_redis_program" do
  user "root"
  cwd "/usr/local/src/redis/"
  code <<-EOH
    tar -zxf redis-2.6.13.tar.gz
    (cd redis-2.6.13/ && make test && make && make install)
  EOH
  not_if "ls /usr/local/bin/redis-server"
end

# サービスを起動する
bash "start_redis_server" do
  user "root"
  cwd "/usr/local/bin"
  code <<-EOH
    /usr/local/bin/redis-server &
  EOH
  not_if "ps aux | grep \[r\]edis-server"
end

自己評価で 30 点。

一部の情報を attribute で定義してみる

最初の recipe の問題点

最初に掲げた recipe の問題点として考えられるのが、以下のような情報を recipe にベタ書きしてしまっている点。

等、流動的な情報(バージョン番号)や環境によって差異が生まれやすい情報を recipe に直接記載することで cookbook の汎用性をものの見事に損ねていると思われる。

では、上記の情報を attribute を使って外出しにしてみる。

attribute

redis_2_cookbook/attributes/default.rb

# 作業用ディレクトリ
default['redis']['work_dir'] = '/usr/local/src/redis/'

# ソースコードの URL
default['redis']['source_ver_num'] = '2.6.13'
default['redis']['source_url_path'] = 'http://redis.googlecode.com/files/'
default['redis']['source_file_name'] = 'redis-#{default['redis']['source_ver_num']}.tar.gz'

# redis-server のインストールパス
default['redis']['server_install_path'] = '/usr/local/bin/redis-server'

こんな感じに流動的な情報については attribute に書いてみた。

recipe

上記の attribute を受けての recipe は下記のようにに書き換えた。

redis_2_cookbook/recipes/redis_install.rb

# Debian 系と redhat 系の分岐が必要だけど...
#package "tcl8.5" do
#  action :install
#end

# 作業用ディレクトリの作成
directory node['redis']['work_dir'] do
  action :create
  not_if "ls -d #{node['redis']['work_dir']}"
end

# 最新のソースコードを取得
remote_file node['redis']['work_dir'] + node['redis']['source_file_name'] do
  source node['redis']['source_url_path'] + node['redis']['source_file_name']
  not_if "ls #{node['redis']['server_install_path']}"
end

# ソースコードのアーカイブを展開して make && make test && make install
bash "install_redis_program" do
  user "root"
  cwd node['redis']['work_dir']
  code <<-EOH
    tar -zxf #{node['redis']['source_file_name']}
    cd #{::File.basename(node['redis']['source_file_name'], '.tar.gz')}
    make
    make install
  EOH
  not_if "ls #{node['redis']['server_install_path']}"
end

# サービスを起動する
bash "start_redis_server" do
  user "root"
  cwd "/usr/local/bin"
  code <<-EOH
    #{node['redis']['server_install_path']} &
  EOH
  not_if "ps aux | grep \[r\]edis-server"
end

node に適用してみる

テスト

knife cookbook test してみる。

knife cookbook test redis_2_cookbook -o ./
checking redis_2_cookbook
Running syntax check on redis_2_cookbook
Validating ruby files
Validating templates

問題無さそう。

foodcritic してみる。

foodcritic redis_2_cookbook
FC008: Generated cookbook metadata needs updating: redis_2_cookbook/metadata.rb:2
FC008: Generated cookbook metadata needs updating: redis_2_cookbook/metadata.rb:3

こちらはとりあえず無視。

Chef Server にアップロード

Chef Server は chef-zero を使う。手軽に使えるのがマジ良い。

knife cookbook upload redis_2_cookbook -o ./

適用

knife xenserver を使って node に適用。

knife xenserver vm create --vm-template 886cd33d-380d-ac9a-83ab-ba0a00c4b7e1 --vm-name redis2 --vm-networks 'Pool-wide network associated with eth0' -r "recipe[setup
]","recipe[redis_2_cookbook::redis_install]" -d chef-full

setup という cookbook も一緒に適用。この cookbook は makegcc をインストールする cookbook 。

確認

node にアクセスしてプロセスを確認。

# ps aux | grep \[r\]edis-server
root      3072  0.0  0.4  40456  2120 ?        Sl   09:27   0:02 /usr/local/bin/redis-server

ついでに redis-server にアクセスする。

# redis-cli
redis 127.0.0.1:6379> help
redis-cli 2.6.13
Type: "help @<group>" to get a list of commands in <group>
      "help <command>" for help on <command>
      "help <tab>" to get a list of possible help topics
      "quit" to exit
redis 127.0.0.1:6379> 

まとめ

  • 少なくとも attribute を使ったほうが良さそう...
  • 次は LWRP を試してみる

github

.kitchen Deep Talks の Ust 録画を見ながら、ビール飲みながら...

概要

先日, CREATIONLINE の @urasoko さんが以前から告知されていた .kitchen Deep Talks を Ust の録画(録画有難うございました!)をビール片手に拝見したので感じた事など。

参加されたのはクラスメソッドの @sato_shi さん、JAWS-UG横浜の @yoshidashingo さん、そして @ryuzee さん。

Ust


Video streaming by Ustream

感じたことやメモ

既存の業務フロー

  • 便利になると判っていても、楽になると判っていても変化を受け入れられないのではないか?

クラウド創世記

インフラをソーシャルコーディング

今回、「ひゃあ」って思ったのは @takipone さんのこのツイート。Chef や puppet のはじめとする Provisioning Framework の普及によってインフラ自体をプログラミングすることの敷居が下がり、 github のようなソーシャルプログラミングによってソーシャルな環境でインフラをプログラミングする環境が当たり前になりつつある。秘伝のシェルスクリプトや口伝は無くしていきましょうよってことなのかな。

ブログやソーシャルメディア

  • 文化づくり、ブランディング、広告宣伝
  • ただし、楽しんでやる
  • 社内で Blog を書くことが認められれている
  • 文化の形成無しに Blog を書く文化は育たないのでは...
  • クラスメソッドさんはポイント制(はてぶ、ツイッター等)
  • 但し、金銭的な対価は続かない(止める理由となる)
  • いい意味で競争心をあおられる
  • コンテンツの質が高まる
  • 自分のメモとしてのエントリと PV を必要とするエントリ
  • レポートなのか、コンテンツなのか?
  • レポートであれば速報性があれば PV がある程度伸びる
  • ツイッターで過去の記事をつぶやく等、自分の時間を使っての実験
  • 選択と集中、ニッチでも構わない

Chef

印象的だったツイートをピックアップ。

よりレシピとはなんぞや?という話に成りかねませんが、個人的には Ruby の勉強は必須と考えていました。でも、お二方のツイートにもあるように Ruby がハードルを上げるってことは無さそう。Chef から Ruby を学ぶって考え方も出来るかな。

似たようなシチュエーションとしては会社に Chef を導入したものの...と結局、属人的なシステムになってしまわなないようにしたいなと思ったり。

ビールとピザ

まとめ

録画を拝見させて頂いたが、Chef 話に留まらずインフラのソーシャルコーディング、クラウド創世記、ブログの PV を上げる方法(笑)、アジャイル等、多岐に渡りつつ、その業界を牽引する方々の濃い話が短い時間の中にがっちり詰まっていた感があった。

インフラをコーディングという考え方には以前から共感を持ち、Chef や puppet を弄りブログで書いたりしているが、さらにそれを推し進めてインフラをソーシャルコーディングという新しいインフラの在り方を感じることが出来た。

まだまだ書けそうな気がするので、引き続き、録画を見ながら書いていければと思います...

最後になりますが、参加された各位、司会の @urasoko さんお疲れ様でした。また、録画を提供して頂きまして有難うございましたー。次回も楽しみにしています〜