2014年1月31日金曜日

Chefってなんじゃ?

「男もすなるChefといふものを、女もしてみむとてするなり。」
 −紀貫之

ということで、いまさらすぎますがChefです。

とっても有名な入門書「入門Chef Solo - Infrastructure as Code」にだいたいのことが書いてあります。

Chefはサーバーの設定をしたりソフトウェアをインストールしたりする管理ツールとのことで、rubyのDSLでレシピを書いて実行するとサーバーの状態が変更されるので、セットアップが自動化できるとのことです。

他にChef Serverというのもあるようですが、ここでは割愛してChef Soloだけで進めます。
まずはインストールです。
前回インストールしたvirtual box内のローカルにchef soloをインストールしてみます。


rbenvのインストール


今回はrbenvからrubyを入れるためrbenvからインストールします。
rubyを直接インストールする場合は、これは必要ありません。
また、全ユーザで参照できるように、/etc/profileにrbenvのパスを設定しておきます。
$ sudo yum install -y openssl-devel git vim tree
$ cd /usr/local/
$ sudo git clone git://github.com/sstephenson/rbenv.git rbenv
$ sudo groupadd staff
$ sudo chgrp -R staff rbenv
$ sudo chmod -R g+rwxXs rbenv
$ sudo mkdir /usr/local/rbenv/plugins
$ cd /usr/local/rbenv/plugins
$ sudo git clone git://github.com/sstephenson/ruby-build.git
$ sudo chgrp -R staff ruby-build
$ sudo chmod -R g+rwxs ruby-build
$ sudo su -
# vim /etc/profile.d/rbenv.sh
export RBENV_ROOT=/usr/local/rbenv
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"
# source /etc/profile



rubyのインストール


rubyをインストールします。
sudoでchefが呼び出せるように、/etc/sudoersにパスの設定をしておきます。
# rbenv install --list
# rbenv install -v 2.0.0-p353
# rbenv global 2.0.0-p353
# ruby -v
ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux]
# exit
$ ruby -v
ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux]
$ gem -v
2.0.14
$ sudo visudo -f /etc/sudoers.d/rbenv
Defaults !secure_path
Defaults env_keep += "PATH RBENV_ROOT"


chefとknife soloのインストール


gemでインストールします。
$ sudo gem install chef
$ sudo gem install knife-solo
$ sudo rbenv rehash



chefリポジトリの作成


