2013年6月28日金曜日

cut コマンドでCSV/TSVの特定のカラム情報のみを抽出する

http://linuxjm.sourceforge.jp/html/GNU_textutils/man1/cut.1.html

CSVやTSVが手元にある時、ある特定のカラムの情報だけを抜き出したい場合、cutコマンドを使用するのが便利そうです。
$ vi hoge.csv
a,b,c
1,2,3
4,5,6
7,8,9
というCSVがあった時に、カラム"b"だけを抜き出したい場合は
$ cut -d "," -f 2 hoge.csv
b
2
5
8
という感じで抜き出せます。

"-d" はデリミタで、デフォルトはタブ区切り文字です。TSVの場合は-dは未指定でも可。
"-f"は抜き出すカラム(一番左が1)。複数抜き出す場合は"2,3"のようにカンマ区切りで複数指定すればOKです。

2013年6月25日火曜日

mac に jq(command-line JSON Prosesser) をインストールする

ここからダウンロードするのが一番ラクだということに気づいた…
http://stedolan.github.io/jq/

ダウンロード後、実行パーミッション(chmod +x )を加えればOKです。

Fluentd + out_s3 でタグごとに出力先フォルダを変更する

勿論タグごとに match エレメントを記載してベタ書きしても良いのですが、こういう際は、複数の設定を動的に行えるforest pluginが便利です。

https://github.com/tagomoris/fluent-plugin-forest

◯設定例
<match hoge.**>
    type forest
    subtype s3
    <template>
        aws_key_id [aws_key]
        aws_sec_key [secret_key]
        s3_bucket [bucket_name]
        s3_endpoint [region_name].amazonaws.com
        path fluentd_logs/${tag}/
        buffer_path /var/log/fluentd/${tag}
    </template>
</match>
データの保存先(path ならびに buffer_path)にplace holderとして${tag}を指定しています。
このように設定しておくと、"hoge.**"  のログデータについてタグごとに fluentd_logs/ 配下に保存されるようになります。

一点注意として、out_s3はfile typeのbufferを標準では使用するようになっており、いったんバッファ情報を特定フォルダ(buffer_path)に保存します。その際、buffer_path の保存先をタグごとに振り分けて置かないと、同じbufferファイルに複数タグのデータが保存され、S3保存時にも一緒くたに保存されてしまいます。


◯json内に含まれた値に応じて保存先を変更する
あらかじめ設定されたタグではなく、jsonに含まれた値に応じて保存先を変更したい場合は、rewrite_tag_filter を使用してタグを書き換える、という方法があります。
https://github.com/y-ken/fluent-plugin-rewrite-tag-filter

たとえばFluentdに流れているデータが以下のようなフォーマットになっていて、
1970-01-01T00:00:00+09:00   log.pc    {"event":"register"}
"event" に各種イベント名が含まれており、タグ名とイベント名の組み合わせごとに保存先を変更したい、という場合は以下のように設定します。
<match log.**>
    type forest
    subtype rewrite_tag_filter
    <template>
        remove_tag_prefix log
        capitalize_regex_backreference yes
        rewriterule1 event (.*) hoge.${tag}_$1
    </template>
</match>
log.pc というタグを hoge.pc_register というタグに書き換えています。
こちらと先述のout_s3の定義を組み合わせると、最終的に [bucket_name]/fluentd_logs/hoge.pc_register/ というS3のディレクトリにファイルが保存されます。


s3 の Reduced Redundancy Storage に書き込む

S3に多少のロストが許容できるようなそれほど重要でない情報を少し安く保存する、という手段として、Reduced Redundancy Storage(RSS)として書き込む、という方法があります。

以下は3年前のブログなので、改めて触れるまでも無く大多数の方はご存知でしょうが…
http://aws.typepad.com/aws/2010/05/new-amazon-s3-reduced-redundancy-storage-rrs.html

ノーマルオプション(Standard)では 99.999999999% の可用性が保証されているのに対し、RSSオプションでは 99.99% の可用性で、もし10,000ファイルをS3に置いた場合、1年の間に1つかそれ以上のファイルがロストする、という計算。ただしその分価格が33%割安、となります。
あまり長期保存しないファイルを一時的に置く、万が一ロストしても構わない、という場合RSSを選択するとかなりお得になります。


