ラベル linux の投稿を表示しています。 すべての投稿を表示
ラベル linux の投稿を表示しています。 すべての投稿を表示

2013年5月15日水曜日

logrotateってなんじゃ?(pigzで高速ローテート)

大量アクセスがあってログファイルが巨大であったりすると、日々のlogrotateが重くなります。

対象ディレクトリ以下に大量のファイルがある場合も時間がかかることがありますが、その場合はローテションの世代数を減らしたり、対象ディレクトリから定期的に退避すれば済むため、ネックになるのは主に圧縮によるものだと思います。

デフォルトではgzipが利用されますが、別の圧縮プログラムを利用することもできます。
圧縮プログラムの選定ですが、基本的に圧縮率の高いプログラムは時間がかかりますし、高速なものは圧縮率が低いものが一般的ですが、例外があります。

gzipなどはシングルコアしか使用しませんが、マルチコアで分散処理する圧縮プログラムだとおなじ圧縮フォーマットでも格段に速くなります。

今回は、その例としてpigzを利用してみます。
pigzは、gzipフォーマットの圧縮をマルチコアで行う高速gzipとして動作します。
pigzで圧縮したファイルはgunzipで解凍できるので、pigzの無い環境にもファイルを持っていくことができます。

それでは早速触ってみます。


複数のCPUがあるマシンでないと意味が無いので、コア数を確認します。
# cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7

今回はc1.xlargeですので、8コアです。


pigzをダウンロードして、ビルドします。
cd /usr/local/src/
curl -OL http://www.zlib.net/pigz/pigz-2.3.tar.gz
tar xzvf pigz-2.3.tar.gz
cd pigz-2.3/
make

cc -o pigz pigz.o yarn.o zopfli/deflate.o zopfli/blocksplitter.o zopfli/tree.o zopfli/lz77.o zopfli/cache.o zopfli/hash.o zopfli/util.o zopfli/squeeze.o zopfli/katajainen.o -lpthread -lz
zopfli/tree.o: In function `CalculateEntropy':
tree.c:(.text+0x75): undefined reference to `log'
tree.c:(.text+0x11a): undefined reference to `log'
tree.c:(.text+0x16b): undefined reference to `log'
collect2: ld はステータス 1 で終了しました
make: *** [pigz] エラー 1


なんかエラーが出ました。リンクが張られていないようなので、Makefileで-lmオプションをつけます。
6c6
<  $(CC) -o pigz $^ -lpthread -lz
---
>  $(CC) -o pigz $^ -lpthread -lz -lm




もう一度ビルドします。
# make
cc -o pigz pigz.o yarn.o zopfli/deflate.o zopfli/blocksplitter.o zopfli/tree.o zopfli/lz77.o zopfli/cache.o zopfli/hash.o zopfli/util.o zopfli/squeeze.o zopfli/katajainen.o -lpthread -lz -lm
ln -f pigz unpigz

# ls -l
合計 584
-rw-r--r-- 1  501 games   2630  5月 16 03:46 2013 Makefile
-rw-r--r-- 1 root root    2626  5月 16 03:46 2013 Makefile.org
-rw-r--r-- 1  501 games   2188  7月 29 05:37 2012 README
-rwxr-xr-x 2 root root  128382  5月 16 03:46 2013 pigz
-rw-r--r-- 1  501 games   4836  3月  4 15:24 2013 pigz.1
-rw-r--r-- 1  501 games 140762  3月  4 16:42 2013 pigz.c
-rw-r--r-- 1 root root  115496  5月 15 04:17 2013 pigz.o
-rw-r--r-- 1  501 games   8758  3月  4 15:24 2013 pigz.pdf
-rw-r--r-- 1  501 games    947  7月 29 05:37 2012 pigz.spec
-rwxr-xr-x 2 root root  128382  5月 16 03:46 2013 unpigz
-rw-r--r-- 1  501 games  11079  1月 14 07:56 2012 yarn.c
-rw-r--r-- 1  501 games   6358  1月 14 07:56 2012 yarn.h
-rw-r--r-- 1 root root   11624  5月 15 04:17 2013 yarn.o
drwxr-xr-x 2  501 games   4096  5月 15 04:17 2013 zopfli



