いますぐ実践! Linuxシステム管理

いますぐ実践! Linux システム管理 / Vol.238 / 読者数:2333名

こんばんは、うすだです。

いままで、読者数が増えたときには、単細胞的に喜んでいました。

ですが、ここのところのメルマガ絡みっぽいスパムの多さを考えますと、 そう単純に喜んでいられないかもしれない、という気がしています。

1日に1人くらいの増加であれば、 多少なりとも読もうと思って登録された方がいらっしゃったのかな、 感謝感激だな、などと推測できます。

ですが、どばばばばっと急に増えると、そっち系(?)の、 あるいはオートマチックな何か(?)が登録しているのかもしれない…といった、 疑いの眼で見てしまう自分がおります。

メルマガに登録したところで、 たいした情報は得られないだろうと思ってはいるのですが、 関連する知識がごっそりないため、各所で勝手な想像をして、 疑惑を膨らませてしまっています。

というわけで、いまどきのセキュリティ関連の情報を、 一般常識的くらいには仕入れておかないといけないなあ、 と思っている今日この頃です。

無知は罪だということを再確認しつつ、今回もはりきってまいります。

今回のお題 - OOM Killer に殺されないようにする

メモリもスワップも枯渇すると、否応なくやってくる死神さんと言えば、 「OOM(Out Of Memory) Killer」さんですよね。

ですが、あるサーバで、ある大事なサービスを提供しているのであれば、 サービスそのものはもちろん、サービスの提供に必須の関連する方々も、 死神さんに kill されないようにする必要があると思います。

というわけで今回は、OOM Killer さんに殺されないようにする方法を、 さらっと紹介したいと思います。

まずは OOM Killer についてさらっと

メモリもスワップもいっぱいになって、Linuxカーネルが、 新たにメモリを割り当てられなくなったとき、OOM Killer さんが召喚されます。

OOM Killer さんは、各プロセスを評価して、 もっとも「悪い」とされるプロセスを選び、 kill します(SIGKILLシグナルを送ります)。

評価基準は、ざっくり書くと、メモリ(とスワップ)をたくさん使っているかどうか、 です。
(詳しくは、mm/oom_kill.c の oom_badness() あたりをご覧ください。)

また、各プロセスには oom_score_adj(/proc/oom_score_adj) という値があり、 これで重み付けされます。
値は -1000〜1000 のいずれかで、低いほど kill されにくくなります。
デフォルトは 0 です。-1000 だと絶対 kill されません。

お察しの通り、initプロセスやカーネルスレッドなどは対象外です。
また、 CAP_SYS_ADMIN というケーパビリティを持っているプロセスは優遇されます (前述の oom_score_adj が -30 されます)。

oom_score_adj の値を見てみましょう

具体的にどんな値が設定されているか、見てみましょう。
oom_score_adjが 0 でないプロセス名とその値を洗い出すべく、 コマンドラインで以下を実行してみました。

  $ for dir in /proc/[0-9]*; do
  >   if [ "`cat $dir/oom_score_adj`" != 0 ]; then
  >     echo "`cat $dir/comm` : `cat $dir/oom_score_adj`"
  >   fi
  > done | sort | uniq
  auditd : -1000
  chromium-browse : 100
  chromium-browse : 200
  chromium-browse : 300
  sshd : -1000
  udevd : -1000

/proc/PID/oom_score_adj の値が 0 でなければ、 comm と oom_score_adjを出力しています。また、 sort と uniq で重複をはしょっています。

auditd や udevd, sshd など、超大事なデーモンは -1000 に設定されています。 …いえ、されているのではなく、自ら設定しているようです。

  $ strings `which sshd` | grep oom
  oom_adjust
  /proc/self/oom_score_adj
  /proc/self/oom_adj
  oom_adjust_setup
  oom_adjust_restore

実際のソースコードを確認していないため、推測ですが、 oom_score_adjなどを触っているように思われます。
(ちなみに、/proc/PID/oom_adj は、古いカーネルの同様の値です。)

逆に、メモリを多用する割には重要度の低い Chromium ブラウザさんは、 0 より大きな値を設定して、対象になりやすくされているようです。

サービスの oom_score_adj を変えてみる

では、実際のサービスに対して、oom_score_adj を設定してみましょう。

たとえば、Ubuntuで、inetd に -100 を設定したい場合、 起動スクリプト「/etc/init.d/openbsd-inetd」に、以下のような細工を施します。

...前略...

