web-dev-qa-db-ja.com

コンピューターがスリープ状態またはオフになっている場合でも、失敗または失敗したバックアップジョブをキューに入れるにはどうすればよいですか?

rsyncを使用してラップトップをホームサーバーにバックアップするためにsshを使用することを考えています。毎日やりたいので、crontabを使用する予定です。私の質問は、ジョブが失われた場合(ラップトップの電源がオフになっている時間にバックアップがスケジュールされている場合)、できるだけ早く実行されますか?それとも単に見逃されますか?

2
Seb

あなたが特にそれを求めたので:

コンピューターがスリープ状態またはオフの場合でも、キューの失敗またはバックアップジョブの欠落

バックアップジョブサーバー上を実行すると、失敗したジョブを再試行(キュー)するのが比較的簡単になります。これは、サーバーが「安定した」要因であると想定されるためです。 cronjobをスクリプトにルーティングして、バックアップが失敗するたびにキューを作成することで、バックアップのキューイングを実行できます。

ただし、ジョブを実行するとクライアント上の場合、ストーリーは少し異なります。 cronからジョブを実行するとき、ジョブが失敗したことにどのように気付くのでしょうか?クライアントコンピューターがスリープ状態または電源が切れている場合、ジョブは開始されなかったため、cronからのセットアップははるかに複雑になります。

したがって、スケジュールされたバックアップを実行して、失敗したジョブまたは失敗したジョブが最初に実行されることを確認する場合は、cron at allを使用しないことをお勧めします。

単一のファイルでスケジュール/キューバックアップをセットアップする

以下のソリューションは、次の手順で比較的簡単に設定できます。

  1. rsyncバックアップコマンドを作成し、テストします。方法がわからない場合は、grsyncを使用します。
  2. 以下のスクリプトを空のファイルにコピーし、バックアップジョブの名前(例)を付けます:

    backup1.py
    
  3. スクリプトの先頭で、適切な設定を行います。

    #--- enter the working rsync command below
    rsync = "rsync -r -t --progress -s '/home/jacob/Bureaublad/test2' '/home/jacob/Bureaublad/test1'"
    #--- set backup time interval below in hours/minutes
    interval = [0, 10]
    #--- set message True if you'd like a warning if a job failed (else False)
    message = True
    

    例のように、rsyncコマンド二重引用符の間を入力します。

    • unique_name.pyでスクリプトを保存します

それでおしまい。スクリプトの開始時から、バックアップは各間隔の後に正確に実行されます。ジョブが失敗した場合、ジョブは正常に実行されるまでキューに入れられます(1分に1回再試行されます)。その後、スケジュールされた(次回の)正確な時刻に新しいバックアップが設定されます。

  • その後、次のコマンドを使用してスクリプトをテスト実行します。

    python3 /path/to/script.py
    

    すべてが正常に機能する場合は、スタートアップアプリケーションに追加します:ダッシュ>スタートアップアプリケーション>追加コマンドを追加します。

    python3 /path/to/script.py
    

スクリプト

#!/usr/bin/env python3
import subprocess
import time
import os
import datetime

#--- enter the working rsync command below
rsync = "rsync -r -t -s '/home/jacob/Bureaublad/test2' '/home/jacob/Bureaublad/test1'"
#--- set backup time interval below in hours/minutes
interval = [24, 0]
#--- set message True if you'd like a warning if a job failed (else False)
message = True
#--- set the max- size of the logfile below (n- lines)
maxlog = 100

#--- don't change anything below
backup_id = os.path.basename(__file__).split(".")[0]
home = os.environ["HOME"]
datefile = home+"/next_backup_"+backup_id; logfile = home+"/backup_log_"+backup_id
print(datefile, logfile)
interval = (interval[0]*3600)+(interval[1]*60)
failed = False

def failed_message():
    # shows a zenity message if a backup failed
    subprocess.Popen([
        "zenity",
        "--info",
        "--text="+backup_id+" failed\n"+\
        "See "+logfile]) 

def readable_t(sec):
    # converts Epoch time to readable date & time
    return datetime.datetime.fromtimestamp(sec).strftime('%Y-%m-%d %H:%M:%S')

