About Me

  • 投稿日:
  • by

あうあう

(Image|Graphics)Magick は resize 後の横縦サイズは小数部分(第1位であろう)を四捨五入している。らしい。

Distort Resize - free-form resizing

All the above resize methods all have one limitation which we touched on earlier, they will round the size of the new image to an integer number of pixels, then map the old image's pixels to the new pixel array.

http://www.imagemagick.org/Usage/resize/

なので、サムネイル画像を動的につくっているけど、その img 要素の width, height を事前に埋めたいときには、四捨五入して計算すること。で、うまくいっている風。

cpanfile for MovableType

  • 投稿日:
  • by
  • カテゴリ:  

この前、さくらの VPS が落ちて再起動されてしまったのち、いっしょに死んだままだった MT を久々に起動するにあたり carton 化しようとおもって作った cpanfile。

Starlet は適宜使いたいサーバーに、Server::Starter、XML::LibXML は不要であれば外しつつ、MySQL じゃないひとは DBD::mysql も適当に変更して、carton install すればできあがり。

% carton exec -- local/bin/start_server --port 8000 --pid-file=log/mt.pid -- plackup -s Starlet --max-workers=2 --access-log=log/access.log mt.psgi >& logs/error.log &

みたいな感じで起動。しかし、ログローテートもしてないしあんばいがよくないので supervisor で動かすようにする。いつか...。

CPAN Module のバージョン間 diff をコマンドラインでみる

  • 投稿日:
  • by
  • カテゴリ:  
CPAN Module の変更点をサクッと調べる方法

というエントリーに反応してみるテスト。

僕は CPAN Module のバージョン間の diff をとるのに、search.cpan.org にある diff 機能を使う以下のようなやっつけスクリプトを使ってる。

たまに空のレスポンスが返ってきたりするけど....、これを less とか grep -A100 .pm とかなんとかにパイプしたりしなかったりしてコマンドラインで生 diff を確認。Cache::Memcached::Fast の 0.19 と 0.20 だったら以下の様な塩梅。

パッケージまるごとの diff なので README とか、MANIFEST とかの差分もはいっててノイズがまぁまぁあるといえばあるけど、目 grep で除外...。

metacpan っていまだによくわかってないけど(汗)、同じようなのあるかしら。

fluent-plugin-ganglia 0.0.1 released

  • 投稿日:
  • by
  • カテゴリ:    

この前つくった fluent-plugin-ganglia を gem にしてバージョン 0.0.1 をリリースした。

前回のエントリーで書いた時点では、gmond を multicast かつ upd_send_channel を bind_hostname = "yes" として動かしている場合に実はうまくいっていなかった。うごいた!とおもってあのエントリーをよろこびいさんで書いた時点では、1台のホストでしかどうささせていなくて、同じ metric group にはいる他のホストで動かし始めたら全然グラフでてこない、という始末...。

これは gmetric gem の Ganglia::GMetric#send が、

conn = UDPSocket.new
conn.connect(host, port)

conn.send gmetric[0], 0
conn.send gmetric[1], 0
conn.close

こうなってて bind してないからだったようで、Ganglia::GMetric#send は使わずにプラグイン内で、

conn = UDPSocket.new
conn.bind(HOSTADDR, 0) if @bind_hostname
conn.send gmetric[0], 0, @host, @port
conn.send gmetric[1], 0, @host, @port
conn.close

こんなような感じで IPSocket.getaddress(Socket.gethostname) でとってきた IP (HOSTADDR定数のところ) に bind して送るようにしたらうまく行った。まぁ、つまり bind_hostname = "yes" なんで bind しろよ、ということと思われる。gmetric コマンドを strace してみてても bind してたし(というか strace して差分みてて気づいた)。

この multicast + bind_hostname してる環境で metric 送りつけるには、こういう設定。

<match metrics>
  type          ganglia
  host          239.2.11.71
  port          8649
  group         metric_group
  name_keys     metrics.field1,metrics.field2
  bind_hostname true
</match>

bind_hostname true で bind してから送るようになる。multicast だけど bind_hostname してなければいらない。

というのを経ての 0.0.1 はコレの本番環境で絶賛稼働中。unicast での Ganglia はためしたことがないのだけどたぶんうごくとおもうので、もし試せる方おりましたらフィードバックいただけると幸い...。

ご利用ください。

Plack::Middleware::DefaultDocument

  • 投稿日:
  • by
  • カテゴリ:  

Plack::Middleware::DefaultDocument というのを作った。