◯rubyのaws-sdkでreduced_redundancyを有効にする
http://aws.amazon.com/jp/sdkforruby/
require 'aws-sdk'

s3 = AWS::S3.new({})
bucket = s3.buckets[s3_bucket]
bucket.objects[s3path].write(Pathname.new(filepath), :reduced_redundancy => true)

◯pythonのaws-sdkでreduced_redundancyを有効にする
http://aws.amazon.com/sdkforpython/
import boto

s3 = boto.connect_s3()
bucket = s3.lookup(s3_bucket)
key = bucket.new_key(s3path)
key.set_contents_from_filename(filepath, reduced_redundancy=True)

◯Fluentdのout_s3でreduced_redundancyを有効にする。
現バージョン(2013/6/25現在)ではreduced_redundancyに対応していないので、以下のような修正を行う必要があります。
https://github.com/moaikids/fluent-plugin-s3/commit/183708f6a9a5c2eb12e8d64a9f02233c25cb3a9d

2013年6月24日月曜日

mrjob で手軽に elastic map reduce

http://pythonhosted.org/mrjob/

mrjobは、AWSのElastic MapReduceサービスを手軽に使用するためのPython実装です。
EMRのクライアントライブラリとして有名な"Amazon Elastic MapReduce Ruby Client( http://aws.amazon.com/developertools/2264 ) "と比べると出来ることは少ない印象ですが、その分シンプルに使うことができます。


◯インストール
pip install mrjob

◯jobの作成
mrjobに含まれている"MRJob" というクラスを継承することで、手軽にmapper / reducerのjob作成する事ができます。以下はmrjobのサイトにも記載されている、word countを行うサンプルのソース。
#mrjob_sample.py
from mrjob.job import MRJob


class MRWordCounter(MRJob):

    def mapper(self, key, line):
        for word in line.split():
            yield word, 1

    def reducer(self, word, occurrences):
        yield word, sum(occurrences)


if __name__ == '__main__':
    MRWordCounter.run()

◯ローカル環境で実行
ローカル環境で作成したjobの動作確認ができます。
python mrjob_sample.py test.txt > result.txt
test.txtの中身が以下のようなものだった場合
A job consists of an MRJob subclass, one or more mapper, combiner, or reducer methods, a call at the bottom to invoke mrj    ob’s logic, and all associated dependencies.
For jobs that consist only of one mapper, combiner, and reducer, in that order, you can simply override one or more of th    e mapper(), combiner(), and reducer() methods:
MapReduceの実行結果が出力されたresult.txtの中身は以下のようになります。
"A" 1
"For"   1
"MRJob" 1
"a" 1
"all"   1
"an"    1
"and"   3
"associated"    1
"at"    1
"bottom"    1
(snip.)

◯EMRの設定
EMRの環境でjobを実行するには、設定ファイルを作成する必要があります。
この中でAWSのcertificate情報や、起動するインスタンスの種類などを指定します。
以下は最低限動作させるための一例です。詳しくは公式サイトのドキュメントを参考ください。
http://pythonhosted.org/mrjob/guides/quickstart.html#configuration
#emr.conf
runners:
    emr:
        aws_access_key_id: [access_key_id]
        aws_region: [region]
        aws_secret_access_key: [secret_access_key]
        ec2_instance_type: m1.small
        num_ec2_instances: 3
        cmdenv:
        s3_log_uri: s3://[bucket_name]/emr/logs/

◯EMR環境で実行
emr環境で実行する場合は" -r emr" オプションを指定します。また、解析対象のデータのs3のパスを指定します。
python mrjob_sample.py -r emr --conf-path ./emr.conf s3://[bucket_name]/[file_path] > result.txt

◯バグ?
実は、pip installでmrjobをダウンロードした場合、上記の手順では正常にEMR上で動作させる事ができませんでした。
ローカル実行は可能ですが、EMR上で動作させる際に"returned an invalid certificate"というエラーメッセージが表示し、emrのエンドポイントへの接続が正しく行われませんでした。

同様のエラーが以下issueに報告されていますが、現在は未だ修正が行われていないようです。
https://github.com/Yelp/mrjob/issues/621

