M.C.P.C. (Mamesibori Creation Plus Communication)

印刷屋から五反田のWeb屋に転職したCLのブログです。

セマフォ不足でApacheが起動できないときセマフォをまとめて削除する

まとめ

Apacheが起動できない状態で、解放されず残っているapacheユーザのセマフォを消すには、rootユーザで

ipcs -s | awk '/apache/{print $2}' | xargs -r ipcrm sem

/apache/のところは、Apache実行ユーザに読み替えよう)

経緯

セマフォ不足でApacheが起動できなくなるっていうトラブルがありました。

前回は10か月前19時30分開始のGotanda.pm #3に参加するため19時退社して(うちは定時21時なんです)モバイルファクトリーの社内で参加中20時ぐらいに会社から電話かかってきて「Apacheが起動できませ~ん」というのでリモートで潜って対応。

今回は、アパートの立ち入り検査があるとかで有給休暇を取って全裸待機していたら朝10時位に電話がかかってきて(うちは始業は10時なんです)「Apacheが起動できませ~ん」というのでリモートで潜って対応。

セマフォ不足であることを示す直接のログは出てこず、Apacheのエラーログに

  • No space left on device: Cannot create SSLMutex
  • No space left on device: Couldn't create accept lock
  • No space left on device: mod_rewrite: could not create rewrite_log_lock

等のなんかのスペースが足りん的なエラーメッセージとして出てくるようです(監視システムなどに設定するのはこのエラーメッセージを設定することになる)。

初回のインシデントの時点で自動で復旧できるぐらい組んどけよっていう批判は甘んじて受けますがともかくセマフォ不足でApacheが起動できないというのをGoogle検索しますと、セマフォの消し方としてこういうのが出てきます。

www.geek.sc

ipcs -s | grep apache | perl -e 'while (>STDIN<) { @a=split(/\s+/); print `ipcrm sem $a[1]`}'

(ダイヤモンド演算子ってそういう書き方できるんだ)よく見るとPerlワンライナーってwhileを書かなくても書いたことにできるスイッチあったよなーと思ったのでいじってみました。

改善

ipcs -sで出てくるのは

# ipcs -s

------ セマフォ配列 --------
キー     semid      所有者  権限     nsems
0x00000000 0          root       600        1
0x00000000 32769      root       600        1
0x00000000 6619138    apache     600        1
0x00000000 6651907    apache     600        1
0x00000000 6684676    apache     600        1
0x00000000 6717445    apache     600        1
0x00000000 6750214    apache     600        1
0x00000000 3670033    root       600        1

という文字列です。Apacheセマフォ不足で立ち上がらないというのは、何らかの原因でApacheが作ったセマフォが百数十個残ったままになっているということ。何も考えずCentOS5/6をセットアップすればカーネルに設定されたデフォルト上限値として128個まで作れることになる(see: kernel.sem)。というわけで、セマフォ不足でApacheが起動できないという場合は、apacheの実行ユーザが所有者となっているセマフォipcrm sem {semid}を実行して120回ぐらい延々と消す必要がある。

要はipcs -sapacheユーザでgrepしたやつの2番目のカラムの{semid}を引数にしたipcrm semを実行させればよいわけなんで、

基本形の

ipcs -s | grep apache | perl -e 'while (>STDIN<) { @a=split(/\s+/); print `ipcrm sem $a[1]`}'

から、

  • Perl-nスイッチで標準入力1行ごとのwhileループを省略しよう
  • 暗黙の$_@_を活用しよう
  • バッククォート演算子じゃなくてsystem関数だとメッセージをprintしなくてよい

つうことで、

ipcs -s | grep apache | perl -nlE 'split(/\s+/); system "ipcrm sem $_[1]"'

としてみた。よく見ると、Perlコマンドラインオプションの-aをつけるとawkモードになって@Fに自動的にカラムが入ってくれるからsplitいらなくなるじゃないかと思って、

ipcs -s | grep apache | perl -a -nlE 'system "ipcrm sem $F[1]"'

としてみた。ところで、最近のLinuxディストリビューションだと最小構成インストールでPerlが入らないという大変なことになっていたりするので、Perl抜きで組み立てたほうがよくね?と思い、

ipcs -s | grep apache | awk '{print $2}' | xargs -l -r ipcrm sem

ここまで来ると、grepの作業ってawkでもできるよねってなって、

ipcs -s | awk '/apache/{print $2}' | xargs -l -r ipcrm sem

となる。そんで、よく考えると、ipcrmmanを見ていなかったことに気づき、参照したれば、

IPCRM(1)                   Linux Programmer’s Manual                  IPCRM(1)

NAME
       ipcrm - remove a message queue, semaphore set or shared memory id

SYNOPSIS
       ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ...

       deprecated usage

       ipcrm {shm|msg|sem} id...

とありましたので、今まで安全のために入れておいたxargs-lオプション(引数を1個だけにして、1回ずつ実行させる)を取っ払って、

ipcs -s | awk '/apache/{print $2}' | xargs -r ipcrm sem

あたりに落ち着きました。

アイキャッチ画像に使ったセマフォ写真は File:Udegishiki Tokushima.jpg - Wikimedia Commons