MogileFS のクライアントとしてだけ動く小さな PSGI アプリかいていて、MogileFS になかったらアプリがもってるデフォルトのファイルを 404 ではなく 200 で返したい、ということをやりたかったんだけど、これを実現する方法として、

  • app 側で対応する
    • 汎用的にしたかったのでなし
  • 手前にいる reverse proxy(apache2.2)にやらせる
    • プロキシした結果 404 だったら別の何かを 200 で返す、という方法がわからなかった
    • あったとしても mod_rewrite での対応となると今回はきつい
      • このアプリにプロキシされるまでにすでにカオス RewriteRule をくぐり抜けてきているので...
  • さらに手前にいる varnish / perlbal にやらせる
    • varnish がバックエンドから 404 うけとったら req.url 書き換えて restart、varnish の後ろにいる perlbal の web server につかませる
    • キャッシュがあればバックエンドひと通り巡るコストもなく速いが関係者が増えるのでなんかいや
  • Middleware::ErrorDocument で subrequest なげて Middleware::Static につかませる
    • 404 は普通につかいたいので、それとは別に enable_if { $_[0]->{PATH_INFO} =~ m{/favicon.ico} } ErrorDocument 〜 とかしてわけて、Static で path => sub { s/// } で PATH_INFO 書き換えて特定ディレクトリからさがす
    • 別にこれでよかった(え)

など堂々めぐりした結果、簡単にできるやつをということで作った。

使い方はこんなん感じ。

enable "DefaultDocument",
    '/favicon\.ico$' => '/path/to/htodcs/favicon.ico',
    '/robots\.txt'   => '/path/to/htdocs/robots.txt';

app からのレスポンスコードが 404 だった場合に、PATH_INFO が key の正規表現にマッチしたら value のファイルを返す、という動作をする。404 以外が返っていこうとした場合は何もしない。

MogileFS からファイル探して、なかったらデフォルトのなにかを必要に応じてだす感じだとこういう風の例。

DefaultDocument という名前は英語的、動作の説明的に微妙な気がしないでもない...。そして相変わらずの俺得 module 感...。

Puppet で td-agent, fluentd plugins を管理

  • 投稿日:
  • by
  • カテゴリ:    

td-agent と fluent-gem コマンドでインストールした fluentd plugins、gem を管理するための Puppet モジュールつくってみた。

package provider に "fluentgem" が追加されているので、

include td-agent
package { 'fluent-plugin-datacounter':
  ensure   => 'installed',
  provider => 'fluentgem',
  require  => Package['td-agent'],
}

として、fluent plugin な gem などを追加することができるので、全部 /etc/td-agent/plugin にいれるとか、exec でほげほげしてふがふがして とか面倒なことしなくてもよくなった。あとは、最小限の td-agent.conf がはいっているのみ。

これで、 td-agent をばらまけるようになったし、先週書いた ganglia plugin で metric 収集も万全。ようやく fluentd スタート地点。

fluent-plugin-ganglia

  • 投稿日:
  • by
  • カテゴリ:    

fluentd を使い始めて2日目。結局 ganglia に metric 送れないことには....、ということで plugin 書いてみた。

仕組み的には @sfujiwara さんの zabbix のやつとおんなじやろ、と思い https://github.com/fujiwara/fluent-plugin-zabbix からコピペしていくつかもにょもにょしただけ (・∀・)

LTSV なアクセスログをみて datacounter でステータスコード別に集計して ganglia に metric おくる、というのをやってみた。

<source>
  type tail_labeled_tsv
  path /var/log/users_access_log
  tag web.access
  pos_file /tmp/td-agent/users_access_log.pos
</source>

<match users.access>
  type copy

  <store>
    type datacounter
    tag web.access.status_count
    count_interval 60s 
    count_key status
    pattern1 2xx ^2\d\d$
    pattern2 3xx ^3\d\d$
    pattern3 4xx ^4\d\d$
    pattern4 5xx ^5\d\d$
  </store>
</match>

<match users.access.status_count>
  type copy
  <store>
    type stdout
  </store>
  <store>
    type             ganglia
    gmond_port       8701
    name_key_pattern [2345]xx_count
    value_type       uint32
    units            views/min 
    group            users_access
    spoof            192..0.2.100:web01
    dmax             30
  </store>
</match>

設定値は gmetric コマンドのロングオプション名とほぼ同じ。fluentd とかぶる名前が幾つかあるので

  • port = gmond_port
  • type = value_type

となっている。

ganglia に metric おくるのには gmetric というそのまんまの名前の gem があったのでそれを使ってぺっと送る。が、td-agent で使っている ruby 1.9.3 に対応していないのか以下のようなエラーがでる...。

2013-02-15 18:38:59 +0900: fluent/engine.rb:115:rescue in emit_stream: emit transaction failed  error="undefined method `reactor_running?' for EventMachine:Module"

とりあえず以下パッチをあてたらうごいた。

--- gmetric.rb.org  2013-02-15 18:41:17.000000000 +0900
+++ gmetric.rb        2013-02-15 18:41:05.000000000 +0900
@@ -1,5 +1,6 @@
 require "stringio"
 require "socket"
+require "eventmachine"

 module Ganglia
   class GMetric

が、こんなことでよいのか ruby 的なところがわからない...。問題なのはここの if 文。

  if defined?(EventMachine) and EventMachine.reactor_running?        # open an ephemereal UDP socket since
    # we do not plan on recieving any data
    conn = EM.open_datagram_socket('', 0)

    conn.send_datagram gmetric[0], host, port
    conn.send_datagram gmetric[1], host, port
    conn.close_connection_after_writing
  else
    conn = UDPSocket.new
    conn.connect(host, port)

    conn.send gmetric[0], 0
    conn.send gmetric[1], 0
    conn.close
  end

だれかなおしてー (´・ω・`)

とりあえず、ganglia にいろいろ送れるようになったけど、設定的になんかいろいろもうすこし良いデフォルトがあるかなぁ(spoof のあたりとか title とか)と思うのでもう少しいじって安定したら gem 化などやってみる。

fluentd++

Ganglia の mysqld python module の MySQL 5.5 対応パッチ

  • 投稿日:
  • by
  • カテゴリ:    

MySQL 5.5 で InnoDB status とれないまま長らく放置していた Ganglia の mysqld python module にパッチあてた。

DBUtil.py には python 2.5 未満でうごかん、みたいなことがほんのり書かれてるけど、Cent OS 5 の標準 python 2.4.3 で残念ながら動いてる(が、EPEL の python26 いれるのがいろいろ良いであろう)。ただ、log_bytes_written、log_bytes_flushed あたりは怪しい数値をだしてるけど...。

というか、だれもつかってないのかしらこのモジュール...。

Perlbal の Reproxy URL Cache metrics を Ganglia で

Perlbal の Reproxy URL Cache の統計を Ganglia に送りつけるやっつけスクリプト。

mgmt のポートにつないで、show service <service name> で Reproxy URL Cache のキャッシュ数、ヒット数、ヒット率なんかが取れる。

% echo "show service web" | nc localhost 5700
show service tp_web
SERVICE tp_web
     listening: --
          role: reverse_proxy
  pend clients: 0
  pend backend: 0
    cache size: 0/1024 (0.00%)
    cache hits: 0
cache hit rate: 0.00%
 connect-ahead: 0/0
          pool: web_pool
         nodes:
                127.0.0.1:6500              0
.

(空っぽの状態だけど)これを parse して gmetric コマンドで定期的に ganglia に送りつけるだけ。

#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long qw( :config posix_default no_ignore_case bundling auto_help );
use Pod::Usage;
use IO::Socket::INET;

GetOptions(\my %opt, qw( host=s service=s gmetric verbose ))
    or pod2usage(1);

my $sock = IO::Socket::INET->new(PeerAddr => $opt{host}, Timeout => 2, Proto => 'tcp')
    or die $!;
print $sock "show service $opt{service}\r\n";
my ($cached, $max, $hit, $rate);
while (my $line = <$sock>) {
    last if $line =~ m{^\.};
    chomp $line;
    if ($line =~ m{\s*cache size: (\d+)/(\d+) \((\d+(?:\.\d+)?)\%\)}) {
        $cached = $1;
        $max    = $2;
    }
    elsif ($line =~ m{\s*cache hits: (\d+)}) {
        $hit = $1;
    }
    elsif ($line =~ m{\s*cache hit rate: (\d+(?:\.\d+)?)\%}) {
        $rate = $1;
    }
}
die unless (defined $cached && defined $max && defined $hit && defined $rate);

if ($opt{verbose}) {
    print "$cached cached, $hit hits, $rate% hit\n";
}
if ($opt{gmetric}) {
    system 'gmetric', '--name', 'perlbal_reproxy_cache_urls', '--value', $cached, '--type', 'uint32', '--group', 'perlbal';
    system 'gmetric', '--name', 'perlbal_reproxy_cache_hits', '--value', $hit, '--type', 'uint32', '--group', 'perlbal';
    system 'gmetric', '--name', 'perlbal_reproxy_cache_hit_rate', '--value', $rate, '--type', 'float', '--group', 'perlbal';
}

__END__

=pod

=head1 NAME

reproxy_cache_stats.pl - get reproxy cache stats

=head1 SYNOPSIS

  % reproxy_cache_stats.pl --host=<host:port> --service=<name> [--gmetric] [--verbose]

=cut

というメモ。