이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

tar를 이용한 증분 백업 / 복원 + atime-preserve

유영재

서버를 운영하면서 가장 중요한 부분을 꼽으라면 백업을 꼽을 것이다. 안정적인 서버의 운영도 중요한 문제지만 소중한 데이터를 보호하는 것은 더 중요한 문제이다. 백업의 방법은 다양하다. RAID를 이용한 백업, sync를 통한 원격 백업, 파일을 압축해서 보관하는 백업, 백업 장비를 이용한 백업 등 많은 종류의 방법이 존재한다. 하지만 나는 백업의 방법 보다 백업 정책이 더 중요하다고 생각한다. 그중에서도 가장 중요한 원칙은 백업본은 원본 데이터와 함께 두지 않는다는 것이다. 다시말해 백업본은 원격지로 보내서 보관하여 원본 데이터가 있는 장소가 복구 불가능한 상황이 되더라도 데이터를 살릴 수 있어야 한다는 것이다. 나의 경우엔 다음과 같은 방식으로 백업본을 관리한다.

  • 원본 데이터를 해당 서버에서 1차 백업
  • 백업된 데이터를 백업 서버로 sync하여 2차 백업
  • 백업 서버의 데이터를 원격지 백업 서버로 sync하여 3차 백업

위 정책이 최선이라는 것은 아니지만 나에게 주어진 여건 내에서 할 수 있는 나름의 최선이라고 생각한다. 비용의 여유가 있다면 백업 솔루션을 사용하는 것이 더욱 좋겠지만 아직은 여건이 안된다.

이 글에서 다루고자하는 이야기는 1차 백업(원본 데이터 보관 서버 내 백업)시 증분 백업을 적용하는 부분이다. 증분 백업은 최종 백업된 상태 이후로 변경된 사항만 다시 백업하는 방식으로 full 백업에 비해 자원 소모가 적다. 그렇다고 지속적인 증분 백업만 할 수는 없으므로(복원 과정이 너무 길어질 수 있고 데이터의 손실 위험이 있을 수 있으므로) 나의 경우엔 일요일 새벽에 full 백업을 하고 주간 동안에는 증분 백업을 시행한다.

이러한 일련의 과정에서 가장 중요한 역할을 하는 것이 tar다. 다른 압축 프로그램들을 사용할 수도 있지만 대부분의 서버에서 기본적으로 설치하는 tar를 사용하는 것이 가장 범용적이라 나는 tar를 압축 백업에 사용한다.

참고로 여기서는 tar의 기본적인 사용법에 대해서는 다루지 않는다.


증분 백업의 원리에 대해서는 Using tar to Perform Incremental Dumpsbdsatish/gist:5650700 - Differential backups를 참고하면 도움이 될 것이다. 그리고 incremental backups with tar에는 기본적인 설명과 증분 백업용 스크립트를 안내하고 있다(참고만 하자).


그리고 미리 한가지를 설명할 것이 있다. 이것은 증분 백업과 직접적인 상관이 있는 것은 아니지만 필요한 경우가 있다. 그것은 다름이 아니라 백업시 파일의 atime을 보존하는 것이다. atime에 대해서는 Difference between mtime, ctime and atime를 참고하면 되는데 간단히 이야기하자면 해당 파일의 최종 access time을 뜻한다. 그렇다면 이게 백업과 무슨 상관이 있을까? tar를 사용해서 파일을 압축하면 atime이 압축 시점으로 변경된다. 그런데 백업을 하면서 atime은 그대로 보존해야 할 수도 있다. 예를들면 cache 파일의 경우 오랫동안 access가 없는 경우 삭제하는 것이 효율이 높이는 방법이므로 삭제시 검사를 위해 atime을 유지해야 한다. 그렇지 않으면 백업할 때마다 최종 access 시점이 변경되면서 언제 실제 access가 일어났었는지 확인할 수 없게 된다. 이 부분은 예시일 뿐이므로 그렇다고만 생각하자. 실제로 파일 access가 많은 곳에서는 성능을 높이기 위해 access time을 기록하지 않도록 설정하기 때문이다. 게다가 cache 파일은 손실되어도 문제가 되지 않으므로 백업에서 아예 제외하는 것이 맞을 것이다.

어쨌든 어떠한 목적으로 인해 백업시 atime을 유지하고자 한다면 tar 사용시 --atime-preserve 옵션을 주면 된다. 자세한 설명은 GNU tar: an archiver tool - 8.2 Handling File Attributes을 참고하자. 그런데 이 옵션을 사용할 경우 유의할 점이 하나 있다. --atime-preserve을 그냥 사용하면 --atime-preserve=replace로 동작하는데 이 경우는 증분 백업이 정상적으로 되지 않는다(계속 full 백업이 일어난다). 이 옵션은 대부분의 시스템에서 동작하나 증분 백업이 정상적이지 않을 수 있다고 메뉴얼에도 설명하고 있다. 그러면 atime을 유지하면서 증분 백업을 하려면 어떻게 해야하나? --atime-preserve=system 옵션을 사용하면 된다. 이 옵션에 대해서도 메뉴얼에 나와 있는데 시스템에 따라서 지원하지 않을 수도 있다고하니 유의하자(CentOS 7에서는 정상 동작했다).


