补充最后的BTRFS工具 btrfs -snp (这允许我们安排快照),我想共享一个新工具来本地或远程同步它们,以实现有效的数据冗余。
用 btrfs -snp 我们可以在不同的BTRFS系统中复制BTRFS快照,并以比使用传统方式高效得多的方式拥有版本化子卷的第二个副本 同步 .
产品特点
- 通过SSH进行本地或远程同步
- 简单语法
- 进度指示
- 支持 z 要么 pbzip2 压缩以节省带宽
- 保留政策
- 自动增量同步
- 克朗 友好
用法
语法类似于 scp
用法 : btrfs -sync [options] <src> [<src>...] [[user@]host:]<dir> -k|--keep NUM keep only last <NUM> sync'ed snapshots -d|--delete delete snapshots in <dst> that don't exist in <src> -z|--xz use z compression. Saves bandwidth, but uses one CPU -Z|--pbzip2 use pbzip2 compression. Saves bandwidth, but uses all CPUs -q|--quiet don't display progress -v|--verbose display more information -h|--help show usage <src> can either be a single snapshot, 要么 a folder containing snapshots <user> requires privileged permissions at <host> for the 'btrfs' command
例子
手册
将家庭快照同步到USB驱动器
# btrfs -sync /home/user/.snapshots /media/USBdrive/home-snapshots
将家庭快照同步到另一台计算机上的USB驱动器
# btrfs -sync /home/user/.snapshots user@server:/media/USBdrive/home-snapshots
将家庭的一个快照同步到另一台计算机上的USB驱动器
# btrfs -sync /home/user/.snapshots/monthly_2018-02-08_200102 user@server:/media/USBdrive/home-snapshots
仅将房屋的每月快照同步到另一台计算机上的USB驱动器
# btrfs -sync /home/user/.snapshots/monthly_* user@server:/media/USBdrive/home-snapshots
利用 –verboseÂ获得更多详细信息
# btrfs -sync --verbose --delete /home/user/.snapshots user@server:/media/USBdrive/home-snapshots * Skip existing '/home/user/.snapshots/monthly_2018-01-09_200102' * Skip existing '/home/user/.snapshots/monthly_2018-02-08_200102' * Skip existing '/home/user/.snapshots/weekly_2018-02-09_140102' * Skip existing '/home/user/.snapshots/weekly_2018-02-16_150102' * Skip existing '/home/user/.snapshots/weekly_2018-02-23_150102' * Skip existing '/home/user/.snapshots/weekly_2018-03-02_180102' * Skip existing '/home/user/.snapshots/daily_2018-03-03_000101' * Skip existing '/home/user/.snapshots/daily_2018-03-04_080101' * Skip existing '/home/user/.snapshots/daily_2018-03-05_100102' * Skip existing '/home/user/.snapshots/daily_2018-03-06_100102' * Skip existing '/home/user/.snapshots/daily_2018-03-07_110102' * Synchronizing '/home/user/.snapshots/hourly_2018-03-08_090101' using seed '.snapshots/hourly_2018-03-07_090101'... time elapsed [0:00:24] | rate [11.1MiB/s] | total size [ 132MiB] * Synchronizing '/home/user/.snapshots/hourly_2018-03-08_100101' using seed '.snapshots/hourly_2018-03-09_090101'... time elapsed [0:01:05] | rate [11.1MiB/s] | total size [ 275MiB] * Deleting non existent snapshots... Delete subvolume (no-commit): '/media/USBdrive/home-snapshots/hourly_2018-03-08_090101' Delete subvolume (no-commit): '/media/USBdrive/home-snapshots/hourly_2018-03-08_100101'
克朗
每天通过Internet同步,仅保留最后50个
cat > /etc/cron.daily/btrfs-sync <<EOF #!/bin/bash /usr/local/sbin/btrfs-sync --quiet --keep 50 --xz /home user@host:/path/to/snaps EOF chmod +x /etc/cron.daily/btrfs-sync
局域网中的每日同步,镜像快照目录
cat > /etc/cron.daily/btrfs-sync <<EOF #!/bin/bash /usr/local/sbin/btrfs-sync --quiet --delete /home user@host:/path/to/snaps EOF chmod +x /etc/cron.daily/btrfs-sync
安装
得到 脚本 并使其可执行。您可以分两行执行此操作,但最好先对其进行检查。不要盲目相信任何人。
sudo wget //raw.githubusercontent.com/nachoparker/btrfs-sync/master/btrfs-sync -O /usr/local/sbin/btrfs-sync sudo chmod +x /usr/local/sbin/btrfs-sync
建议设置指定的用户以接收具有以下内容的快照: 苏度 进入 btrfs Â命令。
- 创建一个 btrfs 两端的用户
$ sudo adduser btrfs
- 在发送机中创建公钥
$ sudo -u btrfs ssh-keygen
- 授予无密码访问权限 btrfs 远程计算机上的用户。
$ sudo -u btrfs ssh-copy-id btrfs @<ip>
- 授予权限 btrfs 用户使用 btrfs 在两端。建立档案
# visudo /etc/sudoers.d/90_btrfs-sync
与内容
btrfs ALL=(root:nobody) NOPASSWD:NOEXEC: /bin/btrfs
如果您想从 克朗 ,您可能必须首先安装它,因为某些发行版已经完全用systemd计时器代替了它。
在Arch Linux中,我就是这种情况。就我而言,我安装了 克罗妮 .
克罗妮 默认情况下会将输出记录到系统日志中,但是如果您想要旧样式的cron邮件,则可以设置电子邮件系统。
另外,请注意,您可以使用 慢性的 如果您只希望仅在出现问题时才进行日志记录。
与之比较 同步
这两种方法的主要区别在于BTRFS在 块 水平,而 同步 在 文件 水平。
因为 同步 适用于文件,它不会检测到诸如重命名或移动文件之类的内容,因此只能再次发送。另外,它需要分析目标位置上的现有文件,以查看它们是否已更新。
为了实现这一点,它可以分析修改日期和大小(相对较快),也可以比较两端文件块的校验和(健壮但较慢)。无论如何,当您将整个分区与成千上万个文件同步时,将产生大量的处理开销。
这种方法的优点是,您可以排除某些文件或文件夹,其中BTRFS按 全有或全无 时尚。
另一方面,BTRFS了解块,因为它是一个 COW文件系统,它已经知道快照和下一个快照之间已更改了哪些字节。如果我们重命名该文件,则仅更改了一些微小的元数据,并且BTRFS知道我们没有’只需再传输那几个字节,就无需再次传输整个文件。
当大文件在内部更改时(例如我们一直在使用的虚拟机的映像文件),也会发生同样的情况。
这就是为什么COW文件系统中的快照具有如此高的空间利用率的原因,这使我们能够创建大容量卷的即时安全副本,而在更改其中的文件时仅占用额外的空间。
显然,缺点是您两端都需要一个BTRFS文件系统,但是为什么我们要坚持现在拥有更现代,功能更强大的旧文件系统呢?
码
#!/bin/bash # # Simple 脚本 that synchronizes BTRFS snapshots locally 要么 through SSH. # 产品特点 compression, retention policy and automatic incremental sync # # 用法 : # btrfs -sync [options] <src> [<src>...] [[user@]host:]<dir> # # -k|--keep NUM keep only last <NUM> sync'ed snapshots # -d|--delete delete snapshots in <dst> that don't exist in <src> # -z|--xz use z compression. Saves bandwidth, but uses one CPU # -Z|--pbzip2 use pbzip2 compression. Saves bandwidth, but uses all CPUs # -q|--quiet don't display progress # -v|--verbose display more information # -h|--help show usage # # <src> can either be a single snapshot, 要么 a folder containing snapshots # <user> requires privileged permissions at <host> for the 'btrfs' command # # 克朗 example: daily synchronization over the internet, keep only last 50 # # cat > /etc/cron.daily/btrfs-sync <<EOF # #!/bin/bash # /usr/local/sbin/btrfs-sync -q -k50 -z /home user@host:/path/to/snaps # EOF # chmod +x /etc/cron.daily/btrfs-sync # # Copyleft 2018 通过 Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com> # GPL licensed (see end of 文件 ) * 利用 at your own risk! # # More at //ownyourbits.com # # help print_usage() { echo " 用法 : $BIN [options] <src> [<src>...] [[user@]host:]<dir> -k|--keep NUM keep only last <NUM> sync'ed snapshots -d|--delete delete snapshots in <dst> that don't exist in <src> -z|--xz use z compression. Saves bandwidth, but uses one CPU -Z|--pbzip2 use pbzip2 compression. Saves bandwidth, but uses all CPUs -q|--quiet don't display progress -v|--verbose display more information -h|--help show usage <src> can either be a single snapshot, 要么 a folder containing snapshots <user> requires privileged permissions at <host> for the 'btrfs' command Cron example: daily synchronization over the internet, keep only last 50 cat > /etc/cron.daily/btrfs-sync <<EOF #!/bin/bash /usr/local/sbin/btrfs-sync -q -k50 -z /home user@host:/path/to/snaps EOF chmod +x /etc/cron.daily/btrfs-sync " } echov() { [[ "$VERBOSE" == 1 ]] && echo "$@" || true; } #---------------------------------------------------------------------------------------------------------- # parse arguments BIN="${0##*/}" KEEP=0 ZIP=cat PIZ=cat SILENT=">/dev/null" OPTS=$( getopt -o hqzZk:dv -l quiet -l help -l z -l pbzip2 -l keep: -l delete -l 冗长的 -- "$@" 2>/dev/null ) [[ $? -ne 0 ]] && { echo "error parsing arguments"; exit 1; } eval set -- "$OPTS" while true; do case "$1" in -h|--help ) print_usage; exit 0 ;; -q|--quiet ) QUIET=1 ; shift 1 ;; -d|--delete ) DELETE=1 ; shift 1 ;; -k|--keep ) KEEP=$2 ; shift 2 ;; -z|--xz ) ZIP=xz PIZ=( z -d ); shift 1 ;; -Z|--pbzip2 ) ZIP=pbzip2 PIZ=( pbzip2 -d ); shift 1 ;; -v|--verbose) SILENT="" VERBOSE=1 ; shift 1 ;; --) shift; break ;; esac done SRC=( "${@:1:$#-1}" ) DST="${@: -1}" # detect remote dst argument [[ "$DST" =~ : ]] && { NET="$( sed 's|:.*||' <<<"$DST" )" DST="$( sed 's|.*:||' <<<"$DST" )" SSH=( ssh -o ServerAliveInterval=5 -o ConnectTimeout=1 "$NET" ) } [[ "$SSH" != "" ]] && DST_CMD=( ${SSH[@]} ) || DST_CMD=( eval ) #---------------------------------------------------------------------------------------------------------- # checks ## general checks [[ $# -lt 2 ]] && { print_usage ; exit 1; } [[ ${EUID} -ne 0 ]] && { echo "Must be run 如 root. Try 'sudo $BIN'"; exit 1; } ${DST_CMD[@]} true &>/dev/null || { echo "SSH访问错误 $NET" ; exit 1; } ## src checks while read entry; do SRCS+=( "$entry" ); done < <( for s in "${SRC[@]}"; do src="$(cd "$s" &>/dev/null && pwd)" || { echo "$s not found"; exit 1; } #abspath btrfs subvolume show "$src" &>/dev/null && echo "0|$src" || \ for dir in $( ls -drt "$src"/* 2>/dev/null ); do btrfs subvolume show "$dir" &>/dev/null || continue DATE="$( btrfs su sh "$dir" | grep "Creation time:" | awk '{ print $3, $4 }' )" SECS=$( date -d "$DATE" +"%s" ) echo "$SECS|$dir" done done | sort -V | sed 's=.*|==' ) [[ ${#SRCS[@]} -eq 0 ]] && { echo "no BTRFS subvolumes found"; exit 1; } ## check pbzip2 [[ "$ZIP" == "pbzip2" ]] && { type pbzip2 &>/dev/null && \ "${DST_CMD[@]}" type pbzip2 &>/dev/null || { echo "INFO: 'pbzip2' not installed on both ends, fallback to 'xz'" ZIP=xz PIZ=unxz } } ## use 'pv' command if available PV=( pv -F"time elapsed [%t] | rate %r | total size [%b]" ) [[ "$QUIET" == "1" ]] && PV=( cat ) || type pv &>/dev/null || { echo "INFO: install the 'pv' package in 要么 der to get a progress indicator" PV=( cat ) } #---------------------------------------------------------------------------------------------------------- # sync snapshots ## get dst snapshots ( DSTS, DST_UUIDS ) get_dst_snapshots() { local DST="$1" unset DSTS DST_UUIDS while read entry; do DST_UUIDS+=( "$( sed 's=|.*==' <<<"$entry" )" ) DSTS+=( "$( sed 's=.*|==' <<<"$entry" )" ) done < <( "${DST_CMD[@]}" " DSTS=( \$( ls -d \"$DST\"/* 2>/dev/null ) ) for dst in \${DSTS[@]}; do UUID=\$( sudo btrfs su sh \"\$dst\" 2>/dev/null | grep 'Received UUID' | awk '{ print \$3 }' ) [[ \"\$UUID\" == \"-\" ]] || [[ \"\$UUID\" == \"\" ]] && continue echo \"\$UUID|\$dst\" done" ) } ## sync incrementally sync_snapshot() { local SRC="$1" local ID LIST PATH_ DATE SECS SEED SEED_PATH SEED_ARG # detect existing SRC_UUID=$( btrfs su sh "$SRC" | grep "UUID:" | head -1 | awk '{ print $2 }' ) for id in "${DST_UUIDS[@]}"; do [[ "$SRC_UUID" == "$id" ]] && { echov "* Skip existing '$SRC'"; return 0; } done # try to get most recent src snapshot that exists in dst to use 如 a seed LIST="$( btrfs subvolume list -su "$SRC" )" SEED=$( for id in "${DST_UUIDS[@]}"; do ID=$(btrfs su sh -u "$id" "$SRC" 2>/dev/null|grep "UUID:"|head -1|awk '{print $2}') PATH_=$( awk "{ if ( \$14 == \"$ID\" ) print \$16 }" <<<"$LIST" ) DATE=$( awk "{ if ( \$14 == \"$ID\" ) print \$11, \$12 }" <<<"$LIST" ) [[ "$ID" == "" ]] || [[ "$PATH_" == "$( basename "$SRC" )" ]] && continue SECS=$( date -d "$DATE" +"%s" ) echo "$SECS|$PATH_" done | sort -V | tail -1 | cut -f2 -d'|' ) # incremental sync argument [[ "$SEED" != "" ]] && { SEED_PATH="$( dirname "$SRC" )/$( basename $SEED )" [[ -d "$SEED_PATH" ]] && SEED_ARG=( -p "$SEED_PATH" ) || \ echo "INFO: couldn't find $SEED_PATH. Non-incremental mode" } # do it echo -n "* Synchronizing '$src'" [[ "$SEED_ARG" != "" ]] && echov -n " using seed '$SEED'" echo "..." { btrfs send -q ${SEED_ARG[@]} "$SRC" \ | "$ZIP" \ | "${PV[@]}" \ | "${DST_CMD[@]}" "${PIZ[@]} | sudo btrfs receive \"$DST\" 2>&1 | grep -v '^At subvol '" \ || exit 1; } | grep -v "^At snapshot " get_dst_snapshots "$DST" # sets DSTS DST_UUIDS } #---------------------------------------------------------------------------------------------------------- # sync all snapshots found in src get_dst_snapshots "$DST" # sets DSTS DST_UUIDS for src in "${SRCS[@]}"; do sync_snapshot "$src" done #---------------------------------------------------------------------------------------------------------- # retention policy [[ "$KEEP" != 0 ]] && \ [[ ${#DSTS[@]} -gt $KEEP ]] && \ echov "* Pruning old snapshots..." && \ for (( i=0; i < $(( ${#DSTS[@]} - KEEP )); i++ )); do PRUNE_LIST+=( "${DSTS[$i]}" ) done && \ ${DST_CMD[@]} sudo btrfs subvolume delete "${PRUNE_LIST[@]}" $SILENT # delete flag [[ "$DELETE" == 1 ]] && \ for dst in "${DSTS[@]}"; do FOUND=0 for src in "${SRCS[@]}"; do [[ "$( basename $src )" == "$( basename $dst )" ]] && { FOUND=1; break; } done [[ "$FOUND" == 0 ]] && DEL_LIST+=( "$dst" ) done [[ "$DEL_LIST" != "" ]] && \ echov "* Deleting non existent snapshots..." && \ ${DST_CMD[@]} sudo btrfs subvolume delete "${DEL_LIST[@]}" $SILENT # License # # This 脚本 is free 软件 ; you can redistribute it and/or modify it # under the terms of the GNU General Public License 如 published 通过 # the Free Software Foundation; either version 2 of the License, 要么 # (at your option) any later version. # # This 脚本 is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY 要么 FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this 脚本 ; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330,
(已删除降价)
嘿,玉米片,
感谢您提供的出色工具!
不幸的是我无法使它工作
在两台计算机上创建了btrfs用户
获得与我执行的无密码SSH访问远程
ssh-copy-id -i〜/ .ssh / id_rsa.pub btrfs @
如
须藤-u btrfs ssh-copy-id btrfs @没有’t work for me.
现在能够不用密码访问ssh btrfs @并在远程上授予btrfs完整的sudo权限,我尝试运行
须藤btrfs-sync–详细/ media / backup_local btrfs @:/ media / backup_remote /
但是得到
SSH访问错误 btrfs @192.168.178.55。您是否具有无密码的登录设置,并具有/ media / backup_remote的足够权限?
在远程上,文件夹/ media / backup_remote由btrfs拥有,并且权限设置为777
我做错了什么?
本地计算机:PRETTY_NAME =”Raspbian GNU / Linux 9(拉伸)”
远程计算机:PRETTY_NAME =”Debian GNU / Linux 10(破坏者)”(通过softy安装的ncp)
谢谢
为什么脚本要通过sudo运行?如果您已经在两台机器上都为btrfs用户赋予了无密码sudo权限,那将有什么需求?我认为设置btrfs用户的确切目的是您不要’不必以root身份使用ssh。如果脚本是通过sudo运行的,那么ssh命令当然也由root执行,而不是btrfs执行。因此必须添加根’s将目标计算机上的.ssh / authorized_keys的pubkey而不是btrfs’就像上面的ssh-copy-id命令一样。
但是同样,如果整个脚本都是通过sudo运行的,那么没有必要向用户btrfs授予btrfs命令的sudo权限。这是什么主意?
关键是,仅允许btrfs用户对某些btrfs命令使用sudo。仍然您没有以root身份运行,并且您没有’不必弄乱根密钥。
嗨nachoparker,
我遇到了和约翰尼斯相同的问题。用户“btrfs”即使修改了sudoers,也不允许运行btrfs-sync。但是,如果您以root身份运行脚本,ssh当然也将以root身份运行。因此,您必须授予对root用户的无密码访问权限,而不是上面说明中所述的btrfs,否则我是否会丢失某些东西?
我认为Svitozars错误也是由此问题引起的。
这里的所有示例都是推送备份。
因此,我想知道,您能否以拉模式在备份服务器上运行btrfs-sync?
是的,您可以ðŸ™,
通常,这仅适用于卷/快照而不适用于BTRFS文件系统吗?假设我只想将一个BTRFS格式的磁盘同步到另一个磁盘(没有子卷或快照),并且我想利用COW的优势,这样就不会再次复制大的重命名文件。此脚本不能这样做吗?任何BTRFS工具都可以做到吗?谢谢!