# oom_score_adj を設定する関数↓を追加
set_oom_score_adj () {
    [ -f /var/run/inetd.pid ] && \
        echo -100 > /proc/`cat /var/run/inetd.pid`/oom_score_adj
}

case "$1" in
    start)
        ...中略...
        log_end_msg 0
        set_oom_score_adj  # ←これを追加
        ;;
    ...中略...
    restart)
        ...中略...
        log_end_msg 0
        set_oom_score_adj  # ←これを追加
        ;;
...後略...

start と restart のときに、oom_score_adjを設定すればいいですよね。
それぞれ同じ処理をするので、関数にしておいて、双方で呼び出します。
inetd のプロセスIDは、「/var/run/inetd.pid」に格納されているので、 それを参照して書き込めばできあがりです。

試しに、サービスを再起動して、設定されることを確認してみましょう。

  $ sudo service openbsd-inetd restart
  * Restarting internet superserver inetd                   [ OK ]
  $ cat /run/inetd.pid 
  17160
  $ cat /proc/17160/oom_score_adj 
  -100

ああ、なっていますね。よかった…。


Upstart の場合は、「oom score 数値」と書いておけば、 自動的に設定をしてくれます。たとえば、 rsyslogd を kill されないようにするには、 「/etc/init/rsyslog.conf」に以下を1行加えるだけです。

oom score -1000

restart では反映されませんでしたので、stop して start します。

  $ sudo stop rsyslog
  rsyslog stop/waiting
  $ sudo start rsyslog
  rsyslog start/running, process 17453
  $ cat /proc/17453/oom_score_adj
  -1000

ああ、あっさりと、簡単にできてしまいました。

おわりに

以上、OOM Killer さんの概要と、kill されないようにする方法を、 簡単にご紹介しました。

メモリオーバーコミットとか、cgroup との絡みとか、 もっと説明すべきことがあったかもしれません。
ですが、諸般の事情により、すっとばさせていただきました。
(諸般と書いてますが、単に時間がなかっただけです。ごめんなさい。)

メモリが潤沢な現在ですと、 OOM Killer さんが発動されることはあまりないかもしれません。ですが、 仮想な環境でちまちま動かしているケースでは、ありうるかもしれません。

そんな不測のときに備えて、最低限の知識は身につけておきましょう。
(そして、脳みそに定着させるため、ぜひ実践を!)

宿題の答え

前回の宿題は、

  カーネルパラメータの長さの上限がいくつか、調べましょう。

でした。

先に答えを言ってしまいますと、上限は 2048文字(バイト) です。
(arch/x86/include/asm/setup.h の COMMAND_LINE_SIZE です。
はい、お察しの通り、アーキテクチャによって異なります。)

…で終わるといけませんので、試してみました。
Ubuntu12.10の「/etc/default/grub」で、 「GRUB_CMDLINE_LINUX」に以下を指定してみました。 (上限を余裕でオーバーする程度の長さにします。)

  GRUB_CMDLINE_LINUX="89012345678901234567890123456789012345678901\
  2345678901234567890123456789012345678901234567890123456789012345\
  6789012345678901234567890123456789012345678901234567890123456789\
  ...延々続くので中略(2048以上の長さにしましょう)...
  3456789012345678901234567890123456789012345678901234567890123456\
  789012345678901234567890123456789012"

そして、GRUBの設定ファイルを更新します。

  $ sudo update-grub2

すると、上限を気にせず、「/boot/grub/grub.cfg」に設定されます。

  menuentry 'Ubuntu' --class ubuntu --class gnu-linux ...後略...
  ...中略...
      linux   /boot/vmlinuz-3.5.0-17-generic root=UUID=中略 ro \
  8901234567890123456789012345678901234567890123456789...後略...

そして、システムを再起動すると…普通に立ち上がりました。
ただ、確認すると、指定した超ロングなパラメータは削られていました。
(その後の quiet とか splash も、ごっそりと削られていますね。)

  $ cat /proc/cmdline
  BOOT_IMAGE=/boot/vmlinuz-3.5.0-17-generic root=UUID=中略 ro

