2009.10.28 cronの話 衝撃の事実にぶったまげたのでメモを兼ねて記録しておく。追記はしない方針 なのだが, 意外と反響がよかったのと, フィードバックがあったので, 追記し てみる。本日中(?)の追記だからいいよね。ということで。先に読んだ人であれ ば項 5 が増えている。 よく cron でバッチ処理が動かないという話を聞く。手動で実行した場合には 問題なく動くということで。よくある極めて典型的なパターンではないだろう か。これは cron がどういう状態で実行されるかイメージできてないために起 こる問題である。 またこの問題は極めて環境依存であり, OS のバージョン間でさえ保証がない。 個別に確認すべき問題でもある。 1. 実行権限に留意 自分的には当たり前だと思ってたのだけど, そのあたりに留意しないする人 がいるということで, 最初にリストアップしておく:D。 バッチなりなんなり cron で実行されるものが誰の権限で実行されるか把握 しなければならない。誰の権限でもいいや, ということは設計上あり得るか もしれない(それはそれで素晴らしい設計なのだろう)が, 実際に適用する際 はそんな設計は「あり得ない」または「無い」ものとして, 誰かしら権限を 持って実行する/させる必要がある。 よく誰でもいいなら nobody でいいや。という人がいるが, あれは NFS に おける nobody という特別な権限(root の代替)であって, 誰にでも解放さ れた「誰でも」という意味ではない。たまたま色々と権限がないように見え るかもしれないが, OS 的に保証されたものではない(あくまでも NFS サブ システム内の権限である)。 いずれにせよ, 安直に root やら自分の権限でテスト(手動実行)するという のが本当に正しいか確認する必要がある。かならず cron に設定した時の権 限で実行しよう。 2. 環境変数に留意 手動で実行した場合と, cron で実行した場合の最大の違いであり, 気がつ いてない, つまり暗黙のうちに依存している最大のものが環境変数である。 典型的には $HOME だったり $LANG だったりするわけだが…。 rsync, scp 等 ssh するものだと, 手動では正常に実行できて, cron では 実行できない問題では, SSH Agent Forwarding に絡むものもあったりする。 「どうしてパスワード入力無しに ssh 接続が可能なのか」(つまり手動では パスワードを聞いてこず, cron では聞いてきたために止まる)意識する必要 がある(公開鍵認証なり Secure Host 認証なり)。 cron で実行される状況下での環境変数の定義は千差万別なので printenv(1) コマンドを使用して常に確認しておく必要がある。 ごく安直には env - バッチプログラム として確認するのがよい。いずれに せよ暗黙的でも明示的でもどんな環境変数に依存しているのか確認すべきで ある(依存していることは悪いことではない, 依存していることに気がつか ないのが悪いのである)。 3. ホームディレクトリ(ワーキングディレクトリ)に留意 cron で実行される場合, どこのディレクトリで実行されるかの保証がない。 また環境変数 HOME すらまともに設定されるかわからない。最近まで FreeBSD では HOME=/var/log なんてのが設定されてて…という場合もある。環境変数 と合わせて pwd(1) コマンドを使用して常に確認しておこう。 4. ログ cron の実行ログは一般に /var/log/cron に出力される(OS 依存)。ただこれ は「実行された」かどうかしかわからない。正常に実行されたのかされてな いのか, 実行が失敗したかの確認はできない。 一般に標準出力・標準エラー出力されるものについては, 実行権限者宛ての メールが届くことになっている。何も出力しないで, exit 0/exit 1 しただ けでは何が起きてるのかわからないことが多い。 というわけで実行ログについては cron に頼らず, 自前で行うべきである。 単純に logger(1) コマンドを使用して syslog 経由でのログを吐くのが簡単 だったりする。あるいはどこか特定のファイルへの出力するなど。やり方は いっぱいあるし, ポリシーやら運用方針やらに絡んでくるので, ログについ て検討すべきことはいっぱいある。 5. 多重起動問題 上記 4 項目で問題ない状態にも関わらず「正常に動かない」問題が発生す ることがある。後から起動したバッチが, 前に起動したバッチを後追い, あるいは追い越すことで, 結果がおかしくなる事例である。 多重起動してもいい作りにするのが常套のような気もするが, そもそもな ぜバッチ処理にしたか, を考慮すれば, 多重起動するのが間違ってる(一般 に負荷が高くなる, 時間がかかる処理をバックグラウンドで実行させるた め)に決まってる:-)。まぁ実際には, なぜ時間がかかってるのかという評 価が必要なのだが(^^;。 < 時間がかかりすぎるのが許可/不許可とか それはそれとして, 多重起動を禁止する方法としては, ロック機構を設け て対応するのが楽である。ロックの設け方も, CGI 等で流行った symlink 方式やら, lock(1) コマンドを使う方法(OS によっては procmail の lockfile(1) が提供されてる場合がある), 共有メモリを使用する(バッチ スクリプトで必要なのか!?)方法など, 色々ある。 それぞれ得失があるが, その辺は調べてもらうとして…。いまどき NFS の ロック問題対応するのに symlink 云々言ってる人とは相手したくないなぁ。 そもそも安いレンサバで負荷がかかるバッチ処理なんて実行させないに決 まってる(偏見です! > 自分)。symlink 消し忘れ問題と合わせて評価した いところ。 < 大抵消し忘れてトラブルになる罠 あぁ。全てのサーバーで多重起動防止(ログ集計等で単一サーバーでのみ 実行したいなど)したい?それは NFS による symlink というやり方はアリ かもね。もっとも複数サーバーにバッチ起動させる際にセントラルにディ スパッチさせた方がトラブルにならない気がするけど:D。 他にも留意点があるかもしれないが(X に依存してた…とかはさすがにないよね?) おおよそ上記 5 点を意識してバッチスクリプト組んでれば問題ないと思うけど。 Written by 重村法克