天運は我に無いけど原因はinodeにあった

nginxとかmod_disk_cacheとかでまぁ何でも良いんですが、キャッシュファイル置き場を/dev/shmにしている時に

No space left on device

ってメッセージがエラーログに出だしたらこれから先を読めば解決!!原因はinode溢れに有り。


dh -ihと叩けば恐らく100%近くになっている筈です。
↓こんな感じになっている事かと思われますよ。泣けますねぇ。

$ df -ih /dev/shm
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
tmpfs                   129K    129K       1  100% /dev/shm

※この場合13万強ファイル置いたら死亡ですね。

という訳でファイル消す訳でもなく分割するでもなくinodeの上限増やしてしまえ!というのがこれから↓です。

※作業前にバックアップは忘れずにね!

cp -ip /etc/fstab /etc/fstab.`date +%Y%m%d`

/etc/fstabの中に↓の行が有るかと思うんですが、

tmpfs                   /dev/shm                tmpfs   defaults        0 0

こいつのdefaultの後にnr_inodesって書いて増やしたい分書いてやればOKです。(0って書くと無限になるみたいです。)
↓256kにする場合はこう書いてぇ。

tmpfs                   /dev/shm                tmpfs   defaults,nr_inodes=256k        0 0

そしてmountし直し。増えた!!

# umount /dev/shm
# mount /dev/shm
# df -ih /dev/shm/
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
tmpfs                   256K       1    256K    1% /dev/shm

nginxとかapacheで使っている場合は握っててumount出来ないので

service nginx stop
umount /dev/shm
mount /dev/shm
service nginx start

の流れでやりましょう。

ちなみに/dev/shmの中umountした時点で全部綺麗になっちゃうから注意してね!

ソースインストールvimで日本語が文字化け

何でこんな事になってしまったんだろう・・・。自分はもっと出来る男だと思い込んでた・・・。

何かと言いますとvimをソースからインストールしたんですがどうにもこうにも日本語表示がうまくいかなくて3時間悩みました。
http://www.vim.org/download.php#unix

# ./configure
# make
# make install

なんでーなんで日本語化けるのーなんでなんでぇ。サーバのロケールはutf8でLANGもあってるし.vimrcにもちゃんとfileencodinfsとか書いてるしなんでぇ!という所で、そういえばオレrpmしか入れた事無かったという事に気づきまして・・・。

頭を冷やしてconfigureのオプションをじっくり見てみるとそれらしいのがやっぱり有りました。

# ./configure --help | grep -2 multi
  --disable-netbeans      Disable NetBeans integration support.
  --enable-sniff          Include Sniff interface.
  --enable-multibyte      Include multibyte editing support.
  --enable-hangulinput    Include Hangul input support.
  --enable-xim            Include XIM input support.

そう!マルチバイト!

という事で↓で無事化けなくなりましとさ。

# ./configure --enable-multibyte
# make
# make install

HadoopでShuffle Error

reduce処理で「Shuffle Error: Exceeded MAX_FAILED_UNIQUE_FETCHES; bailing-out.」と出て戦ったのでメモ。
原因としては各ノードのホスト名(と対応するIPアドレス)が127.0.0.1で通信不能だったため。

↓のようなNICを複数持っている構成の場合(ホスト名=127.0.0.1とhostsに書いてました)

NameNode
ホスト:namenode01.matsumo(127.0.0.1)
eth0:192.168.1.1 # 外向けセグメント(hostsにnamenode01.matsumo.sotoと登録)
eth1:192.168.2.1 # 中向けセグメント(hostsにnamenode01.matsumo.nakaと登録)

DataNode
ホスト:datanode01.matsumo(127.0.0.1)
eth0:192.168.1.2 # 外向けセグメント(hostsにdatanode01.matsumo.sotoと登録)
eth1:192.168.2.2 # 中向けセグメント(hostsにdatanode01.matsumo.nakaと登録)

Hadoopのconf/slaveには中向け通信なので

datanode01.matsumo.naka

と書いておきます。よっしゃよっしゃ(とこの時は思っていた)

するとあらら・・・Reduce失敗。
↓のメッセージがログに出てました。
「Shuffle Error: Exceeded MAX_FAILED_UNIQUE_FETCHES; bailing-out.」

半日くらい戦ったんで結論を勿体ぶりたいですが、端的に言うとnamenode01.matsumo(サーバのホスト名)で名前解決出来てかつ通信出来ないといけないという事みたいです。
内部の何処で何をやってるかは正直分かりませんが・・・

という訳でホスト名と対応するIPアドレスを中向けセグメントに直したら無事動きました

