저는 crond를 대체하거나 확장 기능을 찾고 있습니다.
제가 꼭 갖고 싶은 기능은 내결함성입니다. 예를 들어 지정된 시간에 컴퓨터 전원이 켜지지 않아서(예: 정전으로 인해) 작업 실행이 실패하거나 작업이 성공적으로 실행되지 못한 경우(예: rc!=0)(예: 액세스 부족으로 인해) 인터넷에 연결) 관련 소프트웨어는 다음 예정된 실행까지 정기적으로 재시도해야 하며, 이때 이 실행이 성공한다고 가정하면 일반 작업이 계속됩니다.
가질 가치가 있는 다른 기능:
- REST 인터페이스 등을 통한 원격 제어
- 더 나은 로깅
그러한 소프트웨어를 사용할 수 없는 경우 기존 소프트웨어를 확장하는 것, 아니면 처음부터 무언가를 작성하는 것 중 어느 것이 더 나은 아이디어인지 올바른 방향으로 알려줄 수 있는 사람이 있습니까?
답변1
하루에 한 번 이상 실행해야 하는 작업이 여러 개 있습니다. 내가 하는 일은 이러한 작업에 대한 스크립트를 매시간(또는 더 자주) 시작하는 것입니다. 그러면 스크립트 자체가 디스크의 상태 파일을 확인하여 해당 작업이 실행되었는지 확인합니다.
상태 파일이 존재하고 최신 상태이면 스크립트가 종료됩니다.
파일이 너무 오래되었거나(즉, 하루 전에 마지막으로 기록됨) 존재하지 않는 경우 스크립트가 실행되어 성공적인 종료 시 상태 파일을 작성합니다.
이 기능을 기존 프로그램에 구축할 수 없는 경우 프로그램이 실행되어야 하는지 확인하고, 필요한 경우 이를 호출하고, 성공 시 상태 파일(종료 값, 구문 분석된 출력)을 작성하는 래퍼 스크립트를 만들면 됩니다.
/usr/local/bin/catchup.simple
:
#! /usr/bin/env python
"""
first parameter is a path to a file /..../daily/some_name
That is a status/script file and the /daily/ indicates it needs to run at least
once a day (after reboot, after midnight).
The rest of the parameters is the command executed and its parameters.
If there are no more parameters beyond the first the actual status
file is /..../daily/some_name.status and is expected to be updated by calling
the /....daily/some_name script (which has to be executable). That
script doesn't need to know about the frequency and gets called with
the status file as first (and only) argument.
Valid directory names and their functioning:
/daily/ run once a day (UTC)
/hourly/ run once an hour
The actual scheduling and frequency to check if running is necessary, is
done using a crontab entry:
CU=/usr/local/bin/catchup.simple
CUD=/root/catchup
# month, hour, day_of_month, month day_of_week command
*/5 * * * * $CU $CUD/daily/getlogs curl ....
If mulitple days (or hours) have gone by, no runs are made for skipped
days.
If subprocess.check_output() fails the status file is not updated.
"""
import sys
import datetime
import subprocess
verbose = False # set to True to debug
def main():
if len(sys.argv) < 2:
print 'not enough parameters for', sys.argv[0]
return
if len(sys.argv) == 2:
status_file_name = sys.argv[1] + '.status'
cmd = [sys.argv[1]]
else:
status_file_name = sys.argv[1]
cmd = sys.argv[2:]
freq = sys.argv[1].rsplit('/', 2)[-2]
if verbose:
print 'cmd', cmd
print 'status', status_file_name
print 'frequency', freq
try:
last_status = datetime.datetime.strptime(
open(status_file_name).read().split('.')[0],
"%Y-%m-%dT%H:%M:%S",
)
except (IOError, ValueError):
last_status = datetime.datetime(2000, 1, 1)
now = datetime.datetime.utcnow().replace(microsecond=0)
if verbose:
print last_status
print 'now', now.isoformat()
if freq == 'daily':
if last_status.date() < now.date():
subprocess.check_output(cmd)
elif verbose:
print 'already done today'
elif freq == 'hourly':
if last_status.date() < now.date() or \
last_status.date() == now.date() and \
last_status.hour < now.hour:
subprocess.check_output(cmd)
elif verbose:
print 'already done this hour'
with open(status_file_name, 'w') as fp:
fp.write(now.isoformat())
if __name__ == "__main__":
main()