ちなみに、長さだけでなく、パラメータ数にも上限があります。
CONFIG_INIT_ENV_ARG_LIMIT(具体的には 32)を越えると、 起動時に下記のメッセージが出力されて、パニクります。

  Too many boot init vars at `パラメータ名'

削って立ち上がるのが安全なのか、パニクるのが安全なのか、 議論の必要があるような気がしますが… なにはともあれ、お気をつけください。

今回の宿題

今回の宿題は、

  oom_score_adj の値が子プロセスに継承されるか、調べましょう。

です。

子プロセスに継承されないと、fork する度に、 自分で値を設定しないといけなくなりますので、たぶんそれは面倒だから、 継承されるのだろうなとは思っていますが、実際のところはわかりません。 未確認です。

やってみればわかることですので、試してみたいと思いま…
…いえ、試してみましょう!

あとがき

データベースを誤って消してしまった話を、立て続けに読みました。

間違って10万人の顧客DB消しちまったwwwwwwww
http://mc.matome-complate.com/archives/3673068.html
データベースを誤って初期化した人の結末
http://it.slashdot.jp/story/13/03/08/0313213/

きっと、現場では、やらかしてしまったヒトが責められているのでしょうし、 わたしが関係者なら、やらかしたヒトを責めていると思います。
(そして、わたしが実行したヒトなら、うなだれるしかないと思います。)

でも、ヒトは必ず間違いをやらかすという前提で考えられていたら、 そうならないような体制に、前もってできていたのかもしれません。

どちらのコメントも、そのような意見が多かったように思います。

ただ、事後だからそう言えるのであって、 事前にいろんなケースを考えて体制作りができるの? …という疑問も、 当然ながら思い浮かびます。

実はいま、システムの安全性や信頼性に関するお仕事に、関わってはいるのですが、 これらに関する知識が絶対的に足りていません。

もうちょっとがんばって勉強して、実際に考えられるようになると、 事前に防げるのかどうかなど、気の利いた意見が言えるようになるのかな… と思っているところです。

 

やっぱり、無知は罪ですね…。OTZ

 

今回も、ここまで読んでいただき、誠にありがとうございました。
次回は、4月7日(日) の未明にお会いしましょう!

 

「いますぐ実践! Linux システム管理」の解除は、以下からできます。
http://www.usupi.org/sysad/ (まぐまぐ ID:149633)

バックナンバーは、こちらにほぼ全部そろっています。
http://www.usupi.org/sysad/backno.html

「栗日記」- そろそろ秋に向けて準備が必要かなと思って…早すぎます?
http://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
http://usupi.org/k/ (モバイル栗日記)
http://twitter.com/kuriking/ (栗つぶやき)
http://facebook.com/kuriking3 (栗顔本)


[バックナンバーのトップへ] [Linux システム管理のトップへ]

トップ

バックナンバー
    [日付順] [目的別]

プロフィール

▼ リンク

独学Linux
Linuxデスクトップ環境に関する情報が満載です。 メルマガもありますよ。
Server World
CentOS 6をサーバとしたときの設定例が、これでもかというくらいたくさん載っています。 CentOS以外のディストリビューション(Fedora, Ubuntu)も充実しています。
LINUXで自宅サーバーを構築・導入(Fedora9)
Fedora9のインストールの仕方から管理方法まで、詳しく載っています。 SearchManには情報がもりだくさんです。
マロンくん.NET
〜サーバ管理者への道〜
Linuxをサーバとして使用するための、いろいろな設定方法が載っています。 マロンくんもかわいいです。 なんといっても、マロンくんという名前がいいですね!!
日経Linux
今や数少なくなってしまったLinuxの雑誌。ニュースやガイドもあります。
Linux Square − @IT
@ITが提供する、Linux の情報が満載。 載っていない設定方法はないんじゃないでしょうか。
gihyo.jp…技術評論社
Linuxに限らず様々な技術情報が満載のサイト。 SoftwareDesign誌も、 ソフトウェア技術者は必見です。
SourceForge.JP Magazine
Linux に限らず、オープンソース関連の記事が網羅されています。
ITmediaエンタープライズ:Linux Tips 一覧
Tips というより FAQ 集でしょうか。わからないことがあれば覗きましょう。
IBM developerWorks : Linux
開発者向けですが、勉強になりますよ。
Yahoo!ニュース - Linux
Yahoo!のLinuxに関するニュース一覧です。
栗日記
システム管理とかと全然関係ありませんが、毎日栗の絵を描いています。
システム管理につかれちゃったとき、癒されたいときに、ご覧ください。:-)
WEB RANKING - PC関連
ランキングに参加してみました。押してやってください。

▼ 作ってみました

Add to Google

▼ せんでん




▼ 最近読んだ本

▼ 気に入ってる本