이제부터는 증분 백업에 대해서 본격적으로 알아보자. 그런데 사실은 아주 간단하다. -g 옵션을 사용해서 백업 파일에 대한 기록을 남겨서 보관하는 것만 추가하면 기존의 tar 압축과 다를 것이 없다. 예를들어 아래의 스크립트를 보자.

tar -g backup.snap -zcf backup.tgz /backup/target/

위처럼 실행을 하면 /backup/target/ 폴더를 backup.tgz로 압축해준다. 그런데 일반 압축과의 차이는 backup.snap라는 파일이 하나 더 생긴다는 것이다. 이 파일은 압축한 파일들의 정보로 이후 증분 백업시 데이터로 사용되므로 보관해야 한다.

방금했던 압축은 증분이 아닌 full 백업이다. -g 옵션으로 넘겨주는 데이터가 없다면 이전 백업을 확인할 수 없으므로 full 백업을 한다. 자.. 이제 다시 아래의 스크립트를 보자.

cp backup.snap backup-1.snap
tar -g backup-1.snap -zcf backup-1.tgz /backup/target/

이번에도 최초의 full 백업과 별반 다르지 않다. 다만, snapshot 파일을 복사해서 사용했다는 차이만 있다. -g 옵션을 사용하면 백업시 참조만 하는 것이 아니라 이번 백업의 정보를 보관하므로 내용을 덮어쓰지 않도록 복사해서 사용한 것이다. 위 과정을 통해 압축한 결과 파일들을 보면 backup.tgz는 용량이 커도 backup-1.tgz는 용량이 적을 것이다(대상 폴더에 파일이 거의 없었다면 별반 차이가 나지 않을 수 있다). backup-1.tgz는 증분 백업된 파일이기 때문이다.

마지막으로 이 상태에서 다시 증분 백업하는 것을 한번만 더 보자.

cp backup-1.snap backup-2.snap
tar -g backup-2.snap -zcf backup-2.tgz /backup/target/

굳이 설명하지 않아도 보면 이해가 될 것이다. 그럼에도 한번 더 적는 이유는 cp backup.snap backup-2.snap 처럼 최초 snapshot에서 시작해야 하는지 마지막 snapshot에서 시작해야 하는지 헛갈릴 수 있기 때문이다. 사실 이 부분은 증분 백업 방식에 따라 차이가 나는 부분인데 뒤에 자세히 설명할 것이므로 일단은 그렇다고 생각하자.

이로써 증분 백업에 대한 기본적인 설명이 끝났다. 생각보다 아주 간단하다. 하지만 이해를 돕기 위해 아래에 예시를 하나 첨부한다.

우선 백업 과정이다. 이후의 예시들은 명령 중간 중간에 ls -al 등으로 test 폴더의 상태를 확인해 가면서 진행하면 이해가 더 쉬울 것이다.

mkdir test
echo "1" > test/1.txt
echo "2" > test/2.txt

tar -g backup.snap -zcf backup.tgz ./test/

echo "3" > test/3.txt
echo "4" > test/4.txt

cp backup.snap backup-1.snap
tar -g backup-1.snap -zcf backup-1.tgz ./test/

rm -f test/4.txt

cp backup-1.snap backup-2.snap
tar -g backup-2.snap -zcf backup-2.tgz ./test/

echo "5" > test/5.txt
echo "6" > test/6.txt

cp backup-2.snap backup-3.snap
tar -g backup-3.snap -zcf backup-3.tgz ./test/

이번엔 증분 백업된 내용에서 복원하는 과정이다.

# 복원 과정을 확인하기 위해 백업한 폴더는 일단 지운다.
rm -rf test

tar zxvf backup-0.tgz
ls -al ./test/

tar --incremental -zxvf backup-1.tgz
ls -al ./test/

tar --incremental -zxvf backup-2.tgz
ls -al ./test/

tar --incremental -zxvf backup-3.tgz
ls -al ./test/

그런데 이상하게 보이는 부분이 있을 수 있다. 압축할 때 사용했던 snapshot 파일을 복원시에는 사용하지 않는다는 것이다. 대신에 --incremental를 사용하고 있다. 이게 가능한 이유는 tgz 파일이 이미 자신에게 필요한 정보를 모두 가지고 있기 때문이다. 단, 정상적인 복원를 위해서는 백업한 순서대로 복원해야 한다. 그리고 --incremental 대신 --listed-incremental=/dev/null를 사용해도 무방하다. 그리고 이 부분에 대해서는 유의할 점이 한가지 있다. 다른 대부분의 설명글에 보면 snapshot 파일을 복원시에 사용한다. 그럼에도 불구하고 내가 이렇게 설명하는 것은 메뉴얼에 동일하게 나와있으며 위 실험 결과를 보면 알 수 있듯이 정상 동작하기 때문이다. 하지만 tar의 버전에 따른 차이일 수도 있으니 유의하자.

