홈서버와 클라우드 VM 인스턴스의 백업 시스템 구성
BorgBackup, restic를 이용하여 효율적으로 백업하고 자동화하기
BorgBackup
이제까지 rsync
을 통해 3일 간격으로 서비스 폴더를 HDD에 총 30일 치 백업을 했다. 원본 파일의 무결성이 보장된다면, 백업 중 하나가 꺠져도 그나마 안전하지만 무식한 방법이므로 하드 디스크 용량을 상당히 잡아먹는다. 서비스가 차지하는 공간이 늘어나면 이런 식으로는 저장 공간이 남아나지 않을 것이므로, 더 효율적인 방법이 필요해서 고른게 BorgBackup
이다.
BorgBackup
은 압축 기술, 중복 제거와 파일의 변경된 부분만 백업(증분 백업)해 저장공간을 효율적으로 사용하는 백업 솔루션이다. 이외에도 백업 데이터의 암호화, 스냅샷 기능을 지원한다. restic과 비슷하지만 단순 파일 시스템 기반이라서 Amazon S3, Cloudflare R2 등의 오브젝트 스토리지에 백업은 어렵다.
설치
1
$ sudo apt install borgbackup
2.x 버전은 포스팅 시점에 안정화 버전이 아니므로 apt 저장소에서 올라와있는 안정화 버전(1.2.8
)을 사용하겠다. 나중에 이 글을 볼 사람이 있을지는 모르겠지만, 체인지 로그를 보면 2.x 버전은 1.x 버전과 큰 차이가 있으니 정식 버전이 2.x인 미래에서 이 글을 본다면 주의…
사용하기
- 저장소 생성
1
$ borg init --encryption=repokey <백업 디렉토리>
--encryption=repokey
: 저장소를 암호화하고 키를 저장소에 포함. 원하는 비밀번호를 입력해야한다.
- 특정 디렉토리에 백업
1
$ borg create <백업 디렉토리>::<백업 스냅샷 이름> <백업할 디렉토리>
백업할 디렉토리의 파일을 백업 디렉토리로 백업한다.
1 2
# 예시 $ borg create --verbose --stats --compression lz4 /mnt/backup/service::backup-2025-03-08 /home/hgsanguk/service
--verbose
: 백업 진행 상황을 상세 출력--stats
: 백업 완료 후 백업 크기, 중복 제거율, 압축율 등의 통계를 출력--compression lz4
: lz4 방식으로 백업 파일 압축
ssh
를 통하여 원격 서버에 백업1
(작성 중)
- 백업 내용 확인
1
$ borg info /mnt/backup/service::backup-2025-03-08
- 백업 복원
1
$ borg extract /mnt/backup/service::backup-2025-03-08 <복원을 원하는 파일 혹은 경로>
이 명령어를 사용한 경로에 바로 복원이 된다.
자동화
서비스가 실행 중인 상태에서 백업을 할 경우 동시성 문제를 생각해야한다. 예시로 데이터베이스를 이용하는 서비스가 있다고 하면, 데이터베이스 트랜잭션 중 백업을 시작했을 때 백업에 무결성과 일관성에 문제가 생길 수 있다. 따라서 백업 시작 전 서비스를 중단하고 백업하는 것이 좋다.
내 홈서버의 대부분 서비스는 Docker 컨테이너 안에서 실행된다. 마찬가지로 실행 중 백업 시 문제가 생길 수 있기 때문에 실행 중인 서비스를 잠시 중단한 다음, 호스트에 마운트된 컨테이너의 파일을 백업하는 과정을 스크립트를 작성해서 crontab
에 등록했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
# 환경 변수
REPO="/mnt/backup/service" # 저장소 경로
SOURCE="/home/hgsanguk/service" # 백업할 데이터 경로
LOGFILE="/var/log/borgbackup.log" # 스크립트의 로그 파일 경로
export BORG_PASSPHRASE="비밀번호" # 저장소를 암호화 했을 경우 비밀번호
# 백업 과정 시작
RUNNING_CONTAINERS=$(docker ps -q) # 실행 중인 Docker 컨테이너 목록 불러오기
echo "[$(date)] 백업을 시작합니다." | tee -a $LOGFILE
# 1. 실행 중인 모든 Docker 컨테이너 중지
if [ ! -z "$RUNNING_CONTAINERS" ]; then
echo "[$(date)] 실행 중인 컨테이너를 중지합니다." | tee -a $LOGFILE
docker stop $RUNNING_CONTAINERS >> $LOGFILE 2>&1
fi
# 2. BorgBackup 실행
echo "[$(date)] BorgBackup을 실행합니다." | tee -a $LOGFILE
borg create --verbose --stats --compression lz4 \
$REPO::backup-$(date +%Y-%m-%d) \
$SOURCE >> $LOGFILE 2>&1
# 3. 오래된 백업 정리 (최근 7개의 백업만 남겨두도록 설정했다.)
echo "[$(date)] 오래된 백업을 정리합니다." | tee -a $LOGFILE
borg prune --keep-last 7 $REPO >> $LOGFILE 2>&1
# 4. 중지한 컨테이너 다시 시작
if [ ! -z "$RUNNING_CONTAINERS" ]; then
echo "[$(date)] 중지한 컨테이너를 다시 시작합니다." | tee -a $LOGFILE
docker start $RUNNING_CONTAINERS >> $LOGFILE 2>&1
fi
echo "[$(date)] 백업이 완료되었습니다." | tee -a $LOGFILE
restic (w/ Google Cloud Platform)
restic 로고. 오리가 비 맞지 말라고 우산을 씌워주는 모습이다…
restic
은 BorgBackup
과 마찬 가지로 중복 제거, 증분 백업, 암호화 및 스냅샷 기능을 지원한다. BorgBackup
과 달리 압축 기능은 없으며 파일 단위의 백업이 아니기 때문에 오브젝트 스토리지에 백업이 가능하다. 클라우드에서 운영하는 간단한 서비스를 비용이 상당한 스냅샷과 블록 디스크 대신 오브젝트 스토리지 서비스에 백업하는데 사용할 것이다. 사실 GCP 프리티어 클라우드 스토리지가 놀고 있어서 어떻게든 써먹으려고 한다…
위에서 언급했듯 파일 시스템을 사용하지 않기 때문에 BorgBackup
에서 백업 내 파일을 탐색 후 특정 파일 및 디렉토리만 복원하는 것은 어렵다. 백업의 내부 구조를 알 수 없다면 그냥 전체 복원을 하거나, 마운트를 한 뒤 파일을 탐색하는 단점이 있다.
홈서버는 BorgBackup
, 클라우드 서비스는 자체 스냅샷 기능 혹은 restic
을 이용할 계획이다.
설치
1
$ sudo apt install restic
Python 기반인 BorgBackup
과 달리 Go로 컴파일되어 가볍다. (백업 속도까지 빠르다는 소리는 아니다. 비슷비슷한 듯?)
Object Storage와 연결 준비
다른 사람과 함께 작업하는 인스턴스라면, 개인 계정(example@gmail.com 형식)보단 Service Account를 생성하여 사용해야한다. 이 인스턴스는 혼자 사용하므로 그냥 개인 계정으로 직접 로그인했다.
Google Cloud Platform의 Compute Engine에서 Object Storage를 사용할 수 있게 만들어보자.
- 로그인
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
$ gcloud auth application-default login You are running on a Google Compute Engine virtual machine. The service credentials associated with this virtual machine will automatically be used by Application Default Credentials, so it is not necessary to use this command. If you decide to proceed anyway, your user credentials may be visible to others with access to this virtual machine. Are you sure you want to authenticate with your personal account? Do you want to continue (Y/n)? y Go to the following link in your browser, and complete the sign-in prompts: <링크> Once finished, enter the verification code provided in your browser:
원하는 Compute Engine 인스턴스에서 해당 명령어를 입력한 뒤 나오는 링크에서 OAuth 로그인을 거친다.
OAuth를 거치면 나오는 화면 2번째 회색 박스 아래에 표시된 키를 터미널에 붙여넣자. ***
1 2 3 4 5
Credentials saved to file: [/home/ubuntu/.config/gcloud/application_default_credentials.json] These credentials will be used by any library that requests Application Default Credentials (ADC). Quota project "my-services" was added to ADC which can be used by Google client libraries for billing and quota. Note that some services may still bill the project owning the resource.
이제 이 인스턴스에 내 계정의 프로젝트로 로그인 되었다.
- 저장소 생성
1
restic -r gs:<버킷 이름>:<버킷 내 경로> init
성공적으로 연결이 됐다면, 암호화를 위한 비밀번호를 입력하라는 메세지가 뜬다. 하라는대로 따라하자.
- 준비 완료
1 2 3 4 5
created restic repository 12345abcde at gs:<버킷 이름>:<버킷 내 경로> Please note that knowledge of your password is required to access the repository. Losing your password means that your data is irrecoverably lost.
준비가 되었다. 이제
BorgBackup
에서 했듯 스크립트를 작성하고, 자동으로 백업하도록 만들자.
자동화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
# 환경 변수 설정
# sudo 권한으로 실행할 경우 상대 경로를 절대 경로로 변경하자
SOURCE="/home/hgsanguk/service" # 백업할 데이터 경로
LOGFILE="/var/log/restic.log" # 스크립트의 로그 파일 경로
export GOOGLE_APPLICATION_CREDENTIALS="~/.config/gcloud/application_default_credentials.json"
export RESTIC_REPOSITORY="gs:<버킷 이름>:<버킷 내 경로>"
export RESTIC_PASSWORD="비밀번호" # 위에서 설정한 비밀번호
# 백업 과정 시작
RUNNING_CONTAINERS=$(docker ps -q) # 실행 중인 Docker 컨테이너 목록 불러오기
echo "[$(date)] 백업을 시작합니다." | tee -a $LOGFILE
# 1. 실행 중인 모든 Docker 컨테이너 중지
if [ ! -z "$RUNNING_CONTAINERS" ]; then
echo "[$(date)] 실행 중인 컨테이너를 중지합니다." | tee -a $LOGFILE
docker stop $RUNNING_CONTAINERS >> $LOGFILE 2>&1
fi
# 2. restic 실행
echo "[$(date)] restic을 실행합니다." | tee -a $LOGFILE
restic backup $SOURCE --verbose --tag service >> $LOGFILE 2>&1
# 3. 오래된 백업 정리 (마찬가지로 7개의 백업만 남겨두도록 설정했다.)
echo "[$(date)] 오래된 백업을 정리합니다." | tee -a $LOGFILE
restic forget --keep-last 7 --prune >> $LOGFILE 2>&1
# 4. 중지한 컨테이너 다시 시작
if [ ! -z "$RUNNING_CONTAINERS" ]; then
echo "[$(date)] 중지한 컨테이너를 다시 시작합니다." | tee -a $LOGFILE
docker start $RUNNING_CONTAINERS >> $LOGFILE 2>&1
fi
echo "[$(date)] 백업이 완료되었습니다." | tee -a $LOGFILE
스크립트에 실행 권한을 부여한 후, 원하는 백업 간격을 생각해서 crontab
에 등록하자.
복원
- 백업 목록 출력
1
$ restic snapshots -r gs:<버킷 이름>:<버킷 내 경로>
- 특정 스냅샷에서 복원
1
$ restic -r gs:<버킷 이름>:<버킷 내 경로> restore <스냅샷 Hash> --target <대상 디렉토리>
- 최근 백업에서 복원
1
$ restic -r gs:<버킷 이름>:<버킷 내 경로> restore latest --target <대상 디렉토리> --path <복원할 디렉토리 혹은 파일>
기능에 대한 더 자세한 설명은 블로그보단 문서를 참고하자.