だるろぐ

だるいぶろぐです

perlbrew と CloudForecast を動かす

  • 環境
% cat /etc/redhat-release
CentOS release 6.3 (Final)

vim zsh screen あたりは入れておく。

perlbrew

まずは拾ってくる。

curl -kL http://install.perlbrew.pl | bash

そして使うためのファイルを読む設定。確か上記の作業後に指示された気がする。

echo " source ~/perl5/perlbrew/etc/bashrc " >> .zshrc_local

ときにこの場合、 .zshrc にはこう書いてある。

load_if_exists () {
    if [ -e $1 ]; then
        source $1
    fi
}

load_if_exists "$HOME/.zshrc_local"

普通に .bashrc / .zshrc に追記してもいい。


いったんシェルを抜けるなり、 source ~/perl5/perlbrew/etc/bashrc するなり。


無駄に最新版を入れる。

perlbrew install perl-5.16.2
perlbrew switch perl-5.16.2
perlbrew install-cpanm
perl -le "warn $]"
5.016002 at -e line 1.

daemontools

使うので。以下参考。
http://www.ftnk.jp/~fumi/cl/2010-04-19-2.html


コマンドは /command/ 以下にインストールされるので、自分のパス通ってるところに ln -s でもする。

memcached

使うよね。

wget http://memcached.googlecode.com/files/memcached-1.4.15.tar.gz
tar zxf memcached-1.4.15.tar.gz
cd memcached-1.4.15
sudo yum install libevent-devel
./configure --prefix=/usr/local/memcached
sudo make install

あとは適当に run ファイル書いて daemontools に任せる。

mysql

rpm を拾ってくるなど。
もうこれサーバ構築ですね。
http://dev.mysql.com/downloads/mysql/#downloads


この辺入れる。

MySQL-client-5.5.28-1.rhel5.x86_64.rpm
MySQL-devel-5.5.28-1.rhel5.x86_64.rpm
MySQL-server-5.5.28-1.rhel5.x86_64.rpm
MySQL-shared-5.5.28-1.rhel5.x86_64.rpm
MySQL-shared-compat-5.5.28-1.rhel5.x86_64.rpm


あと apache とか iptables とか色々と。

CloudForecast(準備)

本題。
拾ってくる。

git clone git://github.com/kazeburo/cloudforecast.git

依存ライブラリとか。以下参考。
http://memememomo.hatenablog.com/entry/20100726/1280124818
http://blog.riywo.com/2011/02/27/043646

あとこれ入れると多分いい。

cpanm Module::Install
cpanm Alien::RRDtool

依存perlモジュールは extlib に入れる想定みたいだけど、別に perlbrew 入れてるから構わず入れる。

cpanm --installdeps .

もう一度perlbrew

net-snmp 辺りを入れようとすると多分エラー吐く。
ググると、どうも perlbrew 入れるときに perlコンパイルオプション指定しないと駄目らしい。
泣きながら再インストール。

perlbrew uninstall perl-5.16.2
perlbrew install perl-5.16.2 -A ccflags=-fPIC

perl本体は削除され、再インストールされるけど、モジュールは削除されずに使いまわされたような気がする。いいのか?


改めて yum で net-snmp 辺りを入れる。当然インストールされるファイルの所有者が root になるので

cd ~/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/
sudo chown -R hirafoo:hirafoo *

する。そして先ほどのサイト見つつ snmp の何かをやるとか。

CloudForecast(起動)

yaml を用意する。

cp cloudforecast_sample.yaml cloudforecast.yaml
cp host_config/basic.yaml{,_org}

見たいの追加する。

--- a/host_config/basic.yaml
+++ b/host_config/basic.yaml
@@ -6,4 +6,9 @@
 component_config:
 resources:
   - traffic:eth0
+  - traffic:2:eth0
   - basic
+  - http:80
+  - memcached:11211
+  - mysql
+  - innodb

ディレクトリ作る。

mkdir data

daemontools で動かす前に、 daemontools でやるのと同等のコマンドを打つ。

root_path=/usr/local/cloudforecast
sudo /usr/local/bin/setuidgid hirafoo $root_path/cloudforecast_radar -c $root_path/cloudforecast.yaml -l $root_path/server_list.yaml

