2009年3月1日

解答を書いておく -1- 課題

仕事で、ある人にスクリプト作成を依頼している。
内容的には単純なのだが、ちょっとややこしい部分があって、論理的に考えないと答が出ないようになっている。半分、演習問題のようなテーマだ。

順調に遅延しているのはいいのだが、私自身他に3件も受け持っているので、
「いざ、もう間に合いません」
と言うときになってからスクリプトを書き始めると、ちょっと間に合わなかったりする。なので、ここに模範解答の形で答を書いておこうか、と思う。いざとなったらここを参照すればよいわけだ。

他の人の演習問題にもなったらな、と思うので、問題も全部公開しよう。秘密といえるような部分は一切無い。



課題


request1


/proc/meminfo の内容を定期的に参照して欲しい。/proc/meminfo のデータは1項目一行形式になっているので、これを1行の TSV に直して欲しい。最初の一回だけ、どの項目がどこにあるのかを示すラベルもつけて欲しい。また、最初の要素に、/proc/meminfo の情報をいつ取得したのか、date の情報を "YYYY/MM/DD hh:mm:ss" の形式で保存して欲しい。

request2


/proc/meminfo には Slab という項目がある。ここの値がある閾値を超えたら、定期的に参照するその参照頻度を上げて欲しい。ようするにデータを取る間隔を短くして欲しいのだ。問題は、いったん間隔を短くしたら、二度と長くしないで欲しい、と言うことと、「閾値」は2つ、Yellow と Red があるので、それぞれに応じて間隔を短くして欲しい。また、この閾値はあとで簡単に変更できるように、スクリプトの上のほうの行で名前で宣言して欲しい。もちろん、その際の「間隔」もそれぞれ名前で宣言しておいて欲しい。

request3


Yellow の閾値を超えたら /proc/slabinfo のデータも取ってきて欲しい。こちらは、/proc/slabinfo のファイルの情報の各行の前に "# " (シャープがきて、その次に空白が1つ) をつけた形で、/proc/meminfo のデータを保存しているログファイルと同じ所に保存して欲しい。

課題の詳細


request1


/proc/meminfo を読んでくると次のようなフォーマットで値が出力される。
MemTotal:      1001008 kB
MemFree: 200708 kB
Buffers: 43400 kB
Cached: 395740 kB
SwapCached: 0 kB
Active: 557556 kB
Inactive: 178624 kB
HighTotal: 97216 kB
HighFree: 140 kB
LowTotal: 903792 kB
LowFree: 200568 kB
SwapTotal: 2096472 kB
SwapFree: 2096472 kB
Dirty: 224 kB
Writeback: 0 kB
Mapped: 356492 kB
Slab: 47820 kB
CommitLimit: 2596976 kB
Committed_AS: 689048 kB
PageTables: 6192 kB
VmallocTotal: 114680 kB
VmallocUsed: 4560 kB
VmallocChunk: 107264 kB
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB

request 1 はようするにこれを

"1001008"<tab>"200708"<tab>"43400"....

のように1行に直して欲しい、という事だ。ただし、<tab> の部分は実際にはタブ文字コード (awk とかだと "\t")で分けて欲しい。また、数字は " で囲ってもらえると助かる。

また、最初の1回だけラベルの部分も欲しい。これはつまり
"MemTotal"<tab>"MemFree"<tab>"Buffers"....

のような行を出して欲しい、と言うこと。

さらに、一番最初にデータを取得した情報が欲しい、とある。これはようするに
date +"%Y/%m/%d %H:%M:%S"

の出力を取り込んで、最初に表示して欲しい、という事だ。当然ラベル行も実際には:
"date"<tab>"MemTotal"<tab>"MemFree"<tab>"Buffers"....

でなくては困る。

request2


/proc/meminfo のSlab:の項目が 358809 未満を Green,
358809 以上 627916 未満を Yellow,
627916 以上を Red
という状態とする。これは32bit Linux Kernel のメモリ領域が 876Mbyte しかなく、それぞれ 40%、70% の所に閾値を用意した、という事だ。実際には Slab が Yellow になることすらめったに無いはずで、Red に突入したら、間違いなく oom-killer が近々発動するだろう。その傾向を見たいのだ。

状態が Green の間は 300秒(5分)間隔で request1 を実施して欲しい。
Yellow になったら 60秒(1分)間隔で request1 と request3 を実施して欲しい。
Red になったら 30秒間隔で request1 と request3 を実施して欲しい。

いったん Yellow になったら、Slab の値が改善しても Green には戻らないで欲しい。同様に Red になったら、Yellow, Green には戻ってはいけない。なので、「今回の Slab の値だけで、次にデータ取得するまでの間隔を決める」のでは困る。

request3


cat /proc/slabinfo を実施するとこんな感じの出力が得られる。
slabinfo - version: 1.1
kmem_cache 60 78 100 2 2 1
blkdev_requests 5120 5120 96 128 128 1
mnt_cache 20 40 96 1 1 1
inode_cache 7005 14792 480 1598 1849 1
dentry_cache 5469 5880 128 183 196 1
filp 726 760 96 19 19 1
buffer_head 67131 71240 96 1776 1781 1
vm_area_struct 1204 1652 64 23 28 1

これの頭に "# " をつけて
# slabinfo - version: 1.1
# kmem_cache 60 78 100 2 2 1
# blkdev_requests 5120 5120 96 128 128 1
# mnt_cache 20 40 96 1 1 1
# inode_cache 7005 14792 480 1598 1849 1
# dentry_cache 5469 5880 128 183 196 1
# filp 726 760 96 19 19 1
# buffer_head 67131 71240 96 1776 1781 1
# vm_area_struct 1204 1652 64 23 28 1

としてほしいわけだ。これは、/proc/meminfo の次の行から出力して欲しい。だから:
"2009/03/01 10:48:58"<tab>"1001008"<tab>"200708"<tab>"43400"....
# slabinfo - version: 1.1
# kmem_cache 60 78 100 2 2 1
# blkdev_requests 5120 5120 96 128 128 1
# mnt_cache 20 40 96 1 1 1
# inode_cache 7005 14792 480 1598 1849 1
# dentry_cache 5469 5880 128 183 196 1
# filp 726 760 96 19 19 1
# buffer_head 67131 71240 96 1776 1781 1
# vm_area_struct 1204 1652 64 23 28 1

:

のような出力が延々続くログファイルが欲しい、という事になる。

背景


oom-killer が発動したシステムがあった。
/var/log/messages に残る oom-killer の行動痕跡によると、slab が異常に大きい。
そこで、slab の状態を記録すると同時に、自分たちが何をやっていたのかを記録する。

slab がでかくなったタイミングで何をやっていたのか知れば、問題点がわかるに違いない。そのための監視用ログシステムが欲しい、と言うわけ。

参考資料


スクリプト言語であれば何を使っても良かったのだが、とりあえず bash と sed と awk ということなので、参考資料は次の通り。