おひさしブリーフ、かっぱです。
tl;dr
下図のように cron ジョブで定期的にスクリプトを生成して実行させようとした時、スクリプト完了する前に次の cron ジョブが走ってスクリプトが生成されて、そのスクリプトが実行されてしまうような状況に遭遇してどうしたもんかなと悩んでいたら ts というツールを見つけたので試してみた。
memo
ts とは
スクリプトやコマンドを ts
コマンド経由で実行することで、それらをジョブとしてキューに放り込んで順次実行してくれるツール。冒頭の構成に ts
を加えると下図のようになり、cron の開始時間とスクリプトの実行時間の依存関係は切り離される。
導入
CentOS 6 に導入するにはソースコードを取得してビルドする必要がある。
01 02 03 04 05 06 07 08 09 10 | $ cat /etc/redhat-release CentOS release 6.7 (Final) $ sudo yum install gcc $ wget http: //vicerveza .homeunix.net/~viric /soft/ts/ts-0 .7.6. tar .gz $ tar zxvf ts-0.7.6. tar .gz $ cd ts-0.7.6 $ make $ mkdir ~ /bin $ cp ts ~ /bin/ts |
サンプル
以下のような雑なシェルスクリプトを cron で実行させる。
01 02 03 04 05 06 07 08 09 10 11 12 13 | $ cat demo.sh #!/bin/sh DATE=` date +%s` echo "echo $DATE && sleep 300" > /tmp/test .sh.$DATE ${HOME} /bin/ts logger -t ts_demo "スクリプトを作成しました" sleep 3 logger -t ts_demo "スクリプトを実行します" ${HOME} /bin/ts sh /tmp/test .sh.$DATE ${HOME} /bin/ts -d logger -t ts_demo "スクリプトの実行が完了しました" $ crontab -l * /3 * * * * /home/centos/demo .sh > /dev/null 2>&1 |
暫く放置してから ts
コマンド(ts -l
でも同等)を実行すると以下のように ts
コマンド経由で実行したコマンド(スクリプト)がジョブとしてキューイングされている。
1 2 3 4 5 6 7 | $ ts ID State Output E-Level Times(r /u/s ) Command [run=1 /1 ] 0 running /tmp/ts-out .b5xJat sh /tmp/test .sh.1470470326 1 queued ( file ) [0]&& logger -t ts_demo スクリプトの実行が完了しました 2 queued ( file ) logger -t ts_demo スクリプトを作成しました 3 queued ( file ) sh /tmp/test .sh.1470470342 4 queued ( file ) [3]&& logger -t ts_demo スクリプトの実行が完了しました |
さらに暫く放置してからジョブ一覧を確認すると…
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | $ ts -l ID State Output E-Level Times(r /u/s ) Command [run=1 /1 ] 6 running /tmp/ts-out .FSSSnL sh /tmp/test .sh.1470470581 7 queued ( file ) [6]&& logger -t ts_demo スクリプトの実行が完了しました 8 queued ( file ) logger -t ts_demo スクリプトを作成しました 9 queued ( file ) sh /tmp/test .sh.1470470761 10 queued ( file ) [9]&& logger -t ts_demo スクリプトの実行が完了しました 11 queued ( file ) logger -t ts_demo スクリプトを作成しました 12 queued ( file ) sh /tmp/test .sh.1470470941 13 queued ( file ) [12]&& logger -t ts_demo スクリプトの実行が完了しました 14 queued ( file ) logger -t ts_demo スクリプトを作成しました 15 queued ( file ) sh /tmp/test .sh.1470471121 16 queued ( file ) [15]&& logger -t ts_demo スクリプトの実行が完了しました 0 finished /tmp/ts-out .b5xJat 0 300.00 /0 .00 /0 .00 sh /tmp/test .sh.1470470326 1 finished /tmp/ts-out .js0Oq6 0 0.00 /0 .00 /0 .00 [0]&& logger -t ts_demo スクリプトの実行が完了しました 2 finished /tmp/ts-out .6Jjrf6 0 0.00 /0 .00 /0 .00 logger -t ts_demo スクリプトを作成しました 3 finished /tmp/ts-out .fotDb6 0 300.00 /0 .00 /0 .00 sh /tmp/test .sh.1470470342 4 finished /tmp/ts-out .8y6fdL 0 0.00 /0 .00 /0 .00 [3]&& logger -t ts_demo スクリプトの実行が完了しました 5 finished /tmp/ts-out .lMTR8K 0 0.00 /0 .00 /0 .00 logger -t ts_demo スクリプトを作成しました |
finished
となっているのは完了したジョブ。logger
で吐いているログを見ると…
01 02 03 04 05 06 07 08 09 10 11 | $ sudo tail -f /var/log/messages Aug 6 08:03:04 ip-xxx-xx-xx-xx ts_demo: スクリプトを実行します Aug 6 08:03:49 ip-xxx-xx-xx-xx ts_demo: スクリプトの実行が完了しました Aug 6 08:03:49 ip-xxx-xx-xx-xx ts_demo: スクリプトを作成しました Aug 6 08:06:04 ip-xxx-xx-xx-xx ts_demo: スクリプトを実行します Aug 6 08:08:49 ip-xxx-xx-xx-xx ts_demo: スクリプトの実行が完了しました Aug 6 08:08:49 ip-xxx-xx-xx-xx ts_demo: スクリプトを作成しました Aug 6 08:09:04 ip-xxx-xx-xx-xx ts_demo: スクリプトを実行します Aug 6 08:12:04 ip-xxx-xx-xx-xx ts_demo: スクリプトを実行します Aug 6 08:13:49 ip-xxx-xx-xx-xx ts_demo: スクリプトの実行が完了しました Aug 6 08:13:49 ip-xxx-xx-xx-xx ts_demo: スクリプトを作成しました |
ts の操作
以下は ts の主な操作。
- ジョブの一覧確認
1 | $ ts -l |
- 完了したジョブをジョブ一覧から削除
1 | $ ts -C |
- ジョブの一括削除(タスクスプーラーサーバープロセスを kill する)
1 | $ ts -K |
- 前のジョブが完了したら実行する
以下のように-d
オプションをつけてジョブを登録することで、${command-1} が正常終了したら ${command-2}が実行される。
1 2 | $ ts ${ command -1} $ ts -d ${ command -2} |
- ジョブの標準出力を確認
1 | $ ts -t ${JOB_ID} |
その他、以下が ts
のヘルプ。
01 02 03 04 05 06 07 08 09 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 | $ ts -h usage: ts [action] [-ngfmdE] [-L <lag>] [-D < id >] [cmd...] Env vars: TS_SOCKET the path to the unix socket used by the ts command . TS_MAILTO where to mail the result (on -m). Local user by default. TS_MAXFINISHED maximum finished jobs in the queue. TS_MAXCONN maximum number of ts connections at once. TS_ONFINISH binary called on job end (passes jobid, error, outfile, command ). TS_ENV command called on enqueue. Its output determines the job information. TS_SAVELIST filename which will store the list, if the server dies. TS_SLOTS amount of jobs which can run at once, read on server start. TMPDIR directory where to place the output files and the default socket. Actions: -K kill the task spooler server -C clear the list of finished jobs -l show the job list (default action) -S [num] get /set the number of max simultaneous jobs of the server. -t [ id ] "tail -n 10 -f" the output of the job. Last run if not specified. -c [ id ] like -t, but shows all the lines. Last run if not specified. -p [ id ] show the pid of the job. Last run if not specified. -o [ id ] show the output file . Of last job run, if not specified. -i [ id ] show job information. Of last job run, if not specified. -s [ id ] show the job state. Of the last added, if not specified. -r [ id ] remove a job. The last added, if not specified. -w [ id ] wait for a job. The last added, if not specified. -k [ id ] send SIGTERM to the job process group. The last run, if not specified. -u [ id ] put that job first. The last added, if not specified. -U swap two jobs in the queue. -B in case of full queue on the server, quit (2) instead of waiting. -h show this help -V show the program version Options adding jobs: -n don't store the output of the command . -E Keep stderr apart, in a name like the output file , but adding '.e' . -g gzip the stored output ( if not -n). -f don't fork into background. -m send the output by e-mail (uses sendmail). -d the job will be run only if the job before ends well -D the job will be run only if the job of given id ends well. -L name this task with a label, to be distinguished on listing. -N number of slots required by the job (1 default). < /id >< /lag > |
最後に
ts の使いドコロ
分散キューシステムでは無いので、一台のサーバー上でしか動かせないのは注意が必要
遅延処理はしたいけど、処理の実行順序は出来るだけ守りたい場合
SQS や RabbitMQ 等を導入する程でもなくて、一台のサーバーで完結するようなバッチ処理
今まで知らずにすいません
本当にすいません。