Capistranoのメモ

最近Capistrano使ってて何点かハマったのでメモ

通常複数サーバにデプロイする場合に並列実行されてしまうが直列に実行したい場合、max_hosts => 1を追記する。

namespace :deploy do
  task :hoge, :roles => app, :max_hosts => 1 do
    run "echo `hostname`"
  end
end

 

nohupでプロセスをバックグラウンドで実行したい場合、(実行したいコマンド) && sleep 1とする。


run "(cd $DIR && (nohup ./app &)) && sleep 1"

sleep 1の箇所はecho 1でもいけるけどsleepがいいかな。

Hadoop-1.1.1のインストールメモ

久々にhadoopを触ってrpmでインストールしたらstart-*.sh、stop-*.shに実行権限がついて無かった。
昔と作法?が変わっているようなのでメモ。

JAVAのインストール

javaが入っていない場合は下記のURLからダウンロード、インストールを行う。
バージョンは今回は1.6の最新版を利用する。
http://www.oracle.com/technetwork/java/javase/downloads/jdk6downloads-1902814.html
jreだとjpsコマンドがインストールされないのでjdkのインストールを行う。


root# chmod 755 jdk-6u41-linux-x64-rpm.bin
root# ./jdk-6u41-linux-x64-rpm.bin

.bashrc等にJAVA_HOMEの追加


root# echo "JAVA_HOME=/usr/java/default" >> ~/.bashrc
root# echo 'export PATH=$PATH:/usr/java/default/bin' >> ~/.bashrc

Hadoopのインストール

masterの構築

下記のURLからダウンロード
http://archive.apache.org/dist/hadoop/core/hadoop-1.1.1/


root@master# rpm -i hadoop-1.1.1-1.x86_64.rpm

hadoopユーザの追加、環境変数の追加


root@master# echo "HADOOP_HOME=/usr" >> ~/.bashrc
root@master# useradd hadoop
root@master# mkdir -p /var/lib/hadoop
root@master# chown hadoop:hadoop /var/lib/hadoop

hostsファイルの設定。ここでは自身(master)用とslave用の名前を設定する
このIPは仮なので環境に合わせて設定する事

  • /etc/hosts


192.168.1.2 master
192.168.1.3 slave001

設定ファイルの生成。対話形式で設定ファイルを生成する。


root@master# hadoop-setup-conf.sh

Where would you like to put config directory? (/etc/hadoop)
Where would you like to put log directory? (/var/log/hadoop)
Where would you like to put pid directory? (/var/log/hadoop)
What is the host of the namenode? (master)
Where would you like to put namenode data directory? (/var/lib/hadoop/hadoop/namenode)
Where would you like to put datanode data directory? (/var/lib/hadoop/hadoop/datanode)
What is the host of the jobtracker? (master)
Where would you like to put jobtracker/tasktracker data directory? (/var/lib/hadoop/mapred)
Where is JAVA_HOME directory? (/usr/java/default)
Would you like to create directories/copy conf files to localhost? (Y/n) Y

Review your choices:

Config directory : /etc/hadoop
Log directory : /var/log/hadoop
PID directory : /var/log/hadoop
Namenode host : master
Namenode directory : /var/lib/hadoop/hadoop/namenode
Datanode directory : /var/lib/hadoop/hadoop/datanode
Jobtracker host : master
Mapreduce directory : /var/lib/hadoop/mapred
Task scheduler : org.apache.hadoop.mapred.JobQueueTaskScheduler
JAVA_HOME directory : /usr/java/default
Create dirs/copy conf files :

Proceed with generate configuration? (y/N)

hadoopユーザの設定


root@master# su - hadoop
hadoop@master$ echo "JAVA_HOME=/usr/java/default" >> ~/.bashrc
hadoop@master$ echo 'export PATH=$PATH:/usr/java/default/bin' >> ~/.bashrc
hadoop@master$ echo "HADOOP_HOME=/usr" >> ~/.bashrc
hadoop@master$ ssh localhost <- sshとknown_hostの作成のためだけなのでexitする
hadoop@master$ cd .ssh
hadoop@master$ ssh-keygen -t rsa -P "" -f hadoop.pem -C "hadoop@master"
hadoop@master$ cat hadoop.pem.pub >> autorized_keys <- ここは/etc/ssh/sshd_configを参照すること
hadoop@master$ chmod 600 authorized_keys
hadoop@master$ vi config

# master
Host master
Port 22
User hadoop
Hostname master
IdentityFile ~/.ssh/hadoop.pem
TCPKeepAlive yes
IdentitiesOnly yes

# slave
Host slave001
Port 22
User hadoop
Hostname slave001
IdentityFile ~/.ssh/hadoop.pem
TCPKeepAlive yes
IdentitiesOnly yes

hadoop@master$ ssh master <- これでログインできれば設定完了

slaveはslaveのhadoopユーザの設定が終わったら確認

namenodeのフォーマットを行う