127.0.0.1 namenode01 namenode01.matsumo  localhost localhost.localdomain
↓
127.0.0.1 localhost localhost.localdomain
192.168.2.1 namenode01 namenode01.matsumo namenode01.matsumo.naka

中々それっぽいページが見つからなかったので書いておきます。

dateの前とか後とかのオプションのまとめ

何回やっても覚えないというか都度ぐぐる破目になっているのでここにまとめます!
期間は秒から年まで!過去ago!未来は何も無し!

#!/bin/bash

echo "----------------------"
echo "過去シリーズ"
echo "----------------------"

echo ""
date '+%Y/%m/%d %H:%M:%S'

echo "去年"
date -d '1 year ago' '+%Y/%m/%d %H:%M:%S'

echo "先月"
date -d '1 month ago' '+%Y/%m/%d %H:%M:%S'

echo "昨日"
date -d '1 day ago' '+%Y/%m/%d %H:%M:%S'

echo "1時間前"
date -d '1 hour ago' '+%Y/%m/%d %H:%M:%S'

echo "1分前"
date -d '1 minutes ago' '+%Y/%m/%d %H:%M:%S'

echo "1秒前"
date -d '1 second ago' '+%Y/%m/%d %H:%M:%S'

echo "----------------------"
echo "未来シリーズ"
echo "----------------------"

echo ""
date '+%Y/%m/%d %H:%M:%S'

echo "来年"
date -d '1 year' '+%Y/%m/%d %H:%M:%S'

echo "来月"
date -d '1 month' '+%Y/%m/%d %H:%M:%S'

echo "明日"
date -d '1 day' '+%Y/%m/%d %H:%M:%S'

echo "1時間後"
date -d '1 hour' '+%Y/%m/%d %H:%M:%S'

echo "1分後"
date -d '1 minutes' '+%Y/%m/%d %H:%M:%S'

echo "1秒後"
date -d '1 second' '+%Y/%m/%d %H:%M:%S'

実行結果は以下の通り!何だかやってる内に覚えちゃった気がする。

$ bash date.sh
----------------------
過去シリーズ
----------------------
今
2011/10/10 21:27:44
去年
2010/10/10 21:27:44
先月
2011/09/10 21:27:44
昨日
2011/10/09 21:27:44
1時間前
2011/10/10 20:27:44
1分前
2011/10/10 21:26:44
1秒前
2011/10/10 21:27:43
----------------------
未来シリーズ
----------------------
今
2011/10/10 21:27:44
来年
2012/10/10 21:27:44
来月
2011/11/10 21:27:44
明日
2011/10/11 21:27:44
1時間後
2011/10/10 22:27:44
1分後
2011/10/10 21:28:44
1秒後
2011/10/10 21:27:45

思います。考えています。推測されます。恐らく。の抜け道

不規則に発生するバグに当たったり、何だか知らねぇけどロードアベレージが吹きそうな値になったり。昨日まで快適に動いてたSQLが今日になって鼻血出そうなくらい劣化したり・・・。

とか働いてると色々有るかと思いますが、その時点では原因は不明瞭にも関わらず相手に伝えなきゃいけない時ってありますよね。
とはいえ断言出来ないので↓のような文面に陥りがちだと思います。

リリース後暫くは大丈夫だったと思うのですが、
今日の午前中くらいに突然負荷が急騰しだしました。
MySQLが怪しいとは思うのですが恐らく何も変更を加えていないと考えられますので
MySQL以外となるとApacheの方だと推測されます。

思う。考えられる。推測されます。恐らく。ハァハァだってまだ何も分かってないんだもん!!
という気持ちは分かりますが伝える相手は恐らくもっと分かってません。
ので冒頭に全て推測です!!と断言してしまうとするっと抜けれそうだと。

以下、現時点で確認出来ている事象と要因となります。
午前中は安定稼動。
MySQLに構成変更無。
Apacheに主要因有り。

力強く言い切ってますが万が一MySQLに誰か爆発物仕込んでてもその時点では分かってなかったから良いもんねー。えへへへへ。
なんてぇ事を考えてみました。小さい事ですがするりするりと生き抜くためにこういうテクニックは身につけていきたいものです。

まぁ勿論、時と場合と相手によりますが・・・ね。

findのemptyとdeleteが便利

4年前に「findのexecが便利」なんて事を書きましたが・・・。
http://d.hatena.ne.jp/yumatsumo/20070516

成長したのかしてないのかまた感涙もののfindのオプションを発見しました。
今回はemptyとdeleteが便利!というお話です。


きっかけ

mod_disk_cacheのhtcachecleanがどうも粛清が緩くてinode溢れに悩んでいた訳です。

もう頼ってられない、自分でやろう!粛清じゃー!という訳で以下の2つのルールでやろうと思ってはまりました。
・更新日がCacheMaxExpireを超えたファイルは削除
・空のディレクトリは削除