자.. 다시 백업 과정을 한번 생각해보자. 복원시에 snapshot 파일이 필요하지 않다면 굳이 백업시마다 snapshot 파일을 복사하거나 하지 않고 계속 같은 파일을 덮어쓰도록 하면 안될까? 결론부터 이야기 하자면 당연히 가능하다. 그럼 굳이 복사를 했던 이유는 뭘까? 이 부분은 증분 백업의 방법 중 level을 증가시키면서 백업하는 방법을 보여주기 위해서였다. 따라서 이 방법에서는 backup.snap 파일을 다음 백업에서도 사용해도 된다. 데이터를 덮어쓰겠지만 어짜피 level을 증가시키면서 백업하는 것이므로 상관없다.

이번엔 level-0와 level-1만 생성하는 백업 과정을 한번 보자. 이를 예를들어 설명하자면 다음과 같다. 최초에 full 백업(level-0) 한다. 다음 백업에서 증분 백업(level-1)으로 백업한다. 여기까지는 위의 방식과 같다. 하지만 이 다음부터 다르다. 위의 방법에서는 그 다음 백업은 level-1 상태를 기초로 level-2로 백업했다. 하지만 이 방법에서는 다시 level-0에서 level-1을 백업한다. 따라서 기존에 level-1 백업 파일은 필요가 없다. 아래의 스크립트를 보자.

이전 백업 방법과 유사하지만 자세히 보면 다른 백업 과정이다.

rm -rf *
mkdir test
echo "1" > test/1.txt
echo "2" > test/2.txt

tar -g backup-0.snap -zcf backup-0.tgz ./test/

echo "3" > test/3.txt
echo "4" > test/4.txt

cp backup-0.snap backup-1.snap
tar -g backup-1.snap -zcf backup-1.tgz ./test/
rm -f backup-1.snap

rm -f test/4.txt

cp backup-0.snap backup-1.snap
tar -g backup-1.snap -zcf backup-2.tgz ./test/
rm -f backup-1.snap

echo "5" > test/5.txt
echo "6" > test/6.txt

cp backup-0.snap backup-1.snap
tar -g backup-1.snap -zcf backup-3.tgz ./test/
rm -f backup-1.snap

자세히 보면 알겠지만 항상 backup-0.snap 파일을 기준으로 증분 백업을 하고 있다. 자세한 설명은 아래의 복원 과정을 보고 설명하겠다.

rm -rf test

tar zxvf backup-0.tgz
ls -al ./test/

tar --incremental -zxvf backup-3.tgz
ls -al ./test/

복원 과정이 이전 방법에 비해 대폭 줄었다. 최초 백업본(full 백업)을 복원하고 중간 단계없이 바로 마지막 백업본으로 복원하고 있다. 결과를 보면 알겠지만 정상적으로 복원되었다. 어떻게 이게 가능할까? 앞서 설명했던 것과 같이 이 방법은 증분 백업을 항상 최초 백업본을 기준으로 증분 백업을 했으므로 최초와 마지막 백업본만 있으면 복구가 가능하다.


짧게 설명하려던 것이 어쩌다보니 길어졌다. 그렇지만 정확한 내용이 전달되었을지 모르겠다. 나름 예시를 들어 설명한다고 했는데.

요약하자면 이렇다. tar를 이용한 증분 백업은 2가지가 있다. 지속적 증분 백업(level 0~max)과 1차 증분 백업(level 0~1). 이 이름들은 정식 명칭이 아니라 그냥 내가 설명하려고 붙인 이름이다. 지속적 증분 백업은 용량 효율이 좋을 수 있으나 복원 과정에서 모든 백업본이 필요하다. 그리고 원하는 지점까지만 복원하는 것이 가능하고 지속적인 변경만 백업하므로 효율(시간/공간)이 높다.

그러나 1차 증분 백업은 최초 백업과 마지막 백업만 있으면 복원이 가능하므로 관리가 용이하나 중간 지점으로의 복원이 불가능(중간 백업본도 모두 유지한다면 가능)하며 효율이 떨어질 수 있다.

그런데 이런 차이는 상황에 따라 다를 수 있으므로 차이를 이해하고 선택적으로 사용하면 된다. 그리고 내용이 길어질까봐 압축/복원시 사용하는 옵션들의 사용법에 대해서는 설명하지 않았으니 메뉴얼을 참고하자. 압축/복원시 다양한 옵션을 활용하면 보다 정교한 백업을 할 수 있다.

Comment