root@master# sudo -u hadoop hadoop namenode -format

各サーバの起動


root@master# sudo -u hadoop hadoop-daemon.sh start namenode
root@master# sudo -u hadoop hadoop-daemon.sh start secondarynamenode
root@master# sudo -u hadoop hadoop-daemon.sh start datanode
root@master# sudo -u hadoop hadoop-daemon.sh start jobtracker
root@master# sudo -u hadoop hadoop-daemon.sh start tasktracker
jpsコマンドで起動の確認

root@master# jps
27966 Jps
3167 TaskTracker
2915 DataNode
3033 JobTracker
3308 JobHistoryServer
2668 NameNode
2824 SecondaryNameNode
起動したものが表示されていない場合は/var/log/hadoop/hadoop/*.logの該当のログを確認して対応する事

Slaveの構築

マスターの構築とほぼ同じなので省略する
.ssh/以下はmasterのhadoopユーザのものをコピーする
下記のコマンドでマスターと自身への接続を確認


hadoop@slave001$ ssh master
hadoop@slave001$ ssh slave001
nameノードは起動しないのでフォーマットは行わない。
起動するのはdatanodeとtasktrackerのみ

root@slave001$ sudo -u hadoop hadoop-daemon.sh start datanode
root@slave001$ sudo -u hadoop hadoop-daemon.sh start tasktracker
jpsコマンドで確認

root@slave001$ jps
21500 DataNode
21906 TaskTracker
23651 Jps

masterの/etc/hadoop/slavesに追記


slave001
設定を読み込む

root@master# sudo -u hadoop hadoop dfsadmin -refreshNodes
root@master# sudo -u hadoop hadoop mradmin -refreshNodes

slaveを追加した後にHDFSのブロックの偏りが大きい場合はblancerを起動する
blancerはHDFSのブロックの格納量が標準化した時点で自動的に終了する


root@master# sudo -u hadoop hadoop-daemon.sh start balancer

gunicornのベンチ

nginx + tornadoのAPIを書いてgunicornの有り無しでのベンチ。

GETでリクエストを受け取って引数によってJSの内容を書き換えて表示するAPIで行う。

ベンチ対象マシンのCPUはXeonのL5410 @ 2.33GHzで4コア。

nginxのworkerを4でリバースプロキシとして利用。

tornadoはport8880 - 8883で4プロセス立ち上げてある。

gunicornのworkerは "workers = multiprocessing.cpu_count() * 2 + 1" とした。

ベンチマークはabを使ってconcurrency levelを10から300で、10000リクエスト固定で行い

こんな感じ


$ ab -c * -n 10000 "http://xxxxxx?a=1&b=1"

以下、ベンチ結果*1

concurrency level nginx+tornado request/sec nginx+gunicorn+tornado request/sec
10 2913.766 2831.45
50 3303.586 3045.558
100 3382.636 3062.152
150 2264.074 3106.05
200 2183 3047.724
300 2056.522 2508.646

tornadoは同時接続数が少ない場合は性能がいいが100を過ぎた辺りで急に性能が落ちた。

gunicorn有りの場合は200まで安定している。


[2012-10-26追記]
gunicornを複数のポートで立ち上げるというボケをかましていて性能が劣化していたようで…
1つにしたらconcurrency levelを600にしても4000req/secくらい出てましたよと。
gunicornすばらしい。

*1:request/secの数値は5回ベンチした結果の平均

python gunicornの起動メモ

tornadoとgunicornを連携させたい、が起動してくれなかったのでメモ

gunicorn.sh
gunicorn.py

の2ファイルでgunicorn.shからgunicorn.pyを起動したい。
起動コマンドは下記


$ gunicorn -k egg:gunicorn#tornado -b xxx.xxx.xxx.xxx:8888 gunicorn:application

が、エラーが出て起動してくれない


gunicorn Reason: Worker failed to boot

wikiに行ったら "--debug --log-level debug" つけて確認しろってあったので確認


$ gunicorn -k egg:gunicorn#tornado -b xxx.xxx.xxx.xxx:8888 --debug --log-level debug gunicorn:application
.
.
.
NameError: name 'application' is not defined

applicationが無い???

あーもしかしてファイル名指定で読み込む時にgunicorn.shを見にいってるのかな?と


$ mv gunicorn.py main.py
$ gunicorn -k egg:gunicorn#tornado -b xxx.xxx.xxx.xxx:8888 --debug --log-level debug main:application

ファイル名変更したら起動してくれました

varnish3の設定メモ

varnish3の設定メモ

  • varnishでキャッシュしたJSをgzipで配信


sub vcl_fetch {
if (req.url ~ "\.js$") {
set beresp.do_esi = true;
set beresp.do_gzip = true;
}
}

  • 画像、CSS、JSへのアクセスからCookieを除去


sub vcl_recv {
if (req.url ~ "\.(png|gif|jpg|swf|css|js)$") {
unset req.http.cookie;
}
}

sub vcl_fetch {
if (req.url ~ "\.(png|gif|jpg|swf|css|js)$") {
unset beresp.http.set-cookie;
}
}

  • 不必要なヘッダは削除


sub vcl_deliver {
remove resp.http.X-Varnish;
remove resp.http.X-Powered-By;
remove resp.http.Via;
return (deliver);
}

  • リバースプロキシへの転送時にReal-IPヘッダを付与


sub vcl_recv {
remove req.http.X-Forwarded-For;
set req.http.X-Real-IP = regsub(req.http.rlnclientipaddr, "^([0-9.]*),.*$", "\1");
set req.http.X-Forwarded-For = req.http.rlnclientipaddr;
}

とか

ElementTreeでxmlのprefixが指定できない

rssをパースして属性の値や要素を抽出したい

rssの形式はこんな感じ。

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:prefixName="http://xxxx.xx.xx/rss">
<channel>
<title>RSS</title>
<link>http://xxxx.xx.xx/rss</link>
<description>RSS</description>
<lastBuildDate>Thu, 16 Aug 2012 17:23:03 +0900</lastBuildDate>
<language>ja</language>

<item>
<title>test data 1</title>
<link>http://xxxx.xx.xx/rss</link>
<description></description>
<pubDate>Thu, 16 Aug 2012 17:00:00 +0900</pubDate>
<prefixName:details name="test1" id="1" />
</item>

<item>
<title>test data 2</title>
<link></link>
<description></description>
<pubDate>Thu, 16 Aug 2012 17:00:00 +0900</pubDate>
<prefixName:details name="test2" id="2" />
</item>

</channel>
</rss>

RSSを取得してパースするコード

import urllib2
from xml.etree.ElementTree import ElementTree, register_namespace, _namespace_map, tostring

def main():
    try:
        register_namespace('prefixName', 'http://xxxx.xx.xx/rss')
        rss   = ElementTree(file=urllib2.urlopen("http://xxxx.xx.xx/rss/data.xml"))
        items = rss.findall('.//item')
        for item in items:
            print item.find('title').text
            print item.find('prefixName:details')
    except Exception, e:
        print e

if __name__ == "__main__":
    main()

実行結果


prefix 'prefixName' not found in prefix map

prefixmapに無いと言われたので、xml.etree.ElementTree._namespace_mapを確認するとregister_namespace('prefixName', 'http://xxxx.xx.xx/rss')がちゃんと適用されてる。

xml/etree/ElementPath.pyのコードを見てみるとxpath_tokenizer関数の

yield token[0], "{%s}%s" % (namespaces[prefix], uri)

でnamespacesがNoneに書き換わっていて例外に飛んでるみたい。

findの引数に{'http://xxxx.xx.xx/rss':'prefixName'}を追加してもダメでした。

あと気になったのは_namespace_map見てると{'uri':'prefix',,,}形式の辞書になってるのにこの(namespaces[prefix], uri)って指定間違ってないか。

どうにもならんかったのでitem.getchildren()で取得できたノードを確認すると


[]

{uri}tagnameの形式になってるんですけど

下記のコードに書き換えて属性の値の取得ができた。

print item.find('{http://xxxx.xx.xx/rss}details').attrib['name']

こういうものなんですかね。

find('prefix:tagname')で指定できると思ってました。

プロキシ経由でnginxにアクセスした時にクライアントのIPを取得する

www -> VARNISH(proxy) -> Nginx
のようにプロキシ経由でNginxにアクセスした際にNginx側のログのフォーマットを


log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$request_time"';
などとしていると$remote_addrにVARNISHのIPが入ってしまってクライアントのIPが取得できないので
クライアントのIPが取得できるようにしたい

以下設定

varnish


sub vcl_recv {
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = req.http.rlnclientipaddr;
}

nginx


log_format main '$proxy_add_x_forwarded_for - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$request_time"';

$remote_addrを$proxy_add_x_forwarded_for に変更することでクライアントのIPが取得できるようになった

ただカンマ区切りで(clientip, proxyip のように)プロキシのIPも含まれてしまう


192.***.***.10, 192.***.***.11

なのでVARNISH側でつけているのかな?と思って下記の設定を追記


sub vcl_recv {
.
.
.
set req.http.X-Real-IP = regsub(req.http.rlnclientipaddr, "^([0-9.]*),.*$", "\1");
}

してnginx側で$real_ip_header変数で取得しようと思ったらmod_real_ip入って無くて断念
これapt-getでどうやって入れるの?

で、よくわからなかったので


set req.http.X-Forwarded-For = regsub(req.http.rlnclientipaddr, "^([0-9.]*),.*$", "\1");
としてみたらnginxのログで

, 192.***.***.10, 192.***.***.11

カンマが増えてしまった...

$proxy_add_x_forwarded_for 使えばゴミ(proxyip)付きだけどクライアントIPが取得できたので今回はここまで