def currtime():
    # returns current (Epoch) time
    return int(time.strftime("%s"))

def sync(failed):
    # performs the rsync command, on errors: writes to log and shows message
    try:
        subprocess.check_call(["/bin/bash", "-c", rsync])
        set_next(planned, interval, currt)
        return False
    except subprocess.CalledProcessError:
        if failed == False:
            open(logfile, "+a").write(readable_t(planned)+" failed\n")
            if message == True:
                failed_message()
        return True

def next_run():
    # reads current queue file, creates it if it doesn't exist
    try:
        return int(open(datefile).read())
    except FileNotFoundError:
        currt = currtime()
        open(datefile, "wt").write(str(currt))
        return currt

def set_next(lastr, interval, currt):
    # creates the next queue, maintains the log file
    nextrun = lastr
    while nextrun <= currt:
        nextrun += interval
    open(datefile, "wt").write(str(nextrun))
    newline = [readable_t(lastr)+" succesfully finished on "+\
                        readable_t(currtime())+"\n"]
    try:
        limited_lines = open(logfile).readlines()[-maxlog+1:]+newline
    except FileNotFoundError:
        limited_lines = newline
    with open(logfile, "wt") as log:
        for l in limited_lines:
            print("4")
            log.write(l)

while True:
    time.sleep(60)
    planned = next_run(); currt = currtime()
    if currt > planned:
        failed = sync(failed)

さらに

  • スクリプトは、backupscriptにちなんで名付けられたログファイルを保持します。
    ログファイルに保存する最大行数は、スクリプトの先頭で設定できます。

    #--- set the max- size of the logfile below (n- lines)
    maxlog = 50
    

    enter image description here

    次の例では、スクリプトは午前11時10分から5分ごとにバックアップするように設定されています。

    • 最初の2つ(11:10、11:15)は正常に実行されます
    • 3番目(計画11:20)は、ラップトップがスリープ状態であるために失敗します。ジョブはキューに入れられ、少し遅れて実行されます。ジョブは11:27に終了し、次のスケジュールされたバックアップは10:30に実行されます
    • その後、サーバーが利用できないため、11:35にスケジュールされたジョブは失敗します。 message = Trueを設定した場合:

      enter image description here

    • ジョブはキューに入れられ、後で11:41:44に正常に実行されます

    など

バックアップの開始時間をリセットする方法は?

対応するバックアップジョブ(next_backup_で始まる)のホームディレクトリにあるキューファイルを削除するだけで、現在の時刻がサイクルの新しい開始時刻になります。

ボンネットの下での仕組み

  • スクリプトが起動すると、最初のバックアップが作成されます(スクリプトの起動から1分以内に開始)。
  • ジョブが終了すると、スクリプトはfinished job + the set backup intervalの(開始)時間に合わせてキューファイルを作成します。
  • スクリプトは、1分に1回、キューファイルが「オーバータイム」であるかどうかを確認し、現在の時間とキューに入れられた時間を比較します。その場合、スクリプトはバックアップジョブを実行し、新しいキューを作成します。

    ラップトップ(クライアント)の電源をオフにするとどうなりますか?
    次に、作成された最新のキューファイルはスクリプトによって更新(上書き)されないため、コンピューターの起動(または起動)後の最初の遅延バックアップが実行されます。

    その後の次のバックアップキューの作成方法
    次のバックアップは常にバックアップ間隔の全ステップで計算され、常に「同相」になります。言い換えると;最初のバックアップが午後12:00に作成され、間隔を[24, 0]に設定した場合、次のバックアップは常に翌日の12:00にキューイングされます。

複数のバックアップジョブ?

スクリプトはほとんどの時間スリープします。そうでない場合は、1分に1回だけキューファイルを調べます。 SInceは、システムに対してnothingを意味します。それぞれが独自のログファイルとキューファイルを持つ複数のバックアップジョブを同時に実行することは非常に可能です。ログファイルとキューファイルの両方の名前はスクリプトの名前に基づいているため、必要なのは各スクリプトに一意の名前を付けることだけです。

1
Jacob Vlijm