Hatena::ブログ(Diary)

(ひ)メモ このページをアンテナに追加 RSSフィード

2009-09-30 (Wed)

Linuxでmysqldがcoreをお吐きになりませぬ

以下、Linux kernel 2.6 でのお話です。

  • vanilla な Linux kernel 2.6.29.1
  • MySQL 5.1.35とかそのへん

ぼくが改めて言うまでもなく、プログラム(mysqld)が異常終了したときなどに吐かれるcoreファイルは、その原因を探る上で非常に有益です。

が、なぜか、

  • setrlimitのRLIMIT_COREはunlimitedになっている
  • my.cnfの[mysqld]セクションに「core-file」を指定している

にも関わらず、自分の環境のmysqldはcoreを吐いてくれませんでした。

結論から言うと、

# MYSQL_DIR=/usr/local/app/mysql

# ( sleep 10 && pkill -ABRT mysqld ) &
# $MYSQL_DIR/bin/mysqld \
    --basedir=$MYSQL_DIR \
    --datadir=/db/mysql \
    --pid-file=/db/var/mysql.pid \
    --user mysql \
    ;
Aborted
# ls ~mysql/core*
ls: cannot access /db/mysql/core*: No such file or directory

# ( sleep 10 && pkill -ABRT mysqld ) &
# sudo -u mysql $MYSQL_DIR/bin/mysqld \
    --basedir=$MYSQL_DIR \
    --datadir=/db/mysql \
    --pid-file=/db/var/mysql.pid \
    ;
Aborted (core dumped)
# ls ~mysql/core*
/db/mysql/core.1254295158_mysqld_3367

の様に、実効ユーザの指定にmysqldの--userオプションを使った場合はcoreができませんが、sudo (やdaemontoolsに含まれるsetuidgidでもOK) を使った場合は期待通りcoreができます。


さて、--userの場合にcoreができない原因はなんでしょうか。

MySQLのドキュメントに依れば、

On some systems, such as Solaris, you do not get a core file if you are also using the --user option.

no title

だそうなのですが、「Linux」は明言されていないのでmysqlのソースを覗いてみます。

ながーいmain関数の中にこのようなコードがあり、check_user()の後でset_user()を呼んでいます。

  if ((user_info= check_user(mysqld_user)))
  {
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
    if (locked_in_memory) // getuid() == 0 here
      set_effective_user(user_info);
    else
#endif
      set_user(mysqld_user, user_info);
  }

続いて、check_user()とset_user()を引用します。

static struct passwd *check_user(const char *user)
{
(snip)
    if (*pos)                                   // Not numeric id
      goto err;
    if (!(tmp_user_info= getpwuid(atoi(user))))
      goto err;
  }
  return tmp_user_info;
  /* purecov: end */

err:
  sql_print_error("Fatal error: Can't change to run as user '%s' ;  Please check that the user exists!\n",user);
  unireg_abort(1);

#ifdef PR_SET_DUMPABLE
  if (test_flags & TEST_CORE_ON_SIGNAL)
  {
    /* inform kernel that process is dumpable */
    (void) prctl(PR_SET_DUMPABLE, 1);
  }
#endif

#endif
  return NULL;
}
static void set_user(const char *user, struct passwd *user_info_arg)
{
(snip)
  if (setgid(user_info_arg->pw_gid) == -1)
(snip)
  if (setuid(user_info_arg->pw_uid) == -1)
(snip)
}

Linux の場合、setuidやsetgidなどで実効uidやgidが操作された場合、prctl(2)でPR_SET_DUMPABLEしないとcoreは吐かれません。

ここで前掲のコードを見ると、prctlを呼んでいるcheck_user()を実行した後、setgid,setuidを呼んでいるset_user()を実行する流れになっています。

prctlは、setuidしたで呼ばないと意味がないので、これではcoreは吐かれませんね。

もうひとつ興味深いのは、check_userでprctlが呼ばれるのは、何かしらの処理が失敗してerrラベルへ飛ばされたときのみ、という点です。つまり、正常処理ではそもそもprctlが呼ばれないように読めます。うひ。


この症状がバグレポートされてないかと探してみるとありました。

Bug#21723の報告者のパッチではきちんとsetuidの後にprctlを呼ぶようになっているのですが、なぜか(5.1.20で)なされた修正では、前述のように変なところでprctlが呼ばれるようになってしまっていました。。。


というわけで、いざというときにcoreを吐かせるために、mysqldを起動するときは--userオプションではなくて、sudoやsetuidgidコマンドで実効ユーザを指定するといいんじゃないかと思います。


