ZFS 스냅샷 백업

This entry is part 46 of 48 in the series FreeNAS 서버 만들기

몇개월 전에… ‘GNU Tar 을 이용해 백업하기‘ 라는 포스팅에서 GNUTar 을 이용해 증분 백업을 하는 내용을 올린 적이 있습니다. 이제 더 세련된 백업 방식으로 ‘백업하기’ 포스팅이 돌아왔습니다.

이번엔, Tar 이 아니라, ZFS 의 자체 기능을 사용해서 백업을 해보겠습니다. 더 세련되고, 더 깔끔한 방법으로 말입니다.

GNU Tar 을 이용해 백업하기‘ 포스팅을 보고 오시는게 좋습니다.

ZFS 스냅샷 전송

ZFS 스냅샷 백업의 핵심은 ZFS 스냅샷 전송이라는 기능입니다. 말 그대로 스냅샷이 찍힌 시점의 데이터를 전송하는 기능입니다. zfs send 로 스냅샷을 읽어 표준 출력으로 전송하고, zfs receive 로 표준 입력을 받아 스냅샷을 작성합니다. 여기서 포인트는 표준 입출력이라는 것인데, 이 때문에 출력을 파이프로 받아서 처리할 수도 있고, ssh 등을 이용해 외부로 전송할 수도 있고, 아예 파일에 쓸 수도 있습니다.

이 때문에 몇가지 장점이 있는데,

  1. ZFS 데이터의 속성을 그대로 전송할 수 있으며
  2. ZFS 의 기본 기능이며
  3. Zvol도 백업할 수 있고
  4. 증분 백업도 가능한데다
  5. 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. 자동으로 스냅샷을 찍어서
  2. 1달에 1번 전체 백업을 하고
  3. 1주일마다 증분 백업을 하고
  4. rclone 으로 backblaze 로 백업을 하고
  5. 백업을 하기 위해 만든 스냅샷은 삭제합니다.

전체 백업 스크립트

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 만 할 수 있습니다.

시리즈 네비게이션<< FreeNAS 에 Boinc 설치하기InfluxDB 와 Grafana 를 이용한 FreeNAS 모니터링 >>

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다