pigzとunpigzが出力されていたら成功です。
この2つのプログラムをパスの通っているところに置きます。
cp pigz /usr/local/bin/
cp unpigz /usr/local/bin/



それでは実際に、動かしてみます。
まず1GBのファイルを作ります。
dd if=/dev/zero of=test.log bs=1024 count=1024000

# ls -lh
合計 1001M
-rw-r--r-- 1 root root 1000M  5月 15 05:02 2013 test.log




そして、gzipでの圧縮/非圧縮と、pigzでの圧縮/非圧縮で時間とCPU使用率を見てみます。
# time gzip test.log

real 0m11.084s
user 0m10.407s
sys 0m0.673s

# top
Cpu0  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  : 97.3%us,  2.7%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu4  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu5  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu6  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

------------------------------------------------------------------------------------------
# time gunzip test.log.gz

real 0m23.668s
user 0m7.032s
sys 0m1.174s

# top
Cpu0  : 83.6%us, 15.8%sy,  0.0%ni,  0.0%id,  0.3%wa,  0.0%hi,  0.0%si,  0.3%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu4  :  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu5  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu6  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

------------------------------------------------------------------------------------------
# time pigz test.log

real 0m3.025s
user 0m18.781s
sys 0m1.082s

# top
Cpu0  : 51.0%us,  0.7%sy,  0.0%ni, 45.7%id,  0.0%wa,  0.0%hi,  0.0%si,  2.6%st
Cpu1  : 47.8%us,  4.0%sy,  0.0%ni, 48.2%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  : 48.2%us, 11.0%sy,  0.0%ni, 40.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.3%st
Cpu3  : 50.8%us,  0.3%sy,  0.0%ni, 47.8%id,  0.0%wa,  0.0%hi,  0.0%si,  1.0%st
Cpu4  : 49.0%us,  3.3%sy,  0.0%ni, 47.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.7%st
Cpu5  : 50.0%us,  2.0%sy,  0.0%ni, 45.7%id,  0.0%wa,  0.0%hi,  0.0%si,  2.3%st
Cpu6  : 50.3%us,  0.7%sy,  0.0%ni, 48.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.3%st
Cpu7  : 48.3%us,  3.3%sy,  0.0%ni, 48.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.3%st

------------------------------------------------------------------------------------------
# time unpigz test.log.gz

real 0m5.298s
user 0m6.808s
sys 0m1.128s

# top
Cpu0  : 73.8%us,  3.6%sy,  0.0%ni, 21.5%id,  0.0%wa,  0.0%hi,  0.0%si,  1.1%st
Cpu1  : 48.0%us, 18.5%sy,  0.0%ni, 18.9%id, 13.0%wa,  0.0%hi,  0.0%si,  1.6%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu4  :  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu5  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu6  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st


おお、だいぶ速くなったようです。複数のCPUが使われていることも分かります。