上記issueにも記載されているように、mrjobのemr.py中に記載されているEMRのendpoint URLを
    #'ap-northeast-1': 'elasticmapreduce.ap-northeast-1.amazonaws.com',
    'ap-northeast-1': 'ap-northeast-1.elasticmapreduce.amazonaws.com',
という形で書き換えてあげると、うまく動作するようになります。



mrjobはシンプルとはいえ、色々な記述方法、設定方法があり、各種管理コマンドも用意されているようなので、以降も気が向いたら記事を書いていこうと思います。




2013年6月20日木曜日

あるページがfacebookで"いいね""シェア"をされている数を取得

メモ。一番簡単なのは以下。ただし"いいね"と"シェア"の合算値なのかなんだかよく分からない"shares"という値だけが返ってくる。
curl http://graph.facebook.com/http://itsneatlife.blogspot.jp/


jetty の出力するアクセスログをカスタマイズする

かなりjetty独自の方法になっていたのでメモ。

基本的には、${JETTY_HOME}/etc/jetty-requestlog.xml を編集することで行います。
なお、以下は一般的なログ形式であるNCSAに準拠した”NCSARequestLog”を使用した例です。

  • filename : 出力するログファイル名。デフォルトでは"yyyy_mm_dd.request.log"という形で保存されdaily rotateされます。
  • filenameDateFormat : ログファイルに含める日付表現のフォーマット
  • retainDays : 何日間保存するか。デフォルトでは90日
  • extended : trueにするとcombined方式で出力されます。デフォルトはfalse(common)
  • logDateFormat : ログの日付領域の日付表現
  • logLocale : 出力ログのロケールの設定。
  • LogTimeZone : 出力ログのタイムゾーンの設定。デフォルトはGMT
他にも設定項目は多種ありますが、それらしきドキュメントに辿りつけなかったので、直接ソースやJavadocを参照するのが良いと思います。


2013年6月19日水曜日

redis2.6 + lua scripting で snowflake 風の timestamp based id を生成

https://github.com/twitter/snowflake
twitterのID生成に使用されているという噂のsnowflakeですが、こちらは導入にZooKeeperやCassandraが必要という事があり、若干敷居が高いです。

もう少しカジュアルに初められる手段として、redisと、2.6以降から機能追加されたLua Scripting機能を組み合わせて、同種のid生成処理を試してみました。


◯snowflake
snowflakeは、timestampの数値をベースとした64bit idを生成するための仕組みです。
timestamp値を基にid生成することで、以下のメリットが得られます。
  • 生成されたidから、idが生成された時刻を復元することができる。
  • 生成日時が古いidより生成日時が新しいidの方が基本的に数値が大きくなるため、idを用いた時間sortができる。

ただし、通常多くの言語処理系ではtimestampは64bitで表現されています。
また、timestamp値だけをidとして用いると、並列処理でid生成を行った際に重複が発生する可能性があります。
そのため64bitでidを表現したいのであれば、timestamp部のサイズの省サイズ化と、idとして重複を発生させない仕組みが必要です。

省サイズ化については、timestampをそのまま使うのでなく、ある一定時刻のtimestamp値(epoch)を減算することで少ないbit数で表現するようにしています。基本的に今後生成するid値は未来のtimestamp値をベースにすれば良いので、id生成を始める前の過去分の情報を削ることでサイズを節約する、というアプローチです。
実際のsnowflakeのソースでは以下のようになっています(抜粋)
val twepoch = 1288834974657L // Thu Nov 04 10:42:54 JST 2010
var timestamp = System.currentTimeMillis()
timestamp - twepoch

重複の抑制については、id情報にnode idと、各ノードが発行するsequenceという値を付与することによってある程度防いでいます。
実際のsnowfalke id のbit layoutは以下。
  • time - 41 bits (millisecond precision w/ a custom epoch gives us 69 years) 
  • configured machine id - 10 bits - gives us up to 1024 machines 
  • sequence number - 12 bits - rolls over every 4096 per machine (with protection to avoid rollover in the same ms)
unsigned な long で無くても整数値で扱える事を意図してか、実際は 63bit (41+10+12)の情報量でidの表現を行なっています。


◯redis + lua scripting でのid生成
evalコマンドによるscripting機能対応は2.6より追加された機能なので、まず2.6以降のredisをインストールする必要があります。
http://redis.io/download