すると Can't locate local/lib.pm... とか怒られますね。


何でかってと

% head -n 1 /usr/local/cloudforecast/cloudforecast_radar
#!/usr/bin/env perl

このように cloudforecast_radar で /usr/bin/env perl を指定してるんですが、これ実際何が使われるかというとユーザによって変わるわけで。 perlbrew で入れた perl はそのアカウントでしか(普通)使えないわけで。つまり

% cat print_version.pl
#!/usr/bin/env perl
use strict;
use warnings;

print "$^V\n";

% chmod +x print_version.pl

% ./print_version.pl
v5.16.2

% sudo ./print_version.pl
v5.10.1

のように他ユーザで env perl すると perlbrew じゃない perl = システムの perl が使われるわけで。
だから run の中とかで

_perlhome="/home/hirafoo/perl5"

export PATH=$_perlhome/perlbrew/perls/perl-5.16.2/bin:/usr/local/bin:/usr/bin:/bin

とか書いてやる。あくまで一例。他にも色々やりようはある。


さーてこれで radar は動いたし、 web から見るかーと思うと

Odd number of elements in anonymous hash at /usr/local/cloudforecast/lib/CloudForecast/Host.pm line 19

とか言われます。該当部分を見ると

 12 sub list_graph {
 13     my $self = shift;
 14     my @ret;
 15     my $resources = $self->resources;
 16     for my $resource ( @$resources ) {
 17         my $data = $self->load_resource($resource);
 18         my @graphs = $data->list_graph;
 19         push @ret, {
 20             graph_title => $data->graph_title,
 21             resource_class => $data->resource_class,
 22             resource => $resource,
 23             graphs => \@graphs,
 24             sysinfo => $data->graph_sysinfo,
 25             last_error => $data->last_error,
 26         };
 27     }
 28     return @ret;
 29 }

とあるので、この @ret に突っ込んでる内容をダンプしてみると $data->last_error が undef だそうなので

-            last_error => $data->last_error,
+            last_error => $data->last_error || "",

とでもしてしまいます。

終わり

ここまで終えて、別にサーバで負荷かかるようなこと一切してないからグラフ見てもつまらないなーそもそもモニタリングする必要があるのって負荷かかってるサーバなんじゃ…そもそもこのサーバ何のために手に入れたの…とかは考えると落ち込むのでさっさと忘れてしまう。

date()が無ければleft()を使えばいいじゃない

こんなテーブルに

mysql> select hoge_datetime from hoge_log limit 1;
+---------------------+
| hoge_datetime       |
+---------------------+
| 2012-07-01 10:00:00 |
+---------------------+
mysql> select date(hoge_datetime) from hoge_log limit 1;

こう打ったら

ERROR 1064: You have an error in your SQL syntax.  Check the manual that corresponds to your MySQL server version for the right syntax to use near '(hoge_datetime) from hoge_log limit 1' at line 1

怒られて何でやねんとなったけどよくよく考えれば自分が触っているのは mysql 4.0 だった。

DATE() は MySQL 4.1.1 以降で使用できる。
http://dev.mysql.com/doc//refman/4.1/ja/date-and-time-functions.html

仕方ないので

mysql> select left(hoge_datetime, 10) from hoge_log limit 1;
+---------------------------+
| left(hoge_datetime, 10)   |
+---------------------------+
| 2012-07-05                |
+---------------------------+

で取った。group by にも使えた。

cpanモジュールにサンプルアプリなどを添付するときは no_index directory を指定する

例えばこのように。この場合は eg ディレクトリ以下にサンプルアプリが入っている。
http://cpansearch.perl.org/src/TOKUHIROM/Amon2-3.35/Makefile.PL

no_index directory => 'eg';

こうしないと、サンプルアプリの中で MyApp みたいな名前空間を使ったとき、それも cpan に登録されたり、そのディレクトリの中のファイルまでインデックスされて悲惨な事になります。
悲惨な例(27日に消えます) : http://search.cpan.org/~hirafoo/Pickles-Dispatcher-Auto-0.01/

これは既に某黒帯柔道家に教えてもらって対応済みの 0.02 が上がってますが、依存関係を全く解決していなかったのでテストがコケまくっているのに気付いたので 0.03 がそのうち上がります。