次はこれをlogrotateで利用してみます。
コメント付きの部分が圧縮プログラムの設定になります。
全体的に適用したい場合は、/etc/logrotate.confに記述します。
# cat /etc/logrotate.d/httpd
/var/log/httpd/*log {
    daily
    missingok
    ifempty
    compresscmd /usr/local/bin/pigz               # 圧縮プログラムを指定
    uncompresscmd /usr/local/bin/unpigz       # 解凍プログラムを指定
    compressext .gz                                           # 圧縮ファイル拡張子を指定
    sharedscripts
    postrotate
        /sbin/service httpd reload > /dev/null 2>/dev/null || true
    endscript
}


httpdのログファイルを作って、時刻をローテーションの時間に合わせます。
dd if=/dev/zero of=/var/log/httpd/access_log bs=1024 count=1024000
date -s "2013/05/14 03:00:00"


しばらくすると、きちんとローテーションされていることがわかります。
# ls -l /var/log/httpd/
合計 1160
-rw-r--r-- 1 root root       0  5月 16 03:11 2013 access_log
-rw-r--r-- 1 root root 1176015  5月 16 03:11 2013 access_log-20130516.gz
-rw-r--r-- 1 root root     245  5月 16 03:11 2013 error_log
-rw-r--r-- 1 root root     294  5月 16 03:11 2013 error_log-20130516.gz


マルチコア環境に限りますが、これでローテーションが軽くなります。
以上です。


2011年11月30日水曜日

シェルってなんじゃ?(mysqlに簡単ランダムデータ生成)

DBにランダムな大量データを入れ込みたいけれども、データの内容やテーブル同士の結合などあまり気にしなくて良い場合は、ちゃんとしたジェネレーターなどをインストールするよりもシェルでさくっと書いてしまいたい時があります。

ここではmysqlにシェルからINSERTし続けるシェルを書いてみたいと思います。

例として対象となるテーブルは以下のとおり

CREATE TABLE user (
 id int(11) NOT NULL AUTO_INCREMENT,
 name varchar(50) DEFAULT NULL,
 age int(11) DEFAULT NULL,
 description text,
 created_at datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB

 ここで、idはオートインクリメント、created_atはNOW()を使用するのでいいとして、name, age, descriptionにランダムなデータを入れ込むためにランダム生成をします。


文字列のランダム

mkpasswdというパスワード生成用のランダム文字列の生成ツールで、expectパッケージに含まれています。
たとえば3文字以上の数字と5文字以上の大文字が必ず含まれる50文字のランダム文字列が生成するには以下のようにします
$ mkpasswd -l 50 -d 3 -C 5
kDVoDyxqwd5csu0bfuk8dpoygyhwwersqoazoycvehsovsMjqS


mkpasswdは厳密にランダムさを求めたり使用文字のルールを決めることができるので便利ですが、とても重いので大量に生成するには向かない場合があります。 ほかにもuuidgenというUUIDを作成するコマンドがあります。
これは全世界的に一意なIDを発行するためのツールです。
$ uuidgen
a62f9c8d-9d97-4b5d-a572-d83222837fc2


数値のランダム

数値をランダムに出力するにはexprが便利です。
たとえば1〜8までのランダム数値は以下のようにします。
$ expr 1 + $RANDOM % 8
3

これらをつかってランダムなデータをテーブルに入れるスクリプトを書いてみます。
mysqlへの投入はmysql -e "SQL文"で、コマンドから直にSQL実行できるので、それを利用します。


ランダムデータ生成スクリプト

insert_random.sh
#!/bin/sh

while ((1))
do
name=$(uuidgen | tr -d '-' | cut -c 1-10)
desc=$(mkpasswd -l 50 -s 0)
age=`expr 1 + $RANDOM % 1000`
mysql -u hoge_user cloudpack -phoge_pass -e "INSERT INTO user(name, age, description, created_at) VALUES('${name}', ${age}, '${desc}', NOW());"
echo -n "."
done;

exit 1


これを実行します。
$ sh ./insert_random.sh 
...............................................................................................................................................................................................................................................................................................................................................................................................

進捗がすすんでいるようです。
ちょっと別のターミナルセッションからデータを覗いてみます。

mysql> select * from user;
+----+------------+------+----------------------------------------------------+---------------------+
| id | name       | age  | description                                        | created_at          |
+----+------------+------+----------------------------------------------------+---------------------+
|  1 | 598d056309 |  896 | rkggeRoxaqvmhlcwhon2ordjhafdLoqdrs5ecaplouaqkccdfk | 2011-11-30 03:00:14 |
|  2 | 6ab7bf0029 |  487 | 8iOrolcltandneoxtsmwizx6shtreovoodulnmpjePdqgyrxxa | 2011-11-30 03:00:14 |
|  3 | c62df8dab5 |  464 | oxncixqdamctmeppmohmwkdayxzgw0rxRvwx3vbDhlyndqnlcv | 2011-11-30 03:00:14 |
|  4 | 63a9d01e41 |  609 | ilovfkcskrvdui2nHnumcfarlkn5iVaopabyymiafjiystbyvx | 2011-11-30 03:00:14 |
|  5 | e5abb35d2d |  677 | epkdylnwhlx9kophdOxcvcofqqheuljknsiivgon8cyovyAvaf | 2011-11-30 03:00:15 |
|  6 | 2888a4f6fd |  230 | ywqbCazysbakzweqfohli6oqhjuncohtyxplxp2Frcuhfacyqa | 2011-11-30 03:00:15 |

〜

| 2452 | 2b1e84b98d |  388 | kEheuvccvwusygulkfhig6qvnffxmnveahazmnaclcIbllcz5j | 2011-11-30 03:00:52 |
| 2453 | ea1ef37248 |   44 | feoeumwokgrvtypatzxfyfZg3ltvrnxwdEhzfveveykavitzl9 | 2011-11-30 03:00:52 |
| 2454 | 05a5341597 |  309 | chlpvjttq2axlvkrubjgubfdfhwzoatolbfddo6nqpgWgbkfPh | 2011-11-30 03:00:52 |
| 2455 | 8403fbafe9 |  655 | tdwi2egdtAucxroylarikvqdfhpxnwxeopqxtcufvqsix0Wekz | 2011-11-30 03:00:52 |
| 2456 | 471be293bc |  660 | lnKqpbueti3izbg1orceykiomjqocexezpkulqgkdhzFczyfze | 2011-11-30 03:00:52 |
| 2457 | 05628b4a7c |  180 | ueebrCqwn5brvjUqrkwcpg6qpjpkokkrtxvfczxjtcyqmrfpnf | 2011-11-30 03:00:52 |
| 2458 | 3688ea54b3 |   92 | wrpamv9QmycbccpuwqPnlsjdyiwcl5avyfgytdqllgtiljcwsc | 2011-11-30 03:00:52 |
+------+------------+------+----------------------------------------------------+---------------------+
2458 rows in set (0.01 sec)

おお、順調に投入されています。
このように、気軽に大量のデータをつくれ、また常にデータ投入が行われている状態にすることで開発の最初から負荷を気にすることもできます。
以上です。

2011年11月24日木曜日

シェルってなんじゃ?(sedとsortでアクセスログのソート)


Apacheなどのアクセスログで複数のホストの同一日付のファイルをAWSTATSなどにかけることがあります。
その場合logresolvemergeなどを利用するのですが、logresolvemergeは各ファイルが内部でソートされていることを前提としています。
ログは書きこみプロセスなどの関係で時折内部で日時が前後することがあるため、ファイルの内部をソートしなおす必要があります。

スクリプト言語などのループなどを利用してもできるのですが、ここではシェルを使った方法を紹介したいと思います。

使うコマンドは
  • cat
  • sort
  • sed
です。

たとえばログの中身が
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET / HTTP/1.1" 304 -
のようになっているファイルが数十個あるとします。

ここでは例としてhoge1.log, hoge2.logの以下の2ファイルをサンプルとして使用します。
$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -


cat
まずこれらのファイルを連結します
$ cat *
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
連結しただけなのでまだ順番は変わりません。

つぎにその出力を普通にソートしてみます。
sortコマンドには以下のようにセパレータで区切られたフィールドと範囲指定でソートキーを優先順に複数定義することができる-kオプションがあります。

以下は、区切り文字を半角スペースとして1行をフィールド分割したときの2番目のフィールドの2文字目から3文字目を第一ソートキー、1番目のフィールドを第2ソートキーとすることを表します。

$ cat test.log
b A03z 2
a A12f 3
a B03d 1

$ sort -t " " -k 2.2,2.3 -k 1,1 test.log
a B03d 1
b A03z 2
a A12f 3

これを利用して以下のログファイルをソートしてみます。

$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

$ cat * | sort -t " " -k 4.9,4.12 -k 4.5,4.7 -k 4.2,4.3 -k 4.14,4.15 -k 4.17,4.18 -k 4.20,4.21
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827

うまくいきませんでした。
これはログを出力したマシンのロケールの問題で、月のフィールドが3文字コードになっているため、文字列では大小を比較できないためです。

こういった場合のためにsortには、-Mオプションが用意されており、Jan < ... <  Decという解釈ができるようになっているのですが、残念ながらフィールドとして独立していないと機能しないようです。またそれ以外のソートキーは文字列として解釈したいので、このオプションは結局使用できませんでした。


sed
仕方ないので、Jan〜Dec を 01〜12に置換してからsortする必要があるようです。
そこでsedを利用します。

sedは文字列の置換が得意なプログラムです。
ここでは/Nov/ を /11/と置換するようにします。

$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:"
125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

sedではこのように-eオプションを連結することで、個々の置換条件を複数適用することができます。また、パラーんの最初のsの直後をセパレータとみなすので、/以外でもセパレータとして使用することができ、パターン内に/がある場合などに便利です。


sort
それではこの結果をソートにかけるようにパイプで連結してみます。
$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

うまくソートできました。

sed
これでもいいのですが、月がソート用に数字変換されたままですので、最後に元に戻してあげます。
$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20 | sed -e "s:/01/:/Jan/:" -e "s:/02/:/Feb/:" -e "s:/03/:/Mar/:" -e "s:/04/:/Apr/:" -e "s:/05/:/Apr/:" -e "s:/06/:/Jun/:" -e "s:/07/:/Jul/:" -e "s:/08/:/Aug/:" -e "s:/09/:/Sep/:" -e "s:/10/:/Oct/:" -e "s:/11/:/Nov/:" -e "s:/12/:/Dec/:"
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/Apr/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

これで完成です。


2011年10月27日木曜日

シェルってなんじゃ?(ワンライナー編1 全文検索)

日々お世話になっている便利なシェルのワンライナーを紹介していきたいと思います。
このシリーズが出たときはネタに困ったときだと思ってください。。


以下は、特定のディレクトリ以下のパターンにマッチしたすべてのファイルから全文検索し、結果を色付けするワンライナーです。

find ディレクトリ -name "パターン" -print | xargs grep "検索ワード" --color=auto

実際に実行してみます。

find ./ -name '*.txt' -print | xargs grep "pack" --color=auto
./src/git/node/CMakeLists.txt:find_package(PythonInterp 2 REQUIRED)
./src/git/node/CMakeLists.txt:# package
./src/git/node/CMakeLists.txt:include("cmake/package.cmake")
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/__init__.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/S3Uri.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/Progress.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/AccessLog.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/Utils.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/CloudFront.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/PkgInfo.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/S3.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/SortedDict.py
....

おおっ。
色もついて便利ですね。

以上です。

2011年10月13日木曜日

EC2でMySQL(世界編1 Spiderとレプリケーションで世界進出)

ふたたびSpiderの話題です。

WEBアプリを世界展開する場合、要件の1つとして、世界各国のユーザーは全てのデータを参照する必要があるが、データの登録は各国、地域の担当者が各自のデータを登録したい、というケースが多いと思います。

たとえば、商品のサイトで、EUと日本にそれぞれ管理者がいるとします。
その場合のユースケースとしては、

  • EUの管理者はEUの商品を登録する
  • 日本の管理者は日本の商品を登録する
  • ユーザーは全ての商品を閲覧できる

となります。

このとき、DBがどのリージョンにあるかによって、ユーザーや管理者の間で、レスポンススポードに差がでます。
DBを日本に置くとEUのユーザーや管理者には遅いシステムとなってしまい、EUに置くとその逆になります。
そのため、各リージョンに全てのリージョンで登録した商品データを用意し、ユーザーのロケーションに関わらず、DBアクセスをユーザーと同じリージョン内で済ませる必要があります。管理者がアクセスするDBも管理者のいるリージョン内で済ませる必要があります。




これをSpiderとレプリケーションで実現してみます。
上図をみると想像できるかも知れませんが、方法としてはEUとJPそれぞれのリージョンに属する商品マスタに対して書き込み、EUではJPの、JPはEUのスレーブをもち、それぞれをSpiderでシャーディングします。
DBの図で表すと、下記のようになります。


それでは実際に試してみます。

データノード

CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
;


Spiderノード

spider_ja
CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION ja_ja VALUES IN (1) COMMENT = 'host "111.111.0.1", port "3306"',
  PARTITION ja_eu VALUES IN (2) COMMENT = 'host "111.111.0.2", port "3306"'
)
;

spider_eu
CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION eu_ja VALUES IN (1) COMMENT = 'host "222.222.0.1", port "3306"',
  PARTITION eu_eu VALUES IN (2) COMMENT = 'host "222.222.0.2", port "3306"'
)
;


レプリケーション

ja-eu
$ mysql -u root
mysql> CHANGE MASTER TO
  MASTER_HOST='222.222.0.2',
  MASTER_USER='remote_user',
  MASTER_PASSWORD='remote_user',
  MASTER_PORT=3306;

eu-ja
$ mysql -u root
mysql> CHANGE MASTER TO
  MASTER_HOST='111.111.0.1',
  MASTER_USER='remote_user',
  MASTER_PASSWORD='remote_user',
  MASTER_PORT=3306;


※各ノード間のアクセス権限やサーバーIDの登録、セキュリティグループの設定などは以前の記事に書いたので割愛します

ポイントはregion_id(1:ja, 2:eu)のLISTパーティションにするため、プライマリキーをidとregion_idの複合キーにするところです。INSERT時には、登録したリージョンの region_idをセットします。
※登録したリージョンではないregion_idをセットするとパーティションに含まれず、シャーディング対象にならないので、注意が必要です。

spider-ja
mysql> INSERT INTO item (name, shop_id, region_id) VALUES('おいしい水', 1, 1), ('おしゃれなバッグ', 2, 1);
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0


spider-eu
mysql> insert into item(name,shop_id,region_id) values('Cool Watch', 3, 2),('Cute Ring', 4, 2);
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

すると、
それぞれのspiderノードですべてのitemがselectできます。
mysql> select * from item;
+----+--------------------------+---------+-----------+
| id | name                     | shop_id | region_id |
+----+--------------------------+---------+-----------+
|  1 | おいしい水          |       1 |         1 |
|  2 | おしゃれなバッグ |       2 |         1 |
|  1 | Cool Watch               |       3 |         2 |
|  2 | Cute Ring                |       4 |         2 |
+----+--------------------------+---------+-----------+
4 rows in set (0.01 sec)


このように、リージョンが分かれていても、アクセス速度を気にすること無くシャーディングができました。
以上です。

2011年8月26日金曜日

FTPでシンボリックリンクが表示されないってなんじゃ?

今日はいつものAWSを少し離れて、一般的なサーバーの話題を。
ftpのサービスを立ち上げて、複数のFTPユーザーに対して同じリソースにアクセスさせたい場合があります。
しかしftpサーバーソフトウェアによっては、シンボリックリンクが表示することができません。

そんなときのTipsを。


ftpサーバーをインストールして起動します。
# yum install proftpd -y
# vi /etc/proftpd.conf
--------追記↓---------
MasqueradeAddress このサーバーのグローバルIP
PassivePorts 60000 61000
--------追記↑--------
# /etc/init.d/proftpd start


そしてユーザーを作成します。
# useradd hoge
# passwd hoge


リンクを張りたいディレクトリを作成します。
# mkdir -p /var/www/html/hoge
# touch /var/www/html/hoge/hello
# chown hoge:hoge -R /var/www/html/hoge


シンボリックリンクを張ります。
# su - hoge
$ ln -s /var/www/html/hoge www


ftp接続しても、シンボリックリンクは見れません。



このような場合、以下のようにシステム内でマウントすることでアクセスできます。

$ mkdir ~/www
$ su -
# mount --bind /var/www/html/hoge www

ftp接続すると、フォルダが表示され、クリックするとリソースが表示されていることがわかります。



以上、ちょっとしたTIPSでした。