なお、scripting処理はevalコマンドを用いて行えます。
http://redis.io/commands/eval

次に、redis nodeのnode id と sequence 情報を表現するための入れ物を用意します。
ここではnode idのkeyは"node_id", sequenceについては"sequence"とします。
redisを複数台用意する場合は、node_idのvalueは各redis serverごとに異なる数値を割り当てます。
$ redis-cli set sequence 1
$ redis-cli set node_id 1

続いて、id生成処理を行うlua scriptを用意します。lua内部で'sequence'はインクリメントし、自分自身のnode_idの値を取得し、timestamp - epoch の値とを組み合わせてidを生成しています。
-- id.lua
local epoch = 1288834974657
local seq = tonumber(redis.call('INCR', 'sequence')) % 4096
local node = tonumber(redis.call('GET', 'node_id')) % 1024
local time = redis.call('TIME')
local time41 = ((tonumber(time[1]) * 1000) + (tonumber(time[2]) / 1000)) - epoch
return (time41 * (2 ^ 22)) + (node * (2 ^ 12)) + seq

その上で以下のようにevalコマンドでluaスクリプトを発行すると、id値が生成されます。
$ redis-cli --eval id.lua
(integer) 347205555082385408
生成に関しては以上です。


◯運用にあたっての制約
本体のsnowflakeについても同じ事が言えますが、単一ノード内で、1ミリ秒以内に4096回以上idの生成を行うと、idが重複してしまう可能性があります。
なので、"redisノード数 × 4096回 / ms" 以上のオーダーでid生成が行われそうな場合は、逐次redisのノードを増やしていく必要があります。

また、idの重複に関しては、上記の仕組みだけでは検知できないので、生成したidを取得した側で重複チェックを行う必要があります。

また、各redisノードに設定されたnode idが万が一重複してしまうとid重複の発生確率も高くなるので、運用的に重複が起こらないようにする必要があります。

また、osの時刻が過去にずれてしまった場合、仕組み的にidの重複が起こりやすくなります。この部分についても実際は考慮が必要になると思います。

実際、一意のidを少ないビット量で表現するのにはどうしても制約がつきまとうので、運用には工夫が必要になるとは思います。
厳密にuuidを運用したい場合は、以下のRFCの記述なども参考になると思います。
http://www.ietf.org/rfc/rfc4122.txt

まあ、手軽にやる方法として、一応ご紹介いたしました、みたいな記事でした。



2013年6月18日火曜日

td-agent/fluentdのmonitor_agentを使用してbuffer使用領域の監視

fluentdの0.10.33からin_monitor_agentという標準プラグインが追加され、これによりfluentdで使用しているpluginのmetrics情報が外部から取得できるようになりました。

◯設定
<source>
  type monitor_agent
  bind 0.0.0.0
  port 24220
</source>
※参考:http://docs.fluentd.org/articles/monitoring#monitoring-agent