kazeburoさんにもアドバイス頂きました。ありがとうございます。

明示的に名前空間を回避する例

http://d.hatena.ne.jp/foosin/20090112/1231757674
1つのモジュールの中で複数個の名前空間使ったりとか、テンポラリな名前空間だったりとかなケースに使う。

偶然回避している例

例えばこれ。
http://cpansearch.perl.org/src/DMAKI/Pickles-0.10/Makefile.PL
これにも eg ディレクトリはあるし、その中で TinyURL という名前空間を使っている。
これ何で登録されないんだろなーと思って作者様に聞いてみました。きっと perl ハッカーは魔法を使っているに違いない。

Q. 何で登録されないんですか?
A. 同名の名前空間が既に存在するから弾かれてるだけでした。 http://search.cpan.org/perldoc?TinyURL


というわけでモジュール作って公開してあわわってならないように Makefile.PL はちゃんと書きましょうという話でした。マジ焦る。

Pickles::Dispatcher::Auto というモジュールを書いた

https://github.com/hirafoo/p5-Pickles-Dispatcher-Auto


Pickles というwafがあって、これの dispatcher はルーティングを記述したファイルが必須で、コントローラに定義されたアクションはこれに書かないといけない。
コントローラに100個アクションがある場合とか面倒なので、自動でディスパッチしてほしいなあと思ったのでそーいうのを書いた。

Picklesの中身

ディスパッチを自動化するにはオリジナルの Pickles::Dispatcher だけいじればいいんだろーと思うとそうではない。
見る順番としては

  1. myapp.psgi
  2. Pickles::WebApp
  3. Pickles::Context
  4. Pickles::Dispatcher

となる。


ディスパッチがハンドラとコンテキストに関係してるので、ディスパッチャだけ上書きすればいいわけではない。
なのでモジュールの中で Pickles::Context の _prepare / controller_class のメソッドを上書きしている。

悩みどころ

/hoge

という path_info を処理するコントローラ・アクションとテンプレートはそれぞれ

  • MyApp::Controller::Root#hoge
  • /hoge.html

でいいと思う。これは不自然ではなく、反論も無いと思う。

めんどいのはこんな path_info の場合。

/foo/

コントローラ・アクションの考えとしては

  • MyApp::Controller::Foo#index
  • MyApp::Controller::Foo::Root#index

が挙がる。一方テンプレートは

/foo/index.html

が自然だと思う。


モジュールを作る上でこのコントローラ・アクションをどっちにするか決めないといけない。
悩んだ末、後者にした。理由は

/

を処理するのが

MyApp::Controller::Root#index

なので。
どうでもいいが、ざっと何かを見た感じ、その中では割と適当だった。
Slash.pm とかもあったような気がしたが空目だと信じる。

いわゆる MyApp::Web とかの名前空間

Catalyst(ハードウェアじゃない方の) 全盛期のときに、 MyApp::Controller じゃなくて MyApp::Web::Controller というような名前空間を推奨するような動きがあった。
大賛成なのだが、 Pickles ではそれが大分実現しづらい。


実はこのモジュール、大分前から原型がある。
http://d.hatena.ne.jp/hirafoo/20110119/1295444740
そっちの方は、あるプロダクトで Pickles を使った時に、

  1. Pickles::WebApp
  2. Pickles::Context
  3. Pickles::Dispatcher
  4. MyApp.psgi

に手を入れて、

  • MyApp::Admin::Controller
  • MyApp::PC::Controller
  • MyApp::Lite::Controller
  • MyApp::Mobile::Controller

のような名前空間で使っていたときに作ったもの。なので実はあれ単体では動かなくて、コードを読んで意図を理解して適切な変更を加える必要がある。駄目ですね。


今回はそもそもの Pickles の流儀に従っているし、各クラスのメソッドを上書きすることなくデフォルトのままで使えるようにしてある。


ところで、この名前空間をデフォルトから変更しよう、というやつ、

MyApp::Controller::Foo::Root
MyApp::Controller::Bar::Root

というのと、

MyApp::Foo::Controller::Root
MyApp::Bar::Controller::Root