以上、さも自分が調べたかのように書いてますが、(7月頃に)実際に調べたのは id:kazuhooku さんです>< ありがとうございました!


あと、

echo 2 > /proc/sys/fs/suid_dumpable

したらsetuid後prctlしないでもcoreが生まれるかなーと思ったんですが生まれませんでした。なんか勘違いしてるのかしら。。。

http://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html

↑生まれました。

Linux 3.6 以降では、/proc/sys/fs/suid_dumpable が 2 ("suidsafe") に設定されている場合、テンプレートは、絶対パス名 (先頭に '/' 文字があるパス名) かパイプ (以下で説明) のどちらかでなければならない。

http://linuxjm.osdn.jp/html/LDP_man-pages/man5/core.5.html

CentOSのkernel 2.6.32-431.el6ですが、この制限が効いてるらしく、

echo '/var/tmp/core.%t_%e_%p' > /proc/sys/kernel/core_pattern

とかしたらちゃんと生まれました!

あと、suid_dumpableの変更はその後で起動したプロセスにだけ適用されるので、変更後にmysqldの再起動が必要な点に注意す。




追記

松信さん のおかげで、reopenされました! @matsunobu++

2009-09-29 (Tue)

keepalived.confのシンタックスチェックツール「keepalived-check」「haskell-keepalived 」が凄い!

id:viverさんとid:maoeさんが(7月ぐらいに)やってくれてました。

ずいぶん前からkeepalived.confのシンタックスチェックをするプログラムないかなーと思ってました。個人的にはバックアップ側のkeepalivedにまず反映して問題がないならマスタにも反映する、という運用手順をとっていますが、はやりsyntax checkerがあるのとないのとでは安心感が大違いです。

また、構築フェーズでは、keepalived.confをあれやこれやといじり試行錯誤するわけですが、confの書式が間違っていてもkeepalivedは警告も出さずスッコリなかったものとして知らん顔で起動するので、「設定してるはずの通りに動かない!え!なんで?なんで?><?」というドはまりに陥りがちです。でも、syntax checkerがあればもう安心です。


ためしに自分とこのkeepalived.confをチェッカにかけてみました。

まず、frsyukiさんのkeepalived-checkはRubyで書かれています。必要なのはruby >= 1.8.6だけ、というお手軽さと、パーサ部のきれいさがポイントです。特に後者は秀逸で、ぼくが拡張パッチの文法定義を追加したときも、keepalived-check.rbのAccept周辺を初見で見よう見まねでいじってできてしまったぐらいです。

試しに実行してみると、"SSL_HELLO"なんて文法は知らない、とエラーが出ています。-vをつけて実行すると、スタックトレースなども表示され、keepalived-check.rbに手を入れる際の参考になります。さて、"SSL_HELLO"はKLabのkeepalived-extcheckパッチを当てると使えるようになる文法なので、エラーが出るのは同然ですが、-e オプションをつけるとこのパッチで増える文法も解釈するような動作になります。

$ ./keepalived-check keepalived.conf
unknown keys "SSL_HELLO" at real_server

$ ./keepalived-check -e keepalived.conf
ok

次に、maoeさんのhaskell-keepalivedです。が、

$ runhaskell Setup.hs build
Preprocessing library keepalived-0.0.1...
Preprocessing executables for keepalived-0.0.1...
Building keepalived-0.0.1...
/usr/bin/ar: creating dist/build/libHSkeepalived-0.0.1.a
[8 of 8] Compiling Main             ( bin/Kc.hs, dist/build/kc/kc-tmp/Main.o )

bin/Kc.hs:206:28:
    Couldn't match expected type `AppConfig'
           against inferred type `a b'
    In the expression:
        do case getOpt Permute (commandOpts cmd) opts of
             (options, files, []) -> let ... in ...
             (_, _, errs) -> defaultAppConfig
    In the definition of `processAppConfig':
        processAppConfig cmd opts
                           = do case getOpt Permute (commandOpts cmd) opts of
                                  (options, files, []) -> ...
                                  (_, _, errs) -> defaultAppConfig

こんなのが出てビルドできませんでした。。。><

ただ、出初めの頃に試したときはちゃんとビルドもできて、文法チェックもちゃんとできてました。今、ビルドできないのはぼくの環境の不備と環境の整え方を知らないせいだと思います…

追記

d:id:maoe:20090928:1254159495 でmaeoさんがバイナリ配布と補足をしてくださってます!



