2015年8月16日日曜日

fluentd output plugin s3

fluentdからs3にログを残す。
s3プラグイン: http://docs.fluentd.org/articles/out_s3
以下はstoreの設定例。

    type s3 #plugin名
    aws_key_id <アクセスキー>
    aws_sec_key <シークレットキー>
    s3_bucket <バケット名>
    s3_region <リージョン名>
    format json #レコードの出力をjsonに変更
    path webapi/ #バケット配下の階層
    buffer_path /var/log/td-agent/buffer/s3_e #バッファ用ファイルのprefix
    time_slice_format %Y%m%d/%Y%m%d-%H #sliceのフォーマット
    #time_slice_wait 10m
    s3_object_key_format %{path}%{time_slice}_%{index}_%{hostname}_error.%{file_extension} #s3上のファイル名
    utc #標準時形式
    include_time_key true #出力レコードに時刻を追加
    include_tag_key true #出力レコードにタグを追加
    buffer_chunk_limit 256m #バッファファイルが256MBになったら送信
    flush_interval 60s #60秒ごとに送信
    store_as json #保存形式をgzからjsonに変更(AWS Console上で内容を確認したい)

fluentdの基本: centralized logging middle ware

ログシステムの保証レベル
at-most-once:最高で1回の送信。ただし到達を保証しない
at-least-once:少なくとも1回の送信。ただし重複の可能性がある
exactly-once:at-most-onceとat-least-onceの両方を満たす


ログ採取ミドルウェアfluentd
chmod +x install-redhat.sh
./install_redhat.sh
sudo service td-agent start //起動default port 24224


ログ/メッセージに任意のタグをつけて逐次的に収集。
フィルタ(データ加工、集計)、バッファ、ルーティングの後に各種データ出力先へ保存(Output Plugin)する。

fluentd メッセージ
[tag, time, record]

in_exec: 一定間隔ごとに指定パスにあるスクリプトをコマンド実行して、結果を収集する
out_exec_filter: tailプラグインなどで収集したログメッセージに対してデータ加工を行う
out_exec: 収集したログメッセージを指定パスにあるスクリプトの引数として渡して実行する

flush_interval: バッファリングしたメッセージを一括で受け渡しする時間間隔

tailプラグイン:既存のアプリログを収集するのに便利


fluentd設定ファイル
/etc/td-agent/td-agent.conf

有効ディレクティブ
<system> // Fluentdのコア部分の動作を決定する
<source> // プラグインの指定
<match> // ログデータの加工、フィルタリング、外部出力
include // 内部または外部の設定ファイルのロード

以下、設定ファイルの各ディレクティブの説明。[]内はデフォルト設定


system
コマンドラインの引数より優先される
log_level: [info]
suppress_repeated_stacktrace: 連続した同一エラー出力を抑制 [抑制無し]
emit_error_log_interval: 指定時間内の同一エラー出力を抑制 [抑制無し]
suppress_config_dump: 起動時に設定ファイルの標準ログ出力を抑制


source
ログの入力元を決定する。
type: Inputプラグインの指定

<source>
  type <name of plugin>
  tag <output tag>
  <option of plugin>
</source>

tailの場合、formatが必須。formatは正規表現で記述可能。例えばapachなどプリセットも用意されている。


match
ログの出力先を決定する。
type: outputプラグインの指定。フィルタリング、ルーティングも可能。

<match <マッチさせるタグ>>
  type <プラグイン名>
  <プラグインのオプション>
</match>

一度受信したtagをルーティングして、vhostのドメイン部分をタグに含めて再度ルーティングすると、vhostの情報を含むログを出力できる。
outputのプラグインには、fileやs3、mongo、stdoutなどがある。
ルーティング目的では、copy、rewrite_tag_filterなどがある。
一旦matchして出力してしまうと以降のmatchでは引っかからないので、copyなどを利用してうまく設定することが必要。