というのではどっちがいいのだろう。ケースバイケースかつ人の好みな気はする。
「ここ以下は Foo の領域ですよ」と扱うか、「ここ以下はコントローラですよ」と扱うかの差なので、やりたい事や実装にもよるか。

Angel Browserが裏で怪しいpostリクエストを発行している

android端末ではブラウザにAngel Browserを使っている。
ブックマーク画面を開くと下部に広告が出るので、以前の方法( http://d.hatena.ne.jp/hirafoo/20120521/1337613553 )でまた消そうと思ったら、 postリクエストを見つけた。
ログから一部抜粋。

"POST http://api.ad-stir.com/report/ HTTP/1.1" 200 226 "-" "Apache-HttpClient/UNAVAILABLE (java 1.4)" -

広告ブロックの前に中身を見てみた。
proxyサーバに手を入れ、クエリパラメータをダンプしてみる。

対象ログ

さっきの。しばらくログを見てみたけど、post先はここしか無い模様。
詳しくないけど Apache-HttpClient というのがandroidアプリのものだということくらいは知っていた。まあアプリ内でやってんだからそりゃそうですね。
バージョン表記とかは知らん。

"POST http://api.ad-stir.com/report/ HTTP/1.1" 200 226 "-" "Apache-HttpClient/UNAVAILABLE (java 1.4)" -

パラメータ

ダンプしたのを適当に整形した。
"key : value" のフォーマット。

ua : Android_app_SC-02C_10
ad_spot_no : 1
date : 1338044400
app_id : MEDIA-d0e75d0f
ver : 1.3.2
Content-Type : application/json; charset=UTF-8
data : {"data":[{"impression":"1","sdk_id":"27"}]}


date は今日の日付のepochだった。でもymdまでしか指定されていない。
ISO 8601で書くと '2012-05-27T00:00:00' になる。
app_idとver は何だろ。Angel Browserのものか、広告sdkのものか。


まあそんなことよりもユーザエージェントを送っているのがよろしくない。
おかげで俺の使ってる端末がばれた。

うんまあ

postだからどうというわけでは全然ないけど、気持ち悪い上に、勝手に自分の端末情報送られるのはもっと気持ち悪い。
そもそもgetですら送るんじゃねえ。
ただでさえ遅い3G回線なのに、広告取得するために無駄な通信しないでほしいっていうかそもそも広告滅べ。


あと別にpostじゃなくてgetも普通にしてる。
こっちはua以外にも uid / url / ip / id / slot / locale / platform とか色々送ってる。
どれもアレだが中でも uid って何だおい。 ip も何だおい。


とりあえず今回の遮断リスト。

adsta.jp
as.a.bypass.jp
img.bypass.jp
js.ad-stir.com
api.ad-stir.com
rtb.ad-stir.com

最後のドメインにブラウザでアクセスしたら Welcome to nginx! と出迎えられた。
apache じゃなくて nginx かー頑張ってますねー

androidで広告を非表示にする

iphone / androidfc2ブログ食べログなどを見たときに表示される、ページをスクロールすると付いてくる広告がこの上なく邪魔なので消す。
とりあえず android で。

AdAway を入れる

https://play.google.com/store/apps/details?id=org.adaway
広告配信サーバのドメインを拾ってきて、そのドメインが全部 127.0.0.1 を向くように hosts を編集してくれるアプリ。
標準のリストは超重い上に海外のが多いらしいので、ググってマシなリストを探して使う。
大体はこれで消えるらしい。
が、消えないのがあるので、広告配信元のドメインを探して追加する。

proxy サーバを用意する

mod_proxy 辺りでさくっと。
そしたら ProxyDroid で繋いで、広告が表示されるサイトやアプリを見る。
ProxyDroid は、ブラウザのみならず全ての通信が proxy サーバを経由するらしいので、ログからドメインを拾う。
標準的な apache のログフォーマットならこうする。

% cat proxy_access_log |cut -d " " -f 7 |sort |uniq  |perl -nle '$_ =~ m{^http://([^/]+)/}; print $1' |sort |uniq

アプリなら大抵広告の通信しかしてないので全部ぶち込む。
サイトはいかにも広告っぽいのは追加して、判断つかないのはアクセスして調べる。

データを編集する

AdAway とかに載ってるドメインの中には、広告じゃないドメインや、広告以外のコンテンツも扱うドメインも含まれているので、適宜削除する。
例えば yahoo はよく見るので以下は弾かない。

  • i.yimg.jp
    • sp サイトの css も扱ってるので、これも弾いてしまうと yahoo 全体が崩れて表示される。
  • rd.yahoo.co.jp
    • リダイレクタに使ってるので、コンテンツによってはリンク先に飛べなくなる


あとドメインが向く先が 127.0.0.1 になってたら自分にアクセス試みて負荷になるんじゃね? 0.0.0.0 でいんじゃね?と思ってググったら、以下を見つけた。
http://logsoku.com/thread/hibari.2ch.net/win/1129451974/
よく分からんけどどっちでも大差ないらしい。でも何となく 0.0.0.0 にしてみる。


あとは自分のサーバにリスト置くなり、手動で hosts 書き換えるなり。
そういやググったら ios にも hosts はあるらしいので、 ios / android どっちも hosts を書き換えるアプリ作って使いまわせるんじゃね?と思ったけど思っただけ。
多分既に誰かがやってんだろ。もしくはやる人は自分でやってんだろ。


そして一部誤爆して食べログの食べ物のご飯自体出なくなったりするけど、そもそも食べログ滅多に見ないから別にいいや。

その他

ググってたら、広告を非表示にしたいけどドメインが分からない場合の手法として、
「広告が表示されるサイト・アプリを見て広告が表示されてる状態で、機内モードなどにして通信されないようにした上で広告をクリックして、ブラウザに表示されたドメインから調べる」
ってのがあったけど、それって実際に広告が表示される流れの

  1. js などで広告のソースの url を get する
  2. その url が実際に img src などで広告を表示する

の、2つ目しか対応できてないよね。大本の get してるドメインから殺すべし。


あと手動で追加した広告ドメインの一部。

0.0.0.0 image1-1.tabelog.k-img.com
0.0.0.0 image1-2.tabelog.k-img.com
0.0.0.0 image1-3.tabelog.k-img.com
0.0.0.0 image1-4.tabelog.k-img.com

0.0.0.0 203.104.105.105
0.0.0.0 ad1.nend.net
0.0.0.0 i.adingo.jp
0.0.0.0 img1.nend.net
0.0.0.0 img.fluct.jp.eimg.jp
0.0.0.0 js1.nend.net
0.0.0.0 media.admob.com
0.0.0.0 op1.searchteria.co.jp
0.0.0.0 op2.searchteria.co.jp
0.0.0.0 op3.searchteria.co.jp
0.0.0.0 op4.searchteria.co.jp
0.0.0.0 op5.searchteria.co.jp
0.0.0.0 op6.searchteria.co.jp
0.0.0.0 op7.searchteria.co.jp
0.0.0.0 op8.searchteria.co.jp
0.0.0.0 op9.searchteria.co.jp
0.0.0.0 op10.searchteria.co.jp
0.0.0.0 op11.searchteria.co.jp
0.0.0.0 op12.searchteria.co.jp
0.0.0.0 op13.searchteria.co.jp
0.0.0.0 op14.searchteria.co.jp
0.0.0.0 op15.searchteria.co.jp
0.0.0.0 op16.searchteria.co.jp
0.0.0.0 op17.searchteria.co.jp
0.0.0.0 op18.searchteria.co.jp
0.0.0.0 op19.searchteria.co.jp
0.0.0.0 op20.searchteria.co.jp
0.0.0.0 op21.searchteria.co.jp
0.0.0.0 op22.searchteria.co.jp
0.0.0.0 op23.searchteria.co.jp
0.0.0.0 op24.searchteria.co.jp
0.0.0.0 op25.searchteria.co.jp
0.0.0.0 op26.searchteria.co.jp
0.0.0.0 op27.searchteria.co.jp
0.0.0.0 op28.searchteria.co.jp
0.0.0.0 op29.searchteria.co.jp
0.0.0.0 op30.searchteria.co.jp
0.0.0.0 op.searchteria.co.jp
0.0.0.0 output.nend.net
0.0.0.0 sh.adingo.jp
0.0.0.0 spap.adingo.jp.eimg.jp