Announcement

Collapse
No announcement yet.

Automating snapshots and backups

Collapse
This topic is closed.
X
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Automating snapshots and backups

    I've long been putting this project off, but I have finally started on it.

    My goal is to do all this automatically;
    1. Make a daily snapshot of any/all subvolumes and keep a rotating seven days' worth.
    2. Make a weekly backup and keep the last two.
    3. Keep a monthly backup for three months.


    I will be doing this on my both server and my desktop, but developing and testing on my desktop. I rarely write scripts well enough to be "transportable" to other systems, so if you want to use any of these you'll need to edit.

    "Bootstrapping" your system:

    The scripts will require running as root and that your BTRFS root filesystem is mounted so the subvolumes can be accessed. Fortunately this part is simple.

    Mounting your root BTRFS filesystem;
    Make a target folder (I use /subvol). Edit fstab, copy the line that mounts your root file system, delete the subvolume reference, and mount at the new location. Here's mine for an example;
    Code:
    UUID=60834933-1e99-4d5c-922c-9abbc373e172 /            btrfs rw,noatime,space_cache,compress=lzo,autodefrag,subvol=@  0       1
    UUID=60834933-1e99-4d5c-922c-9abbc373e172 /subvol   btrfs rw,auto,noatime,space_cache,compress=lzo,autodefrag          0       0
    Notice I added the "auto" option to the /subvol mount. This mounts /subvol at boot time. The "/" mount doesn't need the auto option because it has to be mounted to boot.
    If you would prefer to not mount /subvol at boot and would rather have the script mount it when needed, don't use "auto". You could allow the script to do the whole mounting process, but I find defining it in fstab to be easier to control the mount.

    TIP: In my opinion, having your BTRFS root file system mounted (or mountable) at a specific location is a good practice. This allows easier access to your subvolumes so you can do maintenance or random snapshots when needed. If you want this access but still don't want it mounted at boot time, add the "user" option so you can mount and umount it without "sudo".

    A backup location;
    We'll be using BTRFS send|receive to make these backups. You will need to have a BTRFS backup location either on another drive on your PC, a removable drive, or a networked location. I do not recommend using a separate partition on the same drive as that wouldn't be much of a backup.

    I have a desktop PC and a server so my scripts will use a local drive and a networked drive for backups. If you wish to use a removable device such as a thumb drive or USB drive, it is recommended to use BTRFS send to a file and copy the file to the USB device rather than formatting the USB device to BTRFS. The reason is USB devices can too easily drop off during a long access process and if this happens during a BTRFS receive operation the backup can be incomplete and you may not get a warning that this has occurred, whereas a simple file copy operation will handle a momentary pause with much better chance of success. If anyone is interested in how using a USB device this way might be done, ask and I will expand on it.

    Automation;
    To ensure the scripts run, I use anacron and cron for script launch.
    • cron.daily will run the snapshot script - task #1.
    • cron.weekly will run task #2
    • cron.monthly will run task #3

    Installing anacron will ensure these tasks are run if the computer not available during the specified times. I'm not going to explain cron or anacron here, but if you're unsure of how they work, I encourage you to read up on them.

    Finally, I have several installs on my single btrfs file system so I can multi-boot. To do this (topic of another post) I have renamed my subvolumes from the defaults of "@" and "@home" and you will see this in my scripts. I also run stand-alone grub from a subvolume as well. Most of these will be included in my snapshot and backup routines.

    On to the scripts. I will post them separately and only once I've tested them fully. Remember these are designed to work on my setup so modifications will be needed if you are considering adapt them to your system. I welcome input about anything in the scripts that might make them better, faster, or more robust.

    Please Read Me

    #2
    The daily snapshot script:
    Code:
    #!/bin/bash#
    # IMPORTANT:
    # This script requires the root btrfs file system be mounted somewhere before running.
    # Alternately, have the mount declared in fstab and the script will mount it.
    #
    exec 1> >(logger -s -t $(basename $0)) 2>&1 # Log the script activity
    
    
    ## Set the variables. Edit this section as needed
    # declare an array variable of all target subvolumes
    declare -a SUBVLIST=("@grub" "@KDEneon1804" "@KDEneon1804_home" "@KDEneon" "@KDEneon_home" "@Kubuntu_1804")
    
    
    # other variables
    NOTIFYUSER="stuart"                     # the username to get error messages
    NOTIFYDISPLAY=":0.0"                    # the above user's X session
    source_folder="/subvol/"                # Root file system mount location
    snapshot_folder="/subvol/snapshots/"    # Snapshots folder
    addname="_daily_"                       # This is added to the subvolume name of the daily snapshots
    lastsnapnum=6                           # Last snapshot number to be saved before moving to backup status
    
    
    # Verify the root BTRFS file system is mounted and mount it if not, or fail;
    if [ -d "$source_folder" ]; then
        if ! mountpoint -q "$source_folder"; then
            mount "$source_folder"
            if ! mountpoint -q "$source_folder"; then 
                su $NOTIFYUSER -c "export DISPLAY=${NOTIFYDISPLAY}; /usr/bin/notify-send -i ksnapshot -t 0 'Daily snapshots:' 'Daily snapshot operation failed - no mountable subvolume folder.'"
                exit 1
            fi
        fi
        else 
            su $NOTIFYUSER -c "export DISPLAY=${NOTIFYDISPLAY}; /usr/bin/notify-send -i ksnapshot -t 0 'Daily snapshots:' 'Daily snapshot operation failed - subvolume folder incorrect.'"
            exit 1
    fi
    
    
    ## Begin process
    # loop through the list of subvolumes
    for SUBV in "${SUBVLIST[@]}"; do
        SUBVPATH="$source_folder""$SUBV"
        SUBVSNAP="$snapshot_folder""$SUBV"
    
    
    # Move last daily snapshot to backup status
        if [[ -d "$SUBVSNAP""$addname""$lastsnapnum" ]]; then
            btrfs su de -c "$SUBVSNAP""_backup"
            mv "$SUBVSNAP""$addname""$lastsnapnum" "$SUBVSNAP""_backup"
            btrfs pr set -ts "$SUBVSNAP""_backup" ro true
        fi
    
    
    # Roll the current snapshot names by adding 1 to each trailing number
        for ((i="$lastsnapnum-1";i>=0;i--)); do
            if [[ -d "$SUBVSNAP""$addname"$i ]]; then
            mv "$SUBVSNAP""$addname"$i "$SUBVSNAP""$addname"$(($i+1)); fi
        done
    
    
    # Take new read-only snapshot
        btrfs su sn "$SUBVPATH" "$SUBVSNAP""$addname""0"
        touch "$SUBVSNAP""$addname""0"
    done
    
    
    # Notify the user that the job is done
    su $NOTIFYUSER -c "export DISPLAY=${NOTIFYDISPLAY}; /usr/bin/notify-send -i ksnapshot -t 0 'Daily snapshots:' 'Daily snapshot operation complete.'"
    
    
    # Script complete
    exit 0
    Takes a snapshot every morning of all the subvolumes listed in the array variable defined in this way:
    Code:
    # declare an array variable of all target subvolumes
    declare -a SUBVLIST=("@grub" "@KDEneon1804" "@KDEneon1804_home" "@KDEneon" "@KDEneon_home" "@Kubuntu_1804")
    and then used this way:
    Code:
    for SUBV in "${SUBVLIST[@]}"; do
    To my eye, it's cleaner to have the list at the top of the script rather than buried further down.

    Notes and the sequence of events for this script. For each subvolume in the array list;
    1) Prepare a snapshot for use as a backup.
    I wanted a single week of snapshots, then a backup so the script creates snapshots 0 to 5 (that's six snapshots) then names the 7th as "backup" and sets it as read-only to prepare it for the send|receive operation. The backup snapshot will be created daily but may not be used every day so it deletes the existing backup snapshot and replaces it with snapshot "6". You could lengthen or shorten the cycle by changing the "lastnumsnap" to a different number.
    2) Rename all existing snapshots (eventually 6 plus the backup snapshot) by increasing the trailing digit in the snapshot name by one.
    Today's snapshot ends in "0". yesterday's in "1", the day before in "2", and so on. Again, the length of this cycle is set by "lastnumsnap". "6" in this variable is 7 snapshots because I start with 0.
    3) Take a new "0" snapshot for today.

    Note that there a three locations where I have it notify the user set in the variables (thanks again kubicle) to let you know if the thing worked or failed. I should probably increase the amount of error checking and notifications, but this seemed like enough at this point.
    Last edited by oshunluvr; Dec 31, 2018, 09:48 AM.

    Please Read Me

    Comment

    Working...
    X