特定用途のスクリプト実装後、クーロンにより毎日決まった時間に処理が実行されることを期待していたが「いつの間にかプロセスが落ちていた・・」
しかも「何時?プロセスが落ちたのか分からない」など、異常に気づいたころには「実は数ヶ月前から処理が止まっていた。」などはよくある話です。
大抵の場合、原因は多重起動処理の禁止ロジックを実装していないがために起こる悲劇です。
この問題を回避するべく「多重起動処理」を禁止にする仕組みを実装します。
多重起動禁止処理の作成
定期的に実行するスクリプトが何らかの原因により、正常に実行できずに止まってしまう(キューイング)場合、(翌日の定時などに)後から起動された同一スクリプトのプロセスは次々にOSにキューイングされて行きます。
当然キューイングされたプロセス数分のリソースが消費され、やがてリソース不足に陥りシステムがダウンしてしまいます。
多重起動処理禁止に必要な機能
- ロック機能
- アンロック機能
- 処理の中断機能
上記3つの機能を実装して、多重起動処理検知時に当該プロセスを停止します。
前提
Beエンジニアでシェルスクリプトを実行する環境は下記の通りとします。
実行環境
BASE_DIR(任意のディレクトリ)
- scripts
- bin(実行スクリプト格納領域)
- func.sh (実行ファイル)
- com(共通スクリプト格納領域)
- comFunc.shrc(共通関数定義ファイル)
- etc(設定ファイル等の格納領域)
- message.conf(メッセージ定義ファイル)
- log(スクリプト実行ログの格納領域)
- スクリプト名.log
- tmp(テンポラリ領域)
- rep(レポート出力領域)
- bin(実行スクリプト格納領域)
本処理の仕組みは、共通で使用することを前提に共通関数定義ファイル「comFunc.shrc」へ実装します。
ロック機能
スクリプトが起動した時点で「/tmp」ディレクトリ配下へ「スクリプト名.lock」ディレクトリを作成し、当該スクリプトが実行中であることを後続の同一スクリプトへ知らせるためのファイル「pid」ファイルを作成します。
[<BASE_DIR>/scripts/bin/comFunc.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# -------------------------------------------------- # Make lock directory # -------------------------------------------------- # return 0: success !0: failure # -------------------------------------------------- mkLockDir() { LOCK_DIR=${TMP_PATH}/${SCRIPT_NAME%.*}.lock export LOCK_DIR mkdir ${LOCK_DIR} rc=$? if [ $rc = 0 ]; then logOut lock directory [${LOCK_DIR}] created. echo $$ > ${LOCK_DIR}/pid DIRLOCK_ACQUIRED=YES export LOCK_DIR DIRLOCK_ACQUIRED fi return ${rc} } |
アンロック機能
当該スクリプトが終了した時点で「/tmp」ディレクトリ配下の「スクリプト名.lock」ディレクトリを削除します。
[<BASE_DIR>/scripts/bin/comFunc.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# -------------------------------------------------- # Remove lock directory # -------------------------------------------------- # return N/A # -------------------------------------------------- rmLockDir() { if [ ${DIRLOCK_ACQUIRED} ]; then logOut Removing directory [${LOCK_DIR}]... if [ ! -d ${LOCK_DIR} ]; then abort lock directory vanished. fi if [ ! -f ${LOCK_DIR}/pid ]; then abort Lock pid file vanished. fi locker=`cat ${LOCK_DIR}/pid` if [ ${locker} = $$ ]; then rm -rf ${LOCK_DIR} || logOut could not remove lock directory. else logOut There is lock directory, but locker pid = [$$]. logOut Lock directory is left untouched. fi fi } |
処理の中断機能
既に、同一スクリプトが実行中である場合、後発の同一スクリプト処理を中断します。
[<BASE_DIR>/scripts/bin/comFunc.sh ]
1 2 3 4 5 6 7 8 9 10 |
# -------------------------------------------------- # Abort # -------------------------------------------------- # param1- message # return N/A # -------------------------------------------------- abort() { echo "`formatLog ABORT PID:$$ "${*}"`" 1>&2 exit 1 } |
呼び出し側のシェルスクリプト作成
実行ファイルへ多重起動禁止処理を実装し、実行後30秒間のスリープ処理を記述します。同一スクリプトを時間差で実行します。つまり後発スクリプトが先発スクリプト追い越すことを禁止します。
[<BASE_DIR>/scripts/bin/func.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#!/bin/sh #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # # ver.1.0.0 yyyy.mm.dd # # Usage: # # sh /root/scripts/bin/func.sh # # Description: # 例題コマンド実行スクリプト # # 設計書 # なし # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ . `dirname $0`/../com/comFunc.shrc # ---------------------------------------------------------- # variables (変数の宣言領域) # ---------------------------------------------------------- scope="var" rc=${JOB_ER} # ---------------------------------------------------------- # functions (関数を記述する領域) # ---------------------------------------------------------- scope="func" # ---------------------------------------------------------- # pre-process (事前処理ロジックを記述する領域) # ---------------------------------------------------------- scope="pre" #ログ開始関数呼び出し startLog if mkLockDir; then logOut Successfully locked. else abort Could not acquire lock. fi # ---------------------------------------------------------- # main-process (メインロジックを記述する領域) # ---------------------------------------------------------- scope="main" sleep 30 # ログ出力実行 logOut "このメッセージは[ ${SCRIPT_NAME} ]の[ ${scope} ]領域から出力されています。" if [ $? -eq ${JOB_OK} ]; then rc=${JOB_OK} fi # ---------------------------------------------------------- # post-process (事後処理ロジックを記述する領域) # ---------------------------------------------------------- scope="post" #ログ終了関数呼び出し exitLog $rc |
多重起動禁止処理の実行結果
別々のコンソールから「func.sh」をそれぞれ実行します。
1 2 3 4 5 6 7 8 9 |
[root@jb1 log]# tail -f func.log 2021-03-13 17:07:00 [ pre ] SCRIPT:[ func.sh ] PID:[ 17492 ] STARTED LOG. 2021-03-13 17:07:00 [ pre ] lock directory [/root/scripts/tmp/func.lock] created. 2021-03-13 17:07:00 [ pre ] Successfully locked. 2021-03-13 17:07:05 [ pre ] SCRIPT:[ func.sh ] PID:[ 17502 ] STARTED LOG. mkdir: cannot create directory ‘/root/scripts/tmp/func.lock’: File exists 2021-03-13 17:07:05 [ pre ] ABORT PID:17502 Could not acquire lock. 2021-03-13 17:07:30 [ main ] このメッセージは[ func.sh ]の[ main ]領域から出力されています。 2021-03-13 17:07:30 [ post ] SCRIPT:[ func.sh ] PID:[ 17492 ] ENDED LOG with NOMAL. |
2行目:一発目のスクリプトがPID:17492で実行されました。
3行目:ロック機能により、ロックディレクトリが作成されました。
4行目:一発目のスクリプト(PID:17492)の成功ログが出力されています。
5行目:2発目の同一スクリプトがPID:17502で実行されました。
6行目:既にロックディレクトリが存在するため、新たなディレクトリの作成に失敗。
7行目:2発目の同一スクリプト処理が中断されました。
8行目:一発目のスクリプトの処理が実行されています。
9行目:一発目のスクリプトが正常に終了しました。
※ 本スクリプト利用により発生した利用者の損害全てに対し、いかなる責任をも負わないものとし、損害賠償をする一切の義務はないものとします。