◯確認
上記のように設定後、以下のように24220ポート経由でAPIに接続すると、metrics情報がjson形式で取得できます。
$ curl -s http://localhost:24220/api/plugins.json | jq "."
{
  "plugins": [
    {
      "config": {
        "type": "forward"
      },
      "output_plugin": false,
      "type": "forward",
      "plugin_id": "object:3ff71e700648"
    },
    {
      "config": {
        "port": "24220",
        "type": "monitor_agent"
      },
      "output_plugin": false,
      "type": "monitor_agent",
      "plugin_id": "object:3ff71e6ffb30"
    },
(snip.)
ここで確認できるのは基本的に以下になります。

  1. 登録されているpluginの一覧
  2. 各pluginの設定情報(上記monitor_agentで言えば port:24220)
  3. buffered output pluginのmetrics

このうち、運用者としては3が特に大事になります。大量のデータを取り扱うfluentdノードでは、buffer処理をするoutput pluginでbuffer領域のqueueを使い尽くし(queue size exceeds limit)てしまうことがあります。
そちらを事前に検知して強制的にflushをしたり、使用領域サイズをグラフにプロットしたり、という事が運用上必要となり、解決方法としてはmetrics pluginを使用する、というのが良い策になります。

具体的に、out_forwardのmetrics内容を見てみると以下の様な感じになっています。
    {
      "config": {
        "type": "forward"
      },
      "retry_count": 0,
      "buffer_total_queued_size": 0,
      "buffer_queue_length": 0,
      "output_plugin": true,
      "type": "forward",
      "plugin_id": "object:3ff7206ce948"
    },
retry_count, buffer_total_queued_size, buffer_queue_lengthあたりが、ウォッチが必要な項目になりますね。


◯強制的なflush
たとえばmonitor_agentで取得したbuffer sizeの値が大きくなっていて手動でflushをしたい、といった場合、以下のようにsignalを送る事で強制flushができるようです。
pkill -USR1 -f fluentd
実際のところ、queueがあふれる理由は、トラフィックの急増やoutputの先にあるシステムの負荷高騰やキャパシティ低減が考えられるため、fluentdだけを強制的にflushしてもシステム全体のキャパシティは解決せず、解決することは少ない気もしますが、一応。


◯munin plugin
可視化側の施策の一つとして、monitor agentの値をグラフ化するmunin pluginを作成しました。
https://github.com/moaikids/munin-fluentd

muninについての説明はここでは割愛しますが、システムメトリクスのグラフ可視化ツールでは有名なものです。

上述のgithub内には以下の3種のmunin pluginが含まれています。
  • fluentd_process : プロセス数の取得
  • fluentd_resource : fluentdの使用メモリ量の取得
  • fluentd_monitor_agent : monitor_agentから取得したbuffered output pluginの各種metricsの取得

設定、使い方は、多くのmunin pluginと同様に簡単です。
  • munin ならびに munin-nodeのインストール
  • python(<= 2.6)のインストール ※munin-fluentdのプラグインはpythonで書いてるので
  • munin-nodeサーバーに、上記munin-fluentdのプラグインをインストール
  • munin-nodeの設定ファイルに設定を記述

munin-fluentdのプラグインのインストールは以下のような形で。所定のplugins置き場に、pluginのシンボリックリンクを作成します。
git clone https://github.com/moaikids/munin-fluentd.git
cp munin-fluentd/plugins/fluentd/* /usr/share/munin/plugins/
chmod +x /usr/share/munin/plugins/fluentd_*
ln -s  /usr/share/munin/plugins/fluentd_process /etc/munin/plugins/fluentd_process
ln -s  /usr/share/munin/plugins/fluentd_resource /etc/munin/plugins/fluentd_resource
ln -s  /usr/share/munin/plugins/fluentd_monitor_agent /etc/munin/plugins/fluentd_monitor_agent
また、設定ファイルに以下のような記述が必要です。
[fluentd*]
user munin
env.host localhost
env.port 24220
env.package td-agent #td-agentの場合はこちら、fluentdの場合は"fluentd"と指定
上記設定の上、munin-nodeを再起動すれば、muninのグラフに取得した数値が反映されるようになると思います。
なお、fluentd_monitor_agent では標準では三種の値(retry_count, buffer_total_queued_size, buffer_queue_length)を同一グラフで表示するようにしていますが、それぞれ数値の取りうる値の範囲に大きな差があるため正直見づらくなります。
ln -s  /usr/share/munin/plugins/fluentd_monitor_agent /etc/munin/plugins/fluentd_monitor_agent_buffer_queue_length
ln -s  /usr/share/munin/plugins/fluentd_monitor_agent /etc/munin/plugins/fluentd_monitor_agent_buffer_total_queued_size
ln -s  /usr/share/munin/plugins/fluentd_monitor_agent /etc/munin/plugins/fluentd_monitor_agent_retry_count
上記のように、シンボリックリンクのファイル名のsuffixに各種metricsの値を指定すると、その値だけをグラフ化するグラフが作成できるようになります。

グラフ化した一例は以下のような感じ。












2013年6月11日火曜日

nginxで特定URLへの接続をhttps強制にする

location /hoge {
    if($sheme = 'http'){
        rewrite ^(.*) https://$host$1 permanent;
    }
    (snip.)
}
たとえばこんな感じ。
しかしこの方法はend-to-end方式でのHTTPS接続に対応していない場合のみ有効です。
nginxの前面にロードバランサーが置いてあり、証明書をロードバランサー側に設置し、
(client) --https--> (Load Balancer)  --http--> (nginx)
のような構成になっている場合は使用出来ません。

2013年6月7日金曜日

jstackを用いてJavaのThread Dumpを取得する

kill -3 (process id)
以外の方法でも、Javaに標準で付属の"jstack"というコマンドを使用すればthread dumpの取得が行えるようです。
jstack -F (process id) > dump.log.`date +"%Y%m%d%H%M%S"` 2>&1
みたいな感じで。


AWSのpython client "boto" を用いて特定のタグのインスタンス情報だけ取得する

botoは↓
http://aws.amazon.com/jp/sdkforpython/

インストールはpipで出来ます。
pip install boto

import boto.ec2

conn = boto.ec2.connect_to_region("ap-northeast-1", aws_access_key_id=<access_key_id>,aws_secret_access_key=<secret_access_key>)
reservations = conn.get_all_instances(filters={'tag-key':'Name', 'tag-value': 'hoge'})
for reservation in reservations:
  print reservation
上記のように書くと、Nameというタグの値に'hoge'と指定されているインスタンスのみ取得します。

応用としては、fabricとの組み合わせがあります。
fabricは、pythonで作られた、複数のサーバー群に対して手軽にコマンド実行を行う事ができるツールです。

例えばec2インスタンスに”Group”といったタグを割り当て、値を設定しておき、fabric側では当該グループの情報を取得するようにしておくと、手軽に特定サーバー群へのコマンド一括実行ができますね。
こういった管理手法は、Cloud Design Patternでは”Cloud DI”と呼ぶようです。

2013年6月5日水曜日

JedisでRedis2.6のevalコマンドを使用する

JedisはJavaによるRedisクライアント実装です。
Redisは2.6よりevalコマンドによるServer Side Scriptingに対応しました。この機能は非常に便利なので使用したいのですが、現在のJedis(2013年6月5日現在)の最新版:2.1.0ではevalコマンドにバグが存在するようです。

https://github.com/xetorthio/jedis/issues/340
こちらのissueを見る限りだと2.2.0では修正予定で、master branchには既に修正が反映されているようです。
今すぐにJedisでevalコマンドを使用したい場合は、ソース一式を取得し、自分でmavenコマンドを実行し、 packageを行う必要があります。
git clone https://github.com/xetorthio/jedis.git
cd jedis
mvn package
上記の結果、targetディレクトリに"jedis-2.2.0-SNAPSHOT.jar"が作成されますので、こちらを任意の場所に置き、クラスパスを設定してあげればOKです。


なお、Jedisでのevalコマンドは以下のような感じで使用出来ます。
Jedis jedis = new Jedis("localhost");
Object ret = jedis.eval("return 'hello redis lua world.'");
System.out.println(ret == null ? "(null)" : ret.toString());
実行結果は以下。
hello redis lua world.





nginxでltsv形式のaccess.logを出力する

人間による可読性の良さと、parse処理のしやすさ、1行中の出力内容の順序にたいしてロバスト(項目の順番が変わったり追加されてもparseに失敗することは無い)という点でltsv(Labeled Tab-Separated Value)というファイルフォーマットが一時期流行りました。

もちろん一時の流行ではなく、非常に実用的な手法なので、このたびnginxの出力ログをltsv形式に変えてみました。

◯設定
naoyaさんのブログ(http://d.hatena.ne.jp/naoya/20130205/1360088927)を引用すると、以下のような形になります。
http {
    …
    log_format  ltsv  'time:$time_local\t'
                      'host:$remote_addr\t'
                      'request:$request\t'
                      'status:$status\t'
                      'size:$body_bytes_sent\t'
                      'referer:$http_referer\t'
                      'ua:$http_user_agent\t'
                      'reqtime:$request_time\t'
                      'upsttime:$upstream_response_time';

    access_log  /var/log/nginx/access.log  ltsv;   

nginxで定義されている変数を用いて、タブ区切りでフォーマットを記述していく感じですね。

個人的な環境では上記では項目が不足していたので、以下のように項目を追加しています。
  log_format  ltsv  'time:$time_local\t'
                    'msec:$msec\t'
                    'host:$remote_addr\t'
                    'forwardedfor:$http_x_forwarded_for\t'
                    'req:$request\t'
                    'method:$request_method\t'
                    'uri:$request_uri\t'
                    'status:$status\t'
                    'size:$body_bytes_sent\t'
                    'referer:$http_referer\t'
                    'ua:$http_user_agent\t'
                    'reqtime:$request_time\t'
                    'upsttime:$upstream_response_time\t'
                    'cache:$upstream_http_x_cache\t'
                    'runtime:$upstream_http_x_runtime\t'
                    'vhost:$host';

  • アクセス時間は秒単位では情報として不足しているので、milliseconds($msec)まで出力するようにした。
  • $request は "GET /hoge" のようにrequest methodとrequest pathがスペース区切りで出力されますが、後々の集計の際に一緒になっていると面倒なので、$request_methodと$request_uriをそれぞれ個別に出力するようにした。
  • reverse proxyとして動作した際に有用な情報を追加($upstream~)
あたりが追加した動機です。

なお、WEB+DB Pressの74号で「LTSVでログ活用」という記事が書かれています。こちらに、「推奨ラベル一覧」という表があるので、各項目のlabel名をどのようにするか、参考にすると良いでしょう。
http://www.amazon.co.jp/dp/4774156078


◯Fluentdで読み込み
おそらく多くの現場で、出力したログはFluentd経由で読み込み、各種metricsの可視化や、解析サーバーへの転送などを行なっていると思います。

apacheのログを読み込む際は、たとえばin_tailプラグインなどを用いて、正規表現でparseをする形になります。commonsやcombined形式であればFluentdに標準パーサーがありますが、独自の拡張が行われたログの場合は自分でparseのフォーマットを記述する必要がありました。

ltsvの場合、最新(<= 0.10.32 )のFluentdであればltsv用の標準パーサーがありますので、非常に手軽に取り込めます。
<source>
  type tail
  format ltsv
  (snip.)
</source>
出力項目が変更になってもparse処理の部分は特に気にする必要が無いので、そういう意味では楽ですね。


ltsvは若干出力サイズが冗長になると思いますが、それを補って余りあるメリットがあるので、それほど大規模でないサイトやアプリのロギング方式としては適切だと思います。



2013年6月3日月曜日

munin pluginの動作確認を行う

muninをインストールしたり、pluginを作成した際、pluginの動作確認をしたい時があります。
その際の手法を幾つか。

あらかじめ、動作確認したいpluginについては /etc/munin/plugins/ 配下に設置した上で、munin-nodeの再起動をしておく必要があります。

◯munin-run 
munin-nodeの動作する環境で、ローカルに設定されているpluginの動作確認をする場合はこちらでしょう。
$ munin-run cpu
user.value 11097897
nice.value 129809178
system.value 6680422
(snip.)
$ munin-run cpu config
graph_title CPU usage
graph_order system user nice idle iowait irq softirq
graph_args --base 1000 -r --lower-limit 0 --upper-limit 300
(snip.)

◯muninwalk
muninサーバーから、各munin-nodeがインストールされているサーバーに接続して、正しく値が収集できるか確認したいケースがあります。
その際の便利ツールとして muninwalk というperl製のツールが公開されています。
ref. http://pocketstudio.jp/log3/2012/04/13/muninwalk_and_muninget/


◯ncコマンド
muninwalkは便利なのですが、すべてのサーバーにインストールして回るのは大変なので、多くのLinux OSにインストールされているncコマンドを使用して確認するのが、個人的には楽です。
ncコマンドを実行後、コンソールで " fetch (plugin name)" と入力すると、当該pluginのメトリクスの値が取得出来ます。
$ nc munin-node 4949
# munin node at munin-node
fetch cpu
user.value 11104466
nice.value 129888815
system.value 6684441
(snip.)
munin-nodeのポートに接続出来ない場合はネットワークの設定か、接続先munin-nodeのmunin-node.confの設定に問題がある可能性があります。

pluginの値が取得出来ず"# Unknown service" という文字列が返される場合は当該pluginが正しくmunin-nodeに認識されていない可能性があります。
また"#Bad exit" と返される場合は当該pluginが正しく動作していない可能性があります。

こういう感じで、動作確認や問題の切り分けを行なっていく、という形になります。