knife configureコマンドでknife設定を初期化します。これは初回だけでOKです。
そのあとknife solo initで初期ディレクトリ群を作成し、ついでにgit管理しておきます。
$ knife configure
$ knife solo init myfirstkitchen
$ tree myfirstkitchen/
myfirstkitchen/
|-- cookbooks
|-- data_bags
|-- environments
|-- nodes
|-- roles
`-- site-cookbooks

6 directories, 0 files
$ cd myfirstkitchen/
$ git init
$ git add .
$ git commit -m 'first commit'



クックブックの作成


knife cookbook createで新規にクックブック(レシピ集)を作成します。
自分のクックブックはsite-cookbooks内に置くのが習わしのようです。
$ knife cookbook create myfirstcookbook -o site-cookbooks
** Creating cookbook myfirstcookbook
** Creating README for cookbook: myfirstcookbook
** Creating CHANGELOG for cookbook: myfirstcookbook
** Creating metadata for cookbook: myfirstcookbook
$ tree .
.
|-- cookbooks
|-- data_bags
|-- environments
|-- nodes
|-- roles
`-- site-cookbooks
    `-- myfirstcookbook
        |-- CHANGELOG.md
        |-- README.md
        |-- attributes
        |-- definitions
        |-- files
        |   `-- default
        |-- libraries
        |-- metadata.rb
        |-- providers
        |-- recipes
        |   `-- default.rb
        |-- resources
        `-- templates
            `-- default



レシピの作成


まずは設定。chef-soloの実行時に、クックブックの場所がわかるように以下の内容をファイルに記述します。
クックブックの場所はcookbooksとsite-cookbooksを指定します。
$ cat ./solo.rb
file_cache_path "/tmp/chef-solo"
cookbook_path ["/home/vagrant/myfirstkitchen/cookbooks", "/home/vagrant/myfirstkitchen/site-cookbooks"]

次に実行時にどのクックブックのレシピを実行するかをjsonファイルで指定できます。
$ cat localhost.json
{
  "run_list": [
    "recipe[myfirstcookbook]"
  ]
}

やっとレシピの作成に入ります。空のdefaultレシピが配置されているので、ここに記述します。
httpdとphpをインストールし、サービス起動するように設定します。
$vim site-cookbooks/myfirstcookbook/recipes/default.rb
# Cookbook Name:: myfirstcookbook
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#

# install httpd & php
%w{httpd php}.each do |pkg|
  package pkg do
    action :install
  end
end

# make /etc/init.d/httd and start httpd
service "httpd" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end



実行


それでは実行です。実行時に、先ほど作ったsolo.rbとlocalhost.jsonを指定します。
$ sudo chef-solo -c solo.rb -j localhost.json
Starting Chef Client, version 11.8.2
Compiling Cookbooks...
Converging 3 resources
Recipe: myfirstcookbook::default
  * package[httpd] action install
    - install version 2.2.15-29.el6.centos of package httpd

  * package[php] action install
    - install version 5.3.3-27.el6_5 of package php

  * service[httpd] action enable
    - enable service service[httpd]

  * service[httpd] action start
    - start service service[httpd]

Chef Client finished, 4 resources updated

実行が正常に終わったようです。



確認


/etc/httpdにインストールされています。
$ ls -l /etc/httpd/
total 8
drwxr-xr-x 2 root root 4096 Jan 31 06:08 conf
drwxr-xr-x 2 root root 4096 Jan 31 06:09 conf.d
lrwxrwxrwx 1 root root   19 Jan 31 06:08 logs -> ../../var/log/httpd
lrwxrwxrwx 1 root root   29 Jan 31 06:08 modules -> ../../usr/lib64/httpd/modules
lrwxrwxrwx 1 root root   19 Jan 31 06:08 run -> ../../var/run/httpd


/etc/init.d/httpdがつくられています。
$ ls -l /etc/init.d/httpd
-rwxr-xr-x 1 root root 3371 Aug 13 17:30 /etc/init.d/httpd

起動しています!
$ ps -ax | grep httpd
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
25266 ?        Ss     0:00 /usr/sbin/httpd
25268 ?        S      0:00 /usr/sbin/httpd
25269 ?        S      0:00 /usr/sbin/httpd
25270 ?        S      0:00 /usr/sbin/httpd
25271 ?        S      0:00 /usr/sbin/httpd
25272 ?        S      0:00 /usr/sbin/httpd
25273 ?        S      0:00 /usr/sbin/httpd
25274 ?        S      0:00 /usr/sbin/httpd
25275 ?        S      0:00 /usr/sbin/httpd
25356 pts/0    S+     0:00 grep httpd

どうやらyumでインストールされたようです。
$ sudo yum list installed | grep httpd
Failed to set locale, defaulting to C
httpd.x86_64          2.2.15-29.el6.centos
httpd-tools.x86_64    2.2.15-29.el6.centos


PHPのindexをつくってみます。
# vim /var/www/html/index.php
<?php phpinfo();?>



ブラウザで確認すると、、、


おお、ちゃんとphpも動いてます!



保存


ここまでできたら、再度コミットしておきます。
$ git add .
$ git commit -am "add httpd&php"



このように、レシピを追加して、その状態をgit管理することでサーバーの状態をバージョン管理することができて便利なのだそうです。
今後もっとchefに触れていきたいと思います。以上です。

2014年1月29日水曜日

Vagrantってなんじゃ?

VagrantはVirtual Boxという仮想環境のコマンドインターフェースのようなものです。
VirtualBoxをつかえば、手元に簡単にテスト環境を用意できます。


VirtualBoxのインストール


VirtualBoxをダウンロードしてインストールします。この時点のvagrantの最新がVirtualBox 4.2までの対応だったようなので、ここでは4.2をインストールします。(Vagrantをgemではなく、パッケージインストーラの最新版から入れたらVirtualBoxの最新版に対応していました。)



インストーラなので、そのままインストールします。



Vagrant のインストール


インストールが完了したら次はvagrant のインストールです。
vagrantは、最近のバージョンはGemではなくPackageインストーラでインストールします。


vagrantのインストールが完了したら、vagrantをつかって仮想環境をつくります。VirtualBoxでは仮想環境を固めたものをboxと呼ぶようです。
今回はCentOSのboxを追加します。各boxが、

http://www.vagrantbox.es/

に公開されているので、以下のようなコマンドでURLを追加します。



boxには名前をつける必要があるので、ここでは myfirstbox という名前をつけてみます。
$ vagrant box add myfirstbox https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box
これで、インストールが完了しました。



仮想環境の起動


適当なディレクトリをつくって、vagrant initをすると、Vagrantfileが出来ます。
$ mkdir virtualbox
$ cd virtualbox/
$ vagrant init
$ ls -l
total 16
-rw-r--r--  1 memorycraft  staff  4668  1 28 22:27 Vagrantfile

Vagrantfileを開いて、2点だけ修正します。
・box名を先ほどつけたmyfirstboxへ変更する
・ネットワーク設定をプライベートIPの192.168.33.10に設定する
IPは他のIPでも構いません

Vagrantfile

config.vm.box = "base"
↓
config.vm.box = "myfirstbox"

#config.vm.network :private_network, ip: "192.168.33.10"
↓
config.vm.network :private_network, ip: "192.168.33.10"


これで起動の準備が整ったので、起動してみます。起動はvagrant upをつかいます。
起動のログ出力が正常に終わると、起動完了です。
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'myfirstbox'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- /vagrant


起動した環境にsshログインしてみます。
$ vagrant ssh-config --host myfirstbox >> ~/.ssh/config
$ ssh myfirstbox
[vagrant@vagrant-centos65 ~]$
[vagrant@vagrant-centos65 ~]$ pwd
/home/vagrant
[vagrant@vagrant-centos65 ~]$ ls -la
total 24
drwx------. 3 vagrant vagrant 4096 Dec  5 14:18 .
drwxr-xr-x. 3 root    root    4096 Dec  5 14:18 ..
-rw-r--r--. 1 vagrant vagrant   18 Jul 18  2013 .bash_logout
-rw-r--r--. 1 vagrant vagrant  176 Jul 18  2013 .bash_profile
-rw-r--r--. 1 vagrant vagrant  124 Jul 18  2013 .bashrc
drwx------. 2 vagrant vagrant 4096 Dec  5 14:18 .ssh
[vagrant@vagrant-centos65 ~]$



おお、入れました。
それでは、ここにhttpdをインストールしてWEBサーバーを立ちあげてみましょう。
$ sudo yum install -y httpd
$ sudo /etc/init.d/httpd start



確認


そしてブラウザで、さきほどVagrantFileに記述したIPにアクセスしてみます。



簡単にWEBサーバーが出来てしまいました。
これで他に開発環境とかもつくれてしまいますね。

以上です。

2014年1月16日木曜日

SQSってなんじゃ?(supervisordでSQS)

いまさらですが、SQS+supervisordです。

supervisorはプログラムの起動監視と、自動起動/復帰などを行ってくれるツールです。
これをつかってSQSの受信処理プログラムをワーカーとして管理してみます。


受信テスト用のスクリプトを作成


メッセージの受信は、一度に最大の10件まで、ポーリング20秒で受信したメッセージをログファイルに出力するようにします。
また、今回はログの出力タイミングがわかりやすいように、1回のポーリング後10秒間sleepしています。
$ vim /home/ec2-user/svsqs.rb
#!/usr/local/bin/ruby

require 'aws-sdk'
require 'logger'

access_key = 'XXXXXXXXXXXXXXXX'
secret_key = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
queue_url = 'https://sqs.ap-northeast-1.amazonaws.com/ZZZZZZZZZZZ/svsqs'

log = Logger.new("/home/ec2-user/test.log");

AWS.config(:access_key_id => access_key,
  :secret_access_key => secret_key,
  :region => "ap-northeast-1")
sqs = AWS::SQS.new

while true
  messages = sqs.queues[queue_url].receive_message({:limit => 10, :wait_time_seconds => 20}).map do |m|
    json = JSON.parse(m.body)
    log.debug("#{$$} #{json['msg']}")
    m.delete()
  end
  log.debug("#{$$} -------")
  sleep 10
end
$ chmod 755 /home/ec2-user/svsqs.rb



supervisorのインストールと設定


次に、supervisorをインストールして、上記のスクリプトの起動管理するように設定します。
# easy_install supervisor
# vim /etc/init.d/supervisord

次にsupervisor自体の起動スクリプトをつくってサービス登録します。
#!/bin/sh
#
# /etc/rc.d/init.d/supervisord
#
# Supervisor is a client/server system that
# allows its users to monitor and control a
# number of processes on UNIX-like operating
# systems.
#
# chkconfig: - 64 36
# description: Supervisor Server
# processname: supervisord

# Source init functions
. /etc/init.d/functions

RETVAL=0
prog="supervisord"
pidfile="/tmp/supervisord.pid"
lockfile="/var/lock/subsys/supervisord"

start()
{
   echo -n $"Starting $prog: "
   daemon --pidfile $pidfile supervisord
   RETVAL=$?
   echo
   [ $RETVAL -eq 0 ] && touch ${lockfile}
}
stop()
{
   echo -n $"Shutting down $prog: "
   killproc -p ${pidfile} /usr/bin/supervisord
   RETVAL=$?
   echo
   if [ $RETVAL -eq 0 ] ; then
      rm -f ${lockfile} ${pidfile}
   fi
}
case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  status)
    status $prog
  ;;
  restart)
    stop
    start
  ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}"
  ;;
esac


# chmod 755 /etc/init.d/supervisord
# chkconfig --add supervisord
# chkconfig supervisord on
# chkconfig --list



監視対象プロセスの設定


子設定ファイルの読込み設定

主設定ファイルの末尾に、アプリケーションごとの設定ファイルをインクルードするように設定しておきます。
# echo_supervisord_conf > /etc/supervisord.conf
# echo "[include]" >> /etc/supervisord.conf
# echo "files = supervisord/conf/*.conf" >> /etc/supervisord.conf

子設定ファイルの作成

svsqsという名前で最初に書いたスクリプトの起動設定をします。 自動起動や、自動復帰をONにします。
# mkdir -p  /etc/supervisord/conf/
# vim /etc/supervisord/conf/svtest.conf
[program:svsqs]
command = /home/ec2-user/svsqs.rb
process_name = svsqs
autostart   = true
autorestart = true



supervisorのデーモン起動


ここまで設定できたらsupervisord自体を起動します。
# service supervisord start



supervisorのコンソールでstop/startテスト


supervisorctlというコマンドで管理対象プロセスの操作や状態確認が簡単にできます。
 supervisorが起動すると、autostartがONになっているため対象のスクリプトも起動していることが分かります。
# supervisorctl
svtest                           RUNNING    pid 18770, uptime 15:44:07
supervisor> stop svtest
svtest: stopped
supervisor> status
svtest                           STOPPED    Jan 16 05:12 AM
supervisor> start svtest
svtest: started
supervisor>
supervisor>
supervisor> status
svtest                           RUNNING    pid 10876, uptime 0:00:03
supervisor>
supervisor>
supervisor> exit



自動復帰の確認


プロセスを強制的に落としてみます。
するとsupervisorにより、新しいプロセスとして自動復帰します。
# ps -ax | grep svtest
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
10876 ?        S      0:00 /bin/sh /home/ec2-user/svsqs.rb
10892 pts/0    D+     0:00 grep svtest

# kill -9 10876
# ps -ax | grep svtest
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
10898 ?        S      0:00 /bin/sh /home/ec2-user/svsqs.rb
10901 pts/0    S+     0:00 grep svtest



メッセージの確認


このプログラムで扱うSQSのキューに対し、いくつかメッセージを送ってみると、受信できているのがわかります。
# tail -1000f test.log
D, [2014-01-16T07:02:28.085909 #13119] DEBUG -- : 10898 aaaaaa
D, [2014-01-16T07:02:28.176748 #13119] DEBUG -- : 10898 -------
D, [2014-01-16T07:02:38.347694 #13119] DEBUG -- : 10898 bbbbbbb
D, [2014-01-16T07:02:38.364416 #13119] DEBUG -- : 10898 cccccccc
D, [2014-01-16T07:02:38.414742 #13119] DEBUG -- : 10898 ddddddd
D, [2014-01-16T07:02:38.466001 #13119] DEBUG -- : 10898 -------
D, [2014-01-16T07:02:48.655003 #13119] DEBUG -- : 10898 eeeeeee
D, [2014-01-16T07:02:48.666323 #13119] DEBUG -- : 10898 ffffffff
D, [2014-01-16T07:02:48.718459 #13119] DEBUG -- : 10898 gggggggg
D, [2014-01-16T07:02:48.771584 #13119] DEBUG -- : 10898 -------
D, [2014-01-16T07:02:58.947327 #13119] DEBUG -- : 10898 hhhhhhhh
D, [2014-01-16T07:02:58.957724 #13119] DEBUG -- : 10898 iiiiiii
D, [2014-01-16T07:02:59.023820 #13119] DEBUG -- : 10898 -------
D, [2014-01-16T07:03:09.171886 #13119] DEBUG -- : 10898 jjjjjjj



複数プロセス


次に、このプログラムを並列で複数のワーカーとして起動管理するようにしてみます。
# vim /etc/supervisord/conf/svsqs.conf

複数のプロセスとして管理するには、numprocsというパラメータを2以上に設定します。
また、このときprocess_nameにはワーカーの番号を含める必要があるため、
supervisorの特殊変数 %(program_name)%(process_num)を利用します。
[program:svsqs]
command = /home/ec2-user/svsqs.rb
process_name = %(program_name)s_%(process_num)02d
numprocs = 2
autostart   = true
autorestart = true

設定を変えたので再起動し、プロセスを見てみると、2つ起動しているのが分かります。
# /etc/init.d/supervisord restart

supervisorctlでは、先ほど設定した名前でワーカー名が動的に付与されているのが分かります。
[root@ip-10-160-79-47 ec2-user]# supervisorctl
svsqs:svsqs_00                   RUNNING    pid 14286, uptime 0:36:24
svsqs:svsqs_01                   RUNNING    pid 14287, uptime 0:36:24

プロセスを落としてみると、自動で2プロセスにもどります。
#
# ps -ax | grep rb
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
14286 ?        Sl     0:00 /usr/local/bin/ruby /home/ec2-user/svsqs.rb
14287 ?        Sl     0:00 /usr/local/bin/ruby /home/ec2-user/svsqs.rb
14516 pts/0    S+     0:00 grep rb
#
# kill -9 14286
#
# ps -ax | grep rb
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
14287 ?        Sl     0:00 /usr/local/bin/ruby /home/ec2-user/svsqs.rb
14526 ?        Rl     0:00 /usr/local/bin/ruby /home/ec2-user/svsqs.rb

また、ログをみてみると、2つのプロセスできちんと動作しているようです。
# tail -1000f test.log
D, [2014-01-16T07:25:13.175073 #14287] DEBUG -- : 14287 -------
D, [2014-01-16T07:25:23.311352 #14286] DEBUG -- : 14286 aaaaaaaa
D, [2014-01-16T07:25:23.326240 #14286] DEBUG -- : 14286 bbbbbbbb
D, [2014-01-16T07:25:23.393122 #14286] DEBUG -- : 14286 ccccccc
D, [2014-01-16T07:25:23.441076 #14286] DEBUG -- : 14286 -------
D, [2014-01-16T07:25:28.144026 #14287] DEBUG -- : 14287 ddddddd
D, [2014-01-16T07:25:28.153058 #14287] DEBUG -- : 14287 -------
D, [2014-01-16T07:25:33.682118 #14286] DEBUG -- : 14286 eeeeeee
D, [2014-01-16T07:25:33.735474 #14286] DEBUG -- : 14286 fffffff
D, [2014-01-16T07:25:33.827446 #14286] DEBUG -- : 14286 -------
D, [2014-01-16T07:25:42.971976 #14287] DEBUG -- : 14287 ggggggg
D, [2014-01-16T07:25:42.986925 #14287] DEBUG -- : 14287 -------
D, [2014-01-16T07:25:44.555526 #14286] DEBUG -- : 14286 hhhhhhh
D, [2014-01-16T07:25:44.606725 #14286] DEBUG -- : 14286 -------
D, [2014-01-16T07:25:53.106090 #14287] DEBUG -- : 14287 jjjjjjjj
D, [2014-01-16T07:25:53.118289 #14287] DEBUG -- : 14287 -------
D, [2014-01-16T07:26:01.609948 #14286] DEBUG -- : 14286 kkkkkkkk
D, [2014-01-16T07:26:01.738742 #14286] DEBUG -- : 14286 -------
D, [2014-01-16T07:26:04.564231 #14287] DEBUG -- : 14287 llllllll
D, [2014-01-16T07:26:28.385778 #14287] DEBUG -- : 14287 -------
D, [2014-01-16T07:26:34.210485 #14526] DEBUG -- : 14526 mmmmm
D, [2014-01-16T07:26:34.301180 #14526] DEBUG -- : 14526 -------
D, [2014-01-16T07:26:39.204945 #14287] DEBUG -- : 14287 nnnnn
D, [2014-01-16T07:26:39.224593 #14287] DEBUG -- : 14287 -------

こんな感じで、仮にプログラムがエラーでオチたりした場合でも、常に指定したプロセス数に起動しなおしてくれると助かります。

以上です。