省エネファイルサーバ構築 35 CentOS 5.6 多重起動しないストレージ同期スクリプトをcron登録

ストレージのmainからmirrorへはrsyncでやってますが、そのまま毎日同じ時間に動かすと1日で終わらい程量が多い場合、並列で何個も動いてしまいます。

それを回避するために、

前回起動した時のプロセスが残っている時は次を起動しない

という条件を追加します。
まず、並列起動禁止の入ってない、以前使っていたスクリプトを見てみます。
こんな感じです。

#!/bin/bash

# log directory
log_dir="/var/log/sync_storage"
if [ ! -d $log_dir ]
then
mkdir $log_dir
fi

# log file path
log_file=$log_dir"/"`date +%Y%m%d%H%M`".log"

# start
echo "### Start sync storage ##########" >> $log_file
date >> $log_file

# exec rsync
rsync -av --delete /var/storage/00/main/data /var/storage/00/mirror >> $log_file

# end
echo "### End sync storage ############" >> $log_file
date >> $log_file

exit 0

ではこれを作成してバックグラウンドで実行して、実行中のプロセスを見てみます。

# vi ~/sync_storage.sh

# chmod 755 ~/sync_storage.sh

# ~/sync_storage.sh &
[1] 9644

# ps aux | grep sync_storage
root 9644 0.0 0.0 63868 1240 pts/0 S 02:12 0:00 /bin/bash /root/sync_storage.sh
root 9659 0.0 0.0 65452 880 pts/0 S+ 02:13 0:00 grep sync_storage

一生懸命rsync中のようです。
すぐには終わらないので終わるまで待ちます。
ログファイルをtailしておくとわかりやすくていいです。

# tail -f /var/log/sync_storage/201108170212.log
(略)

さて、こいつがなかなか時間がかかるので今のうちに多重起動させない条件を追加しておきます。

自分自身が実行中かどうか?を取得するためには

# ps -o "cmd" | grep sync_storage.sh
sync_storage.sh

こうなります。
が、最後のシェルスクリプト名は自分自身なので動的に取得した方がカッコイイだけでなく、ファイル名を変更しても対応可能になります。
シェルの中では自分自身はどうなるのかというと$0で取得できるので

ps -o "cmd" | grep $0

これでイケます。この結果が2個以上出てきたらNGです。
1個は自分自身のプロセスなのでNGにすると毎回NGになってしまうのでダメです。

と思ったんですが、grep自身も出てしまうので3つ以上いた場合をNGにするようにします。

で、grepの結果数を取得するにはgrepにオプション-cをつければいいので

ps -o "cmd" | grep -c $0

の結果が3以上の場合はNGとすればOKです。
なんですが、

CNT=`ps -o "cmd" | grep -c $0`
echo $CNT

とやってみるとなぜか重複無し状態でも3になってしまいます。
中を見て見るためにオプションを外してみると

CNT=`ps -o "cmd" | grep $0`
echo $CNT

/bin/bash スクリプト名 /bin/bash スクリプト名 grep スクリプト名


なぜかスクリプト実行が2つ、grepが1つ、という状態になっています。
原因不明です・・・。

なんかもっと簡単な方法はないものかとぐぐったらすぐ出てきました。

シェルスクリプトを二重起動させたくない - 憩いの場
シェルスクリプトの多重起動を防ぎたい! - ”improve it!”(IT四重奏)
プロセスの二重起動を防ぐ方法 | Carpe Diem

上記サイトを参考にしてテスト用に作成してみます。

#!/bin/bash

# 自分のスクリプトと同じパスで呼ばれているプロセスのうち
# 一番古いプロセスIDを取得
running=`pgrep -fo $0`

# 今呼び出されたプロセスと上で取得したプロセスの
# IDが一致してない場合は古いものが起動していると判断して終了
if [ $$ != $running ]; then
# 終了メッセージ
echo "already running : " $running
exit 1
fi

# 開始メッセージ
echo start

# 多重起動テストのために停止
sleep 10

exit 0

これをバックグラウンドで複数実行すれば一つ目が終わってない場合はexit 1になるはず。

  1. 1つめ実行
  2. 1つめプロセスID表示
  3. 1つめ開始メッセージ
  4. 2つめ実行
  5. 2つめプロセスID表示
  6. 1つめがすで実行中メッセージ
  7. 2つめ終了
  8. 1つめが終わるまで待ち
  9. 1つめ終了
  10. 3つめ実行
  11. 3つめプロセスID表示
  12. 3つめが開始メッセージ

という順序で表示されればOK。
やってみる。