というわけで、ぼくはkeepalived.confの文法ミスのお悩みから解放され、以前よりも積極的で前向きな性格になり、充実した毎日を楽しまれているようです。

ありがたやありがたや。

2009-09-11 (Fri)

YAPC::Asia 2009で「『Ficia』インフラとPerlにまつわるエトセトラ」というタイトルでしゃべってきました

f:id:hirose31:20090912022209j:image:w300:right

YAPC::Asia 2009 第一日目で「『Ficia』インフラとPerlにまつわるエトセトラ」というタイトルでしゃべってきましたのでその資料を公開します。


他の方のスライドも

から参照できるようでっす。


以下、今回のトークの内容で参考にさせてもらったURLのリストです。

2009-09-09 (Wed)

YAPC::Asia Tokyo 2009のチェックインにはQRコードが必要です

いよいよ今晩からYAPC::Asia 2009がはじまりますね!

さて、入場に際しては

YAPC::Asia Tokyo 2009では紙媒体のチケットを発行していません。チェックインのためにはQRコードを用いて参加者を認証いたします。

認証用QRコードを入手するには、本サイトにログインして、メニューページを表示してください。支払いが無事済んでいれば、QRコードを含む囲みが表示されているはずです。

当日の入場登録作業(チェックイン)について

だそうなので、皆さんお忘れないように。

あと、プリンタがない人のために、25×25のマスをかきましたので、モニタでQRコードをみながらクーピーなどで黒く塗るといいと思います。(ただし、利用の結果生じた損害について、一切責任を負いません><)

↓の通り、PostScriptで書いたので、手を入れてクロスワードパズルや数独などにもおつかいください。

2009-09-04 (Fri)

安全に一気にファイルの内容を書き換える

ファイルを書き換えるときに、単純に open, write, close だと、そのファイルの性格にもよりますが、問題になるケースがあります。

  • 書き込み処理中に異常終了しちゃった

例えば

open my $fh, '>', '/foo/bar';
print $fh gen_header();
print $fh gen_section_1();
print $fh gen_section_2(); # ← die
print $fh "# EOF\n";
close $fh

な処理で、get_section_2 が異常終了してしまうと、その後書き込まれるはずのものが書き込まれずファイルの内容が中途半端になってしまいます。(まぁでもこのケースはエラー処理をちゃんとやれば回避できますね)

  • 書いている途中で読まれちゃった

サーバ名一覧のような定義ファイルや、毎回読まれるタイプの設定ファイルの場合、生成プログラムが書いている最中で、だれかに(中途半端な状態で)読まれちゃう可能性もあります。

open my $fh, '>', '/foo/bar';
for (@totemo_takusanno_data) {
  print $fh $_; # ← まだ読んじゃだめー
}
close $fh

これらの問題を回避するのは特に難しいことではなくて、おおざっぱにいうと、とりあえずテンポラリのファイルに書いておいて、書き終わったらお目当てのファイル名にrename(2)する、でOKです。

OKなんですが、毎回毎回書くのが面倒なので、裏でそういったお仕事をしてくれる便利Perlモジュールを書きました。

使い方は IO::File と同じ感じで:

use IO::File::AtomicChange;

my $fh = IO::File::AtomicChange->new("/foo/bar", "w");
$fh->print("# create new file\n");
$fh->print("foo\n");
$fh->print("bar\n");
$fh->close; # MUST CALL close EXPLICITLY

な感じでファイルに書き込むと、実は裏ではテンポラリのファイルに書き込んで、$fh->close したタイミングでrename(2)してくれます。

あとオマケ機能で、backup_dir を指定した場合は、オリジナルファイルのバックアップコピーもとってくれます。

例えば、

my $fh = IO::File::AtomicChange->new("/foo/bar", "a",
                                     { backup_dir => "/var/backup/" });

だと、変更前のファイルを、/var/backup/bar_YYYY-MM-DD_HHMMSS_PID に(パーミッション、mtimeを保持しつつ)コピーします。



ちなみになんでこんなモジュールを作ったかというと、9/10(木)、9/11(金)に開催されるYAPC::Asia 2009のトークのためにシステム管理用のコマンドを作っている際に必要になったのでした。

イベントドリブンですね(^q^

2003 | 11 | 12 |
2004 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 05 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 12 |
2012 | 01 | 02 | 03 | 06 | 08 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 |
2015 | 01 | 02 | 07 | 10 |
2016 | 01 | 05 | 10 | 12 |
2017 | 07 |
2018 | 05 |