몇개월 전에… ‘GNU Tar 을 이용해 백업하기‘ 라는 포스팅에서 GNUTar 을 이용해 증분 백업을 하는 내용을 올린 적이 있습니다. 이제 더 세련된 백업 방식으로 ‘백업하기’ 포스팅이 돌아왔습니다.
이번엔, Tar 이 아니라, ZFS 의 자체 기능을 사용해서 백업을 해보겠습니다. 더 세련되고, 더 깔끔한 방법으로 말입니다.
‘GNU Tar 을 이용해 백업하기‘ 포스팅을 보고 오시는게 좋습니다.
ZFS 스냅샷 전송
ZFS 스냅샷 백업의 핵심은 ZFS 스냅샷 전송이라는 기능입니다. 말 그대로 스냅샷이 찍힌 시점의 데이터를 전송하는 기능입니다. zfs send 로 스냅샷을 읽어 표준 출력으로 전송하고, zfs receive 로 표준 입력을 받아 스냅샷을 작성합니다. 여기서 포인트는 표준 입출력이라는 것인데, 이 때문에 출력을 파이프로 받아서 처리할 수도 있고, ssh 등을 이용해 외부로 전송할 수도 있고, 아예 파일에 쓸 수도 있습니다.
이 때문에 몇가지 장점이 있는데,
- ZFS 데이터의 속성을 그대로 전송할 수 있으며
- ZFS 의 기본 기능이며
- Zvol도 백업할 수 있고
- 증분 백업도 가능한데다
- ZFS 백업 스트림 자체를 파일로 저장할수도 있습니다.
직접 해 보며 익히기
스냅샷 전송과 수신
이미 만들어 둔 데이터셋이 하나 있군요. 이걸 이용해보도록 하겠습니다.
Data/persnal/admion
오늘 zfs 스냅샷 백업을 도와 주실 데이터셋을 모셨습니다.
zfs snapshot Data/Persnal/admion@backup-first
Data/Persnal/admion@backup-first 라는 스냅샷을 찍었습니다.
zfs send Data/Persnal/admion@backup-first > /mnt/Data/Persnal_backup.snap
표준 출력으로 보내기 때문에 리다이렉트로 /mnt/Data/Persnal_backup.snap 이라는 파일에 쓰는 것을 볼 수 있습니다. 간단하죠?
그럼 이걸 다른 pool 에다 복구를 해 볼까요.
zfs recv TEMP/Persnal < /mnt/Data/Persnal_backup.snap
TEMP pool 의 Persnal 데이터셋에 받습니다. 표준 입력으로 파일을 받는 것을 볼 수 있습니다.
이제 제대로 받아졌는지 확인해 볼까요.
ls -al /mnt/TEMP
Persnal 이라는 디렉토리가 만들어진걸 볼 수 있습니다. admion 데이터셋을 받아 생성한 것입니다. 짠! 편리하죠?
zfs list -t snapshot
ZFS 스냅샷의 리스트를 보면
Data/Persnal/admion@backup-first
TEMP/Persnal@backup-first
이라는 스냅샷이 있을 것입니다. 여기서 알 수 있듯이, 말 그대로 스냅샷 데이터를 받습니다. 스냅샷이 찍힌 그 상태를 전송하는 것입니다.
재귀적 전송
zfs snapshot -r System/Logs@first
Data/persnal 데이터셋을 재귀적으로 찍었습니다.
zfs send -R System/Logs@first > /mnt/Data/Log.snap
-R 옵션을 주어 하위 스냅샷까지 모두 전송해 Log.snap 으로 저장했습니다.
zfs recv TEMP/Log < /mnt/Data/Log.snap
zfs recv 로 받아 주면 끝!
ls -al /mnt/TEMP/Log/
dataset 트리 구조도 그대로 유지하고 있는 것을 알 수 있습니다.
잘못 된 디렉토리를 선택한다면?
만약 zfs recv 를 했을 때 잘못 된 디렉토리를 선택한다면 어덯게 될까요? 덮어쓸까요? 오류가 날까요? 아니면 원래 있던 데이터와 공존하게 될까요?
백문이 불여일견! 직접 해 보도록 하겠습니다.
zfs recv TEMP/Persnal < /mnt/Data/Log.snap
must specify -F to overwrite it warning: cannot send 'System/Logs@first': signal received
-F 옵션을 줘서 덮어쓰라고 합니다. -F 옵션을 주면 기존에 있던 내용을 덮어씁니다.
cannot receive new filesystem stream: destination has snapshots (eg. TEMP/Persnal@backup-first) must destroy them to overwrite it warning: cannot send 'System/Logs@first': signal received
-F 옵션을 줘서 전송해봤더니 이번에는 스냅샷이 있다고 합니다. 자신이 의존하는 스냅샷이 있는게 아니라면, 해당 스냅샷이 제거되어야만 recv 로 받을 수 있습니다.
zfs destroy TEMP/Persnal@backup-first
스냅샷을 지우고
zfs recv -F TEMP/Persnal < /mnt/Data/Log.snap
다시 받아 보면
ls -al /mnt/TEMP/Persnal
기존 데이터가 사라지고 Logs 데이터셋의 내용이 있는 것을 알 수 있습니다.
zvol 백업
zvol 도 마찬가지로 백업할 수 있습니다. 이번에는 zvol 을 다른 pool 로 복사해 보도록 하겠습니다.
기존에 생성해놓은 Windows_icsci 이라는 zvol 이 있다고 가정하겠습니다.
zfs snapshot Data/Windows_icsci@now
zvol의 스냅샷을 찍은 다음, 이 zvol 을 TEMP pool 아래로 복사해보도록 하겠습니다.
zfs send Data/Windows_icsci@now | zfs recv TEMP/copied_zvol
| 파이프 명령으로 zfs send 로 보낸 것을 zfs recv 로 바로 받는 것을 볼 수 있습니다.
zfs list -t volume
위 명령으로 같은 zvol 이 복제되었음을 알 수 있습니다.
증분 백업
백업에 있어서 증분 백업이 빠지면 섭하죠. 증분 백업을 해보도록 하겠습니다.
System/Logs 를 증분 백업 하도록 하겠습니다.
zfs snapshot System/Logs@second
second 라는 이름으로 스냅샷을 찍어 주고
zfs send -R -I System/Logs@first System/Logs@second > /mnt/TEMP/Log_inc_1_backup.snap
-I 옵션을 주면 증분 백업을 할 수 있습니다. zfs -I 스냅샷1 스냅샷2 형태의 명령인데, 스냅샷1 에서 스냅샷2까지 변경된 내용만 전송합니다.
내친 김에 하나 더 만들어보도록 하겠습니다.
zfs snapshot System/Logs@third
zfs send -R -I System/Logs@second System/Logs@third > /mnt/TEMP/Log_inc_2_backup.snap
증분 백업 받기
지금 가지고 있는 스냅샷 파일은 3개입니다. 하나는 System/Logs@first 스냅샷을 전부 스냅샷 뜬 파일이고, 두 번째는 @first 부터 @second 까지 변경된 부분만 스냅샷을 뜬 파일이고, 세 번째는 @second 부터 @third 까지 변경된 부분만 스냅샷을 뜬 파일입니다.
받을 때는 전체 백업 -> 첫번째 증분 -> 두번째 증분… 이렇게 순서대로 받으면 됩니다.
증분백업이라는 것이 늘 그렇듯 순서대로 받아 주시면 됩니다.
zfs recv TEMP/restore < /mnt/Data/Log.snap
첫 번째 받고
zfs recv TEMP/restore < /mnt/TEMP/Log_inc_1_backup.snap
두 번째 받고
zfs recv TEMP/restore < /mnt/TEMP/Log_inc_2_backup.snap
세 번째 받아주면 됩니다.
자동 백업 스크립트 만들기
이제 자동 백업 스크립트를 짜 보도록 하겠습니다.
- 자동으로 스냅샷을 찍어서
- 1달에 1번 전체 백업을 하고
- 1주일마다 증분 백업을 하고
- rclone 으로 backblaze 로 백업을 하고
- 백업을 하기 위해 만든 스냅샷은 삭제합니다.
전체 백업 스크립트
vi /mnt/SysData/data/script/zfs_fullbackup.sh
#!/bin/bash for before_snapshots in $(zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_); do zfs destroy -r "${before_snapshots}" done zfs snapshot -r ${1}@backup_`date \+%Y\%m\%d\%H\%M\%S` last_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1` snapname=`echo ${1} | sed 's/\//-/g'` zfs send -R ${last_snap} | openssl enc -aes-256-cbc -salt -a -md sha256 -pass file:${3} | gzip -9 > ${2}/${snapname}.snap.aes.gz chown -R bk_user:bk_user ${2}/${snapname}.snap.aes.gz chmod 600 ${2}/${snapname}.snap.aes.gz export _file_loc=${2}/${snapname}.snap.aes.gz export _b2_dest=${4} su bk_user -c 'rclone --fast-list --transfers 4 move ${_file_loc} ${_b2_dest} -q'
전체 백업 스크립트를 만들었습니다.
zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_
첫 번째 인자로 주어지는 dataset 의 스냅샷 중 backup_ 접두사가 붙은 스냅샷만 찾아
zfs destroy -r “${before_snapshots}”
제거해 줍니다.
이전에 만들어진 백업용 스냅샷을 모두 지우기 위해서입니다.
zfs snapshot -r ${1}@backup_`date \+%Y\%m\%d\%H\%M\%S`
현재 시간을 이름으로 하여 스냅샷을 찍습니다.
last_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1`
마지막에 찍은 백업 스냅샷의 이름을 구합니다. 이 경우, 바로 앞에서 만든 스냅샷입니다.
snapname=`echo ${1} | sed ‘s/\//-/g’`
스냅샷 파일의 이름을 설정합니다. / 을 – 으로 바꾸어 줍니다. 파일 이름 문제를 피하기 위해서입니다.
zfs send -R ${last_snap} | openssl enc -aes-256-cbc -salt -a -md sha256 -pass file:${3} | gzip -9 > ${2}/${snapname}.snap.aes.gz
스냅샷을 Openssl로 암호화하고 gzip 으로 압축해서 저장합니다.
chown -R bk_user:bk_user ${2}/${snapname}.snap.aes.gz
chmod 600 ${2}/${snapname}.snap.aes.gz
권한을 주고
export _file_loc=${2}/${snapname}.snap.aes.gz
export _b2_dest=${4}
백업을 위해 환경 변수를 설정한 다음
su bk_user -c ‘rclone –fast-list –transfers 4 move ${_file_loc} ${_b2_dest} -q’
rclone 으로 backblaze 로 백업합니다.
증분 백업 스크립트
vi /mnt/SysData/data/script/zfs_fullbackup_inc.sh
#!/bin/bash before_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1 | cut -d'@' -f 2` zfs snapshot -r ${1}@backup_`date \+%Y\%m\%d\%H\%M\%S` last_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1` snapname=`echo ${1} | sed 's/\//-/g'`_inc zfs send -R -I ${before_snap} ${last_snap} | openssl enc -aes-256-cbc -salt -md sha256 -a -pass file:${3} | gzip -9 > ${2}/${snapname}.snap.aes.gz chown -R bk_user:bk_user ${2}/${snapname}.snap.aes.gz chmod 700 ${2}/${snapname}.snap.aes.gz export _file_loc=${2}/${snapname}.snap.aes.gz export _b2_dest=${4} su bk_user -c 'rclone --fast-list --transfers 4 move ${_file_loc} ${_b2_dest} -q' zfs destroy -r ${1}@${before_snap}
거의 비슷하지만 약간 다릅니다.
before_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1 | cut -d’@’ -f 2`
마지막에 찍힌 스냅샷의 이름을 뽑아 낸 다음
zfs snapshot -r ${1}@backup_`date \+%Y\%m\%d\%H\%M\%S`
새로운 스냅샷을 찍어 주고
last_snap=`zfs list -t snapshot -o name -s creation -r ${1} | grep ${1}@backup_ | tail -1`
방금 찍은 스냅샷의 이름을 받아옵니다.
snapname=`echo ${1} | sed ‘s/\//-/g’`_inc
스냅샷 파일의 이름을 설정하고
zfs send -R -I ${before_snap} ${last_snap} | openssl enc -aes-256-cbc -salt -md sha256 -a -pass file:${3} | gzip -9 > ${2}/${snapname}.snap.aes.gz
-I 옵션으로 증분 전송합니다.
chown -R bk_user:bk_user ${2}/${snapname}.snap.aes.gz
chmod 700 ${2}/${snapname}.snap.aes.gz
권한 주고
export _file_loc=${2}/${snapname}.snap.aes.gz
export _b2_dest=${4}
환경 변수 설정 후
su bk_user -c ‘rclone –fast-list –transfers 4 move ${_file_loc} ${_b2_dest} -q’
전송합니다.
전송이 끝났으면
zfs destroy -r ${1}@${before_snap}
이전에 찍은 스냅샷은 제거해 줍니다.
사용법
zfs_backup.sh 백업할데이터셋 임시위치 암호파일 backblaze목적지
예를 들어, Data/persnal 을 백업하겠다고 하고, 임시 파일 위치는 /mnt/TEMP/backup 이며, 암호 파일은 /mnt/System/passwd/.passwd 에 있고, 전송할 위치는 b2_backup:ZFS-Backup/persnal 이라면
zfs_backup.sh Data/persnal /mnt/TEMP/backup /mnt/System/passwd/.passwd b2_backup:ZFS-Backup/persnal
입니다.
cron 에 등록하기 위한 스크립트
vi /mnt/System/data/script/persnal_zfs_backup.sh
#!/bin/bash tmp_backup_loc="/mnt/TEMP/Backup_Temp" passwd_file_loc="/mnt/System/Data/passwd/.passwd" destination_loc="b2_zfs:ZFS-Snap-Backup/Persnal" bash /mnt/SysData/data/script/zfs_fullbackup.sh Data/persnal ${tmp_backup_loc} ${passwd_file_loc} ${destination_loc} &
t다른 것을 백업할 때도 위와 동일한 방법으로 스크립트를 짜면 됩니다. 증분 백업 또한 사용 방법이 완전히 동일하니 zfs_fullbackup.sh 을 zfs_fullbackup_inc.sh 으로 바꿔 주기만 하면 됩니다.
cron 에 등록
GNUTar 을 이용해 백업하기 – cron 에 등록하기 를 참고해서 cron에 전체 백업과 증분 백업을 등록해 주세요. 크론 주기와 실행 시간은 적절하게 조정해 주시고, 실행할 유저는 root 로 해 주세요. 스냅샷 전송은 root 만 할 수 있습니다.