# vi aaa.sh
# /tmp/aaa.sh &
[1] 12117
# start
/tmp/aaa.sh &
[2] 12120
# already running : 12117
[2] Exit 1 /tmp/aaa.sh
[1] Done /tmp/aaa.sh
# /tmp/aaa.sh &
[3] 12129
# start

まさに予想通り。
# の行にメッセージが出ちゃっているのはメッセージ表示よりコマンド受付開始が早かったから。

これをさっきのスクリプトに組み込んでみるとこうなります。

#!/bin/bash

# log directory
log_dir="/var/log/sync_storage"
if [ ! -d $log_dir ]
then
mkdir $log_dir
fi

# log file path
log_file=$log_dir"/"`date +%Y%m%d%H%M`".log"

# start
echo "### Start sync storage ##########" >> $log_file
date >> $log_file

# check dupe
running=`pgrep -fo $0`
if [ $$ != $running ]
then
echo "### Already running : $running ###" >> $log_file
exit 1
fi

# exec rsync
rsync -av --delete /var/storage/00/main/data /var/storage/00/mirror >> $log_file

# end
echo "### End sync storage ############" >> $log_file
date >> $log_file

exit 0

ではやってみます。
さっきのスクリプトファイルを修正して実行
3回ほど連続して実行してみる。

# vi ~/sync_storage.sh

# ~/sync_storage.sh &
[1] 12255
# ~/sync_storage.sh &
[2] 12261
# ~/sync_storage.sh &
[3] 12265
[2] Exit 1 ~/sync_storage.sh
#
[3]+ Exit 1 ~/sync_storage.sh

2,3番目のものは終了しました。
プロセスを見てみると、1つめだけ残って動いてます。

# ps aux | grep sync
root 12255 0.0 0.0 63868 1240 pts/0 S 08:14 0:00 /bin/bash /root/sync_storage.sh
root 12259 5.6 0.4 75184 10020 pts/0 D 08:14 0:03 rsync -av --delete /var/storage/00/main/data /var/storage/00/mirror
root 12260 0.6 0.4 74892 9460 pts/0 S 08:14 0:00 rsync -av --delete /var/storage/00/main/data /var/storage/00/mirror
root 12272 0.0 0.0 65452 888 pts/0 S+ 08:15 0:00 grep sync

そして終わるまでログを見ててみる。

# tail -f /var/log/sync_storage/201108170814.log
### Start sync storage ##########
2011年 8月 17日 水曜日 08:14:19 JST
building file list ... ### Start sync storage ##########
2011年 8月 17日 水曜日 08:14:21 JST
### Already running : 12255 ###
### Start sync storage ##########
2011年 8月 17日 水曜日 08:14:23 JST
### Already running : 12255 ###
done
sent 6057008 bytes received 20 bytes 28106.86 bytes/sec
total size is 994283992469 speedup is 164153.77
### End sync storage ############
2011年 8月 17日 水曜日 08:17:54 JST

期待通りの動きをしてくれたようです。

ではcronに登録しておきます。
平日の昼12時に起動させれば家にいないのでばっちりです。

# crontab -e
-bash: crontab: command not found

なんとcrontabすら入ってなかった。
入れときます。

webminからインストール済みパッケージを探します。

インストール済みパッケージからcronを探す

でました。インストール済みです。

crontabsはインストール済み

おかしい。詳細を見てみる。[List Files]をクリック

crontabsのパッケージツリー

ちゃんと入っているように見える・・・。
さらに中へ。

/etc/crontab

crontab詳細

普通に入っている・・・。
よくわからないので一旦戻って未インストールの方から探してみる。

cron関連が出てきた。

ぐぐってみるとどうやら vixie-cron がいつものcrontabコマンドらしい。

参考サイト : Gentoo Linux Cronガイド

では入れる。成功。

YUM で vixie-cron をインストール

vixie-cron インストールログ

インストールが完了したので設定してみる。
ちらっとwebminを見ても見当たらない。
インストールしたばかりだからメニューに出てないのかもしれないのでメニューリフレッシュしてみる。
webmin の左側の Refresh Modules をクリックすると更新されます。

webminメニュー

終わるとでてきました。

System > Scheduled Cron Jobs

といくと登録済みcronがでてきます。ココに追加していきます。

説明を追加

Create a new scheduled cron job. のリンクをクリック。

月~金の13時0分に起動させるように設定。

スクリプトをcronに登録

[Create]をクリック。

cronに登録された状態

無事登録されました。
あとは時間が来たら動くはずなので夜ログを見てみます。

コメント