マッチングタグの記述
*: 1つのタグ要素に合致
**: 0個以上のtag要素に合致
{x, y, z}: x, y, zにマッチ。x, y, zはマッチしたいパターン


fluentdテスト用コマンド
fluent-cat: td-agent付属のコマンド

// 標準出力
echo '{"message":"Hello World."}' | /usr/lib64/fluent/ruby/bin/fluent-cat debug.test // fluent-catにはタグ名を渡す

// ログファイルの参照
sudo less /var/log/td-agent/td-agent.log


example) apache

ログファイルの権限などに注意。間違っている場合td-agent.logにエラーが出力されるので容易にわかる。

2015年8月9日日曜日

Lispによる関数型プログラミング入門2 記法

Lispの記法

・前置記法
1 + 2
→ 
 (+ 1 2)

つまり、関数(ここでは和演算)を最初に宣言して、引数を渡す。これはf(x)と記述するのと同じである。

・defun
means define-function

例)
フィボナッチ数計算:
> (defun fib(n)
>   (if (<= n 1)
>     1
>     (+ (fib (- n 1)) (fib(- n 2)))
>   )
> )

> (fib 5)
 8
> (fib 21)
 17711


Lispの評価と特殊形式quote、function

・quote
関数を評価せずにそのままかえす

例)
> (quote (1 2 3))
 (1 2 3)

・function
シンボルの関数をかえす特殊関数
略記 #'

例)
> (function +)

#<SYSTEM-FUNCTION +>

2015年8月3日月曜日

Lispによる関数型プログラミング入門

SoftwareDesignの2015Aug を読んでLispに興味をもったので、何回かにわけて入門のメモを残す。

Lispの前に、関数型プログラミングの特徴から。

関数型プログラミング

グローバル変数や代入文など副作用を生じる機能を使わずにプログラミングを行う。

・従来の考え方
上記のように変数や配列などの状態をループなどを用いて変更し結果を得る

・関数型プログラミングの考え方
関数(主に高階関数)を利用して副作用無しに結果を得る

・関数型プログラミング向きの問題
状態をもたない再帰的な問題
→探索、整列

・人間には再帰的思考が向いている
数学的帰納法(具体的で小さい問題から大きな全体の問題へ向かう)

・並列処理に対する強み
状態を持たないので、並列処理を自然に(というのは同期や排他が必要無い)できる。
クラウドやビックデータ向きである。

・関数呼び出しに対する弱み
関数呼び出しはコストが大きいので、低機能コンピュータには向いていない。高機能コンピュータ上で並列処理するのに向いている。

・動的なプログラミングに対する強み
関数自身をオブジェクトとして扱えるため、動的な関数生成や変更をすることができる。(サービス指向コンピューティング)

・抽象的なプログラミングに対する強み
うまく作れれば、状態をもたないので仕様変更に柔軟であるはず。

・バグの現象
副作用が少ないので、バグの温床がつくられることを防げる。

Lisp
・言語仕様が小さい
・インタプリタ言語であり、実行が簡単
・GC、ラムダなど最近の言語では常識であるアイデアの元となった言語
・動的型付け(型推論ではない)
・かっこが多くて見難い
・前置記法が見難い
・動的型付けはリソースを食う

数学的帰納法だと考えるとわかりやすい
・再帰をみつける = 同じことの繰り返しをみつける。

例)
階乗:
・関数型
 →5(N)の階乗は4(N-1)の階乗の結果に5を掛けること
・手続き型
 →1 x 2 x 3 x 4 x 5

Lispのインストール(OSX)