で、更新日はmtimeでほげほげで良いんですが、rmdirで空ファイル削除しようと思ったんですが中身にファイルが残ってるとステータスがエラーとなってしまい健全性が確認できません。

$ find ./ -type d | xargs rmdir
rmdir: ./: Invalid argument
rmdir: ./test3: Directory not empty
$ echo $?
123

さてさてと悩んでいるとemptyとdeleteという素晴らしいオプションがmanに書いてありました。


このように便利です

↓が検証用のファイル・ディレクトリ構成です。(test1,2は空ディレクトリtest3配下にはファイルが有ります。)

$ mkdir test1 test2 test3; echo "foo" > test3/foo
$ tree
.
|-- test1
|-- test2
`-- test3
    `-- foo

3 directories, 1 file

emptyオプションエイヤ!で空ディレクトリが引っかかりました。

$ find ./test* -type d -empty
./test1
./test2

そしてdeleteオプションをつけると空ディレクトリが削除されました!!おっけーい。

$ find ./test* -type d -empty -delete
$ tree
.
`-- test3
    `-- foo

1 directory, 1 file

rmdirと似たような動作で中にファイルが有る場合は削除されませんでした。(メッセージも同じです。)

$ find ./test* -type d -delete
find: cannot delete ./test3: Directory not empty
$ rmdir test3
rmdir: test3: Directory not empty


ちなみにちなみに

ファイル指定の場合は空ファイルだけが引っかかりました。ファイル指定時のdeleteオプションはファイルサイズに関わらず↓の例だと全部削除されましたのご注意を!!

$ touch empty_file
$ echo "aaa" > aaa_file
$ find ./ -type f -empty
./empty_file

これで快適なrmdirライフを満喫できます!!

プロセスのCoW共有しているメモリのサイズ

もはや伝説と化した記事がありますが、
http://d.hatena.ne.jp/naoya/20080212/1202830671

諸々の都合でLinux::Smaps入れると商用と開発入れなきゃいけないし、
管理上一台だけ入れるって訳にも行かないし、閉ざされた世界なのでcpanminiこしらえなきゃ・・・
とかお悩みのオレのために/proc/PID/smapsをそのままコリコリ読むスクリプト書いてみました。

つまりLinux::Smaps入れないでもOKです。(List::Utilは5.8以降だとコアモジュールなので使ってます。)

ペロッと貼ってご利用ください。

#!/bin/env perl

use strict;
use warnings;

use List::Util ();

@ARGV or die "usage: %0 [pid ...]";

my @output;

for my $pid (@ARGV) {
   die "invalid pid '$pid'" if $pid =~ /\D/;
   my @smaps = `cat /proc/$pid/smaps`;
   die if $? != 0;
   my @shared = map { /(\d+)\s+kB/; $1 } grep { /^Shared_(Clean|Dirty)/ } @smaps;
   my $shared_total = List::Util::sum(@shared);
   my @rss = map { /(\d+)\s+kB/; $1 } grep { /^Rss/ } @smaps;
   my $rss_total = List::Util::sum(@rss);
   my $parcent = sprintf '(%d %%)', int(($shared_total / $rss_total) * 100);
   push @output, [$pid, $rss_total, $parcent];
}

unshift @output, [qw(PID RSS SHARED)];

for my $out (@output) {
    print join "\t", @$out;
    print "\n";
}

使い方もこんな感じで一緒です。

# perl shared_memory_size.pl  `pgrep httpd$`
$ sudo perl shared_memory_size.pl  `pgrep httpd$`

お悩みの諸兄!一人じゃない!けど大勢でもない!

ちなみにオレオレサーバ・・・。もうちょっと頑張れそうですね。

# perl shared_memory_size.pl  `pgrep httpd$`
PID     RSS     SHARED
5273    46800   (65 %)
5297    47208   (65 %)
5325    47184   (65 %)
5341    2912    (76 %)
5359    46052   (67 %)
5436    3060    (77 %)
5444    2932    (76 %)
5450    2884    (82 %)
5455    45464   (69 %)
5458    44584   (72 %)
5467    2892    (77 %)
5469    2896    (77 %)
5471    2860    (78 %)
5472    3112    (76 %)
5477    2708    (83 %)
5486    2724    (81 %)
5487    2716    (81 %)
5490    2832    (77 %)
5491    2756    (84 %)
5492    2260    (90 %)
5493    46420   (70 %)
5495    2748    (79 %)
5498    2204    (90 %)
5499    2196    (90 %)
5500    2200    (91 %)
5501    2196    (91 %)
25394   41824   (77 %)
25406   2292    (89 %)