$ brew intall clisp
$ clisp [~]
i i i i i i i ooooo o ooooooo ooooo ooooo
I I I I I I I 8 8 8 8 8 o 8 8
I \ `+' / I 8 8 8 8 8 8
\ `-+-' / 8 8 8 ooooo 8oooo
`-__|__-' 8 8 8 8 8
| 8 o 8 8 o 8 8
------+------ ooooo 8oooooo ooo8ooo ooooo 8
Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>
Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010
Type :h and hit Enter for context help.
[1]>
view raw list_install.sh hosted with ❤ by GitHub
次回はLispの記法から。

2015年6月28日日曜日

アルゴリズムプログラム 積

アルゴリズムをプログラムする方法を学ぶ。

積を計算することは、現代では0位取り表記によって大変容易になったが、古代エジプトなどでは専門性を求められるものであった。

積は和を複数回繰り返すことによって実現できる。コンピュータではビット演算を組み合わせることによってより効率的に積を計算することができる。

以下は、アーメスのアルゴリズムとその改善版を記述したコードである。
コードが最適化されているか常に思考し、よりよいコードに変換する作業を繰り返すことが大切だとわかる。


//
// multiplication.cpp
// CplusplusPractice
//
// Created by masai on 2015/06/27.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
// xの最上位ビットを評価する(奇数判定する)
bool odd(int n){ return n & 0x1; }
// xを右へ1つシフトする(1/2する)
int half(int n){ return n >> 1; }
// 乗法1 a + a + ... + a
int multiply0(int n, int a){
if(n == 1) return a;
return multiply0(n - 1, a) + a;
}
// 乗法2 a + a + a .. + a = (a + a) + (a + a) + ... + a
int multiply1(int n, int a){
if (n == 1) return a;
// a + a + a + a = (a + a) + (a + a)
int result = multiply1(half(n), a + a);
// (a + a) + (a + a) + (a + a) (nの2進数表現における1の数(population count) - 1 だけ呼ばれる)
if(odd(n)){
std::cout << "plus" << std::endl;
result = result + a;
}
return result;
}
// おまけ: 16進数表現
void output16(int a){
printf("a = 0x%x", a);
}
// 15倍の加法連鎖
int multiply_by_15(int a){
int b = (a + a) + a;
int c = b + b;
return (c + c) + b;
}
// 乗累算1(関数呼び出しを減らす)
int multi_acc0(int r, int n, int a){
if(n == 1) return r + a;
if(odd(n)){
return multi_acc0(r + a, half(n), a + a);
}else{
return multi_acc0(r, half(n), a + a);
}
}
// 乗累算2(記述を単純化する)このような状態を末尾再帰(tail-recursive)という
int multi_acc1(int r, int n, int a){
if(n ==1)return r + a;
if(odd(n)) r = r + a;
return multi_acc1(r, half(n), a + a);
}
// 乗累算3 比較回数の削減(比較の順序によって計算回数が減るケースがある)
int multi_acc2(int r, int n, int a){
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
return multi_acc2(r, half(n), a + a);
}
// 乗累算4 正確なtail-recursive → 再帰から繰り返し構造に置き換えることが簡単
int multi_acc3(int r, int n, int a){
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
n = half(n);
a = a + a;
return multi_acc3(r, n, a);
}
// 乗累算5 再帰から繰り返し構造への置き換え
int multi_acc4(int r, int n, int a){
while(true)
{
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
n = half(n);
a = a + a;
}
}
// ヘルパー1
int multiply2(int n, int a){
if(n == 1) return a;
return multi_acc4(a, n - 1, a);
}
// ヘルパー2
int multiply3(int n, int a){
// nが奇数になるまで、1/2を繰り返す
while(!odd(n)){
a = a + a;
n = half(n);
}
return multiply2(n, a);
}
// ヘルパー3
int multiply4(int n, int a){
// nが奇数になるまで、1/2を繰り返す
while(!odd(n)){
a = a + a;
n = half(n);
}
if(n == 1) return a;
// かならずnが奇数で呼び出すので奇数のときに行われる処理を事前に行っておく
return multi_acc4(a, half(n - 1), a + a);
}
int main(int argc, char* argv[]){
std::cout << "multiplication." << std::endl;
// 和の再帰で積を表現
std::cout << "multipliycation = add." << std::endl;
std::cout << multiply0(3, 2) << std::endl;
// 奇数
std::cout << "odd" << std::endl;
std::cout << odd(13) << std::endl;
// 偶数
std::cout << "even" << std::endl;
std::cout << odd(12) << std::endl;
// 1/2
std::cout << "1/2" << std::endl;
std::cout << half(5) << std::endl;
// 積(アーメスのアルゴリズム)
std::cout << "egyption multiplycation." << std::endl;
std::cout << multiply1(6, 2) << std::endl;
// おまけ: 16進表現
output16(100);
// 15の加法連鎖
std::cout << "multipy by 15." << std::endl;
std::cout << multiply_by_15(30) << std::endl;
// 乗累算
std::cout << multi_acc1(0, 5, 100) << std::endl;
std::cout << "optimize multipy." << std::endl;
std::cout << multi_acc2(0, 5, 100) << std::endl;
std::cout << multi_acc3(0, 5, 100) << std::endl;
std::cout << multi_acc4(0, 5, 100) << std::endl;
// ヘルパー呼び出し
std::cout << multiply2(5, 100) << std::endl;
std::cout << multiply3(5, 100) << std::endl;
std::cout << multiply4(5, 100) << std::endl;
return 0;
}

2015年6月21日日曜日

vagrantとansibleを使ってdocker開発環境をつくる

vagrantは開発環境用のVM構築と共有を簡単に行うためのVMラッパーツールであるが、ansibleを利用して開発環境の設定(プロビジョニング)も繰り返しを避けることができる。

vagrantは、プロビジョニング用の設定をVagrantfileに記述することが可能である。
今回は、プロビジョニングにansibleを利用する。ansibleはplaybookと呼ばれるyaml形式の設定ファイルを参照する。

Vagrantfileには以下のように追記する。ansibleはplaybook.ymlを参照する。

config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
end
view raw Vagrangfile.txt hosted with ❤ by GitHub
ansibleに使用するplaybookを作成する。

---
- hosts: all
tasks:
- name: install latest docker
shell: curl -sSL https://get.docker.com/ubuntu/ | sudo sh
- name: add vagrant user to docker group
command: gpasswd -a vagrant docker
sudo: yes
view raw playbook.yml hosted with ❤ by GitHub
Vagrantfileと同じディレクトリにplaybook.ymlを入れておく。
vagrant up で playbookを利用してプロビジョニングできる。既に vagrant up 済みのVMは以下のように明示的にプロビジョニングする必要がある。

#vagrant provision [~/vagrant/ubunt1404]
==> default: Running provisioner: ansible...
PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/Users/masai/vagrant/ubunt1404/.vagrant/machines/default/virtualbox/private_key --user=vagrant --connection=ssh --limit='default' --inventory-file=/Users/masai/vagrant/ubunt1404/.vagrant/provisioners/ansible/inventory playbook.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [default]
TASK: [install latest docker] *************************************************
changed: [default]
TASK: [add vagrant user to docker group] **************************************
changed: [default]
PLAY RECAP ********************************************************************
default : ok=3 changed=2 unreachable=0 failed=0

2015年6月19日金曜日

Pythonの軽量WEBフレームワークBottle (WebAPIを手軽に記述)

BottleはPythonの軽量WEBフレームワークである。テスト用に素早くAPIを公開したいなと考えたところ、ピッタリだと思った。

インストール
pipでインストールできる。
pip install bottle

サンプルコード api.py
以下の様な短いコードでWeb APIが動作する。

from bottle import route, run
# アノテーションでURLを記述
@route('/deepapi/json/:name')
def hello(name):
return '<h1>Deep %s!</h1>' % name
# オプションをつけるとデバッグ実行
run(host='masai-no-MacBook-Pro.local', port=8080, debug=True, reloader=True)
#run(host='localhost', port=8080)


起動
python api.py
めちゃくちゃ楽できる・・・。今まで使わなかったことを後悔するくらいだ。