년도별 글 목록: 2014

MySQL 원격 증분 백업 – Percona XtraBackup

# Percona XtraBackup 소개

이미 예전에 XtraBackup을 간단하게 소개한 글을 썼었는데 그 글의 내용만으로는 XtraBackup을 실무에 쓰기에 턱없이 부족하다. 그래서 이번에는 XtraBackup을 제대로 쓸 수 있는 문서를 만들었다.

일반적으로 MySQL 백업으로 mysqldump를 사용하는데 매번 전체를 백업하는 방식이므로 데이터가 커질수록 백업에 걸리는 시간이 길어지고 부담이 커진다. 뿐만 아니라 백업된 데이터를 복구하는데도 무척 긴 시간을 필요로 한다. 하지만 XtraBackup을 사용하면 마지막 백업으로부터 변경된 사항만 이어서 백업하는 증분 백업이 가능하다.

XtraBackup은 MySQL의 데이터 디렉토리 자체를 복사하는 것과 같기에 복구에 걸리는 시간은 단지 파일을 복사하는데 걸리는 시간과 같으며 InnoDB 엔진을 적용한 테이블의 경우 MySQL이 실행중인 상태에서도 테이블 락을 걸지 않고 백업이 가능하다. MyISAM 엔진을 사용한 테이블은 락이 걸리므로 주의하자.(락을 걸지 않도록 하는 옵션이 있지만 모험을 하고 싶지는 않다.)

 

# 학습 순서

이 문서에서는 XtraBackup으로 MySQL 백업을 아래와 같은 단계로 학습하겠다.

  1. 로컬 전체 백업 및 복구
  2. 로컬 증분 백업 및 복구
  3. 원격 증분 백업 및 복구

단계가 올라갈수록 복잡도가 높아지며 이전 단계의 이해를 요구하므로 한단계씩 차근차근 진행하자.

 

# 설치

XtraBackup의 설치는 어렵지 않다. 패키지로 설치가 가능하며 우분투에서는 apt-get으로 설치하면 된다.

$ sudo apt-get install xtrabackup

레드햇 계열은 yum으로 설치하면 된다. 요즘 배포판이라면 기본 저장소에 XtraBackup이 포함되어 있지만 그렇지 않다면 Percona에서 제공하는 XtraBackup 설치 가이드를 참고하자.

 

# 전체 백업

전체 백업은 무척 간단하다. innobackupex라는 명령을 사용하는데 xtrabackup을 좀 더 쓰기 쉽게 펄 스크립트로 구현해 놓은 것이다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 /전체백업

위와 같이 백업할 경우 ‘/전체백업/날짜’로 저장된다.
백업디렉토리를 명시적으로 지정하고 싶다면 –no-timestamp 옵션을 추가한다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 --no-timestamp /전체백업

 

# 복원 준비

복원하기 위해서는 백업이 진행되는 동안 생성된 MySQL의 로그를 적용시켜야 한다.
로그 적용은 –apply-log 옵션을 사용하면 된다.

$ sudo innobackupex --apply-log /전체백업

 

# 복원

복원 준비까지 마쳤다면 –copy-back 옵션으로 복원 할 수 있다.
복원은 백업 디렉토리를 MySQL 데이터 디렉토리에 복사하는 것과 동일하다.
때문에 MySQL의 동작을 중지시켜야 하며 데이터 디렉토리는 비어 있어야 한다.
우분투에서 MySQL의 기본 데이터 디렉토리는 /var/lib/mysql 이다.

$ sudo service mysql stop
$ sudo rm -rf /var/lib/mysql/*
$ sudo innobackupex --copy-back /전체백업

마지막으로 MySQL 데이터 디렉토리의 소유자와 그룹을 재설정하고 MySQL을 다시 시작한다.

$ sudo chown -R mysql:mysql /var/lib/mysql
$ sudo service mysql start

 

# 증분 백업

최초 증분 백업을 하기 위해서는 전체 백업을 해놓은 디렉토리가 준비되어 있어야 한다.
증분 백업이란 마지막 백업 이후로 변경된 사항만 이어서 백업하는 것이므로 기본적으로 전체 백업이 필요하다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 --no-timestamp /전체백업

증분 백업은 –incremental 옵션으로 할 수 있다.
여기서 중요한건 어느 시점부터 백업을 시작할지 LSN(log sequence number)을 전달해야 한다.
LSN 전달 방법은 아래와 같은 방법이 있다.

  1. LSN(log sequence number) 값을 직접 명시하는 방법
    –incremental-lsn=LSN
  2. xtrabackup_checkpoints 파일이 있는 디렉토리를 지정하는 방법
    –incremental-basedir=DIR

두 방법 모두 백업 디렉토리 하위의 xtrabackup_checkpoints 파일의 to_lsn 값을 이용하는 것이다.

$ cat /전체백업/xtrabackup_checkpoints
backup_type = full-backuped
from_lsn = 0
to_lsn = 1887987291
last_lsn = 1887987291
compact = 0

로컬 백업에는 2번 방법으로도 충분한데 원격 백업을 위해서는 1번 방법을 응용하는 것이 좋다.
이 문서에서는 1번 방법으로 계속 설명하겠다.

이후 진행을 편하게 하기 위해서 xtrabackup_checkpoints 파일에서 to_lsn 값을 가져오는 간단한 명령을 사용하자.

$ to_lsn=`grep to_lsn ./전체백업/xtrabackup_checkpoints | awk '{ print $3 }'`
$ echo $to_lsn

증분 백업도 전체 백업과 마찬가지로 –no-timestamp 옵션을 추가해서 백업 디렉토리를 명시적으로 지정할 수 있다.

그럼 위에서 설명한  –incremental, –incremental-lsn 옵션과 LSN 값을 가져오는 명령을 이용해서 증분 백업 명령을 내려보자.

$ to_lsn=`grep to_lsn ./전체백업/xtrabackup_checkpoints | awk '{ print $3 }'`
$ sudo innobackupex --user=DB사용자 --password=DB암호 --incremental --incremental-lsn=$to_lsn --no-timestamp /증분백업_1

LSN 값만 제대로 전달되면 증분 백업도 어렵지 않다.
이어서 새로운 증분 백업을 하려면 최근 백업한 디렉토리 하위의 xtrabackup_checkpoints 파일에서 to_lsn 값을 가져와서 처음 과정을 반복하면 된다.

$ to_lsn=`grep to_lsn ./증분백업_1/xtrabackup_checkpoints | awk '{ print $3 }'`
$ sudo innobackupex --user=DB사용자 --password=DB암호 --incremental --incremental-lsn=$to_lsn --no-timestamp /증분백업_2

이렇듯 증분백업은 전체 백업에 자동으로 합쳐지는 것이 아니라 별도의 디렉토리에 생성이 되며 복원을 위해서는 전체 백업에 합쳐야 한다.

전체 백업 + 증분 백업 1 + 증분 백업 2 + …

 

# 전체 백업에 증분 백업을 합치기

전체 백업에 증분 백업을 합치려면 일단 전체 백업 디렉토리에 –apply-log –redo-only 옵션을 적용한다.
그리고 증분 백업 디렉토리를 순서대로 –apply-log –redo-only 옵션과 함께 전체 백업에 적용한다.

$ sudo innobackupex --apply-log --redo-only /전체백업
$ sudo innobackupex --apply-log --redo-only /전체백업 --incremental-dir=/증분백업_1
$ sudo innobackupex --apply-log --redo-only /전체백업 --incremental-dir=/증분백업_2

전체 백업 디렉토리와 마지막 증분 백업 디렉토리 각각 하위의 xtrabackup_checkpoints 파일을 열어보면 to_lsn 값이 동일하다는 것을 확인 할 수 있다.

증분 백업을 전체 백업에 합치고 나면 증분 백업 디렉토리는 삭제해도 된다.

 

# 증분 백업 복원

전체 백업에 증분 백업을 모두 합쳤다면 이후 복원은 일반적인 전체 백업 복원과 동일하다.

$ sudo innobackupex --apply-log /전체백업
$ sudo innobackupex --copy-back /전체백업

 

# 증분 백업 팁

매번 증분 백업을 할때마다 디렉토리가 늘어나고 나중에 한번에 합치기가 불편하다면 증분 백업을 하자마자 바로 전체 백업에 합치면 된다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 /전체백업
$ sudo innobackupex --apply-log --redo-only /전체백업
$ to_lsn=`grep to_lsn ./전체백업/xtrabackup_checkpoints | awk '{ print $3 }'`
$ sudo innobackupex --user=DB사용자 --password=DB암호 --incremental --incremental-lsn=$to_lsn --no-timestamp /증분백업
$ sudo innobackupex --apply-log --redo-only --incremental-dir=/증분백업 /전체백업
$ sudo rm -rf /증분백업

위의 명령 이후 전체 백업을 제외한 3 ~ 6번 라인의 과정을 반복하면 한개의 디렉토리에 백업을 이어갈 수 있다.

 

# 원격 전체 백업

원격 백업이라고 하지만 원격 MySQL에 접속하여 백업하는 것은 불가능하고 로컬에서 백업한 것을 원격으로 전송하는 방식을 쓴다. 백업을 전달받을 원격 호스트는 –remote-host 옵션으로 지정한다.
원격 전송에는 scp가 사용되는데 –scpopt 옵션으로 scp 관련 옵션을 부여할 수 있고 기본 옵션은 ‘-Cp -c arcfour’ 이다.
–remote-host 옵션과 –scpopt 옵션을 제외하면 일반적인 전체 백업과 같다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 --no-timestamp /전체백업 --remote-host=REMOTE@SERVER --scpopt='-Cp -c arcfour'

 

# 원격 증분 백업

증분 백업은 백업 데이터를 스트림으로 내보낼 수 있으며 –stream=xbstream 옵션을 사용하면 된다.
스트림으로 출력되는 데이터는 파이프를 통해 ssh 명령으로 원격 서버에 전송할 수 있으며 원격 서버에서는 xbstream 을 이용하여 전송된 데이터를 풀어 놓으면 된다. 때문에 원격 서버에도 XtraBackup이 설치되어 있어야 한다.

증분 백업 스트림 데이터를 ssh로 원격 서버에 전송하고 원격 서버에서는 xbstream으로 전송된 데이터를 풀어놓는 명령어은 아래와 같다.

$ sudo innobackupex --user=DB사용자 --password=DB암호 --incremental --incremental-lsn=LSN /tmp --stream=xbstream | ssh REMOTE@SERVER "cat - | xbstream -x -C /증분백업"

명령이 복잡해 보이지만 차근차근 뜯어보면 어렵지 않다.

위 명령의 앞부분은 일반적인 증분 백업과 다를바 없다. 다만 백업 경로가 /tmp로 되어있는데 임시로 사용되는 경로일뿐 실제 데이터는 바로 스트림으로 전송된다.

명령의 뒷부분은 우리가 원하는 동작 순서 그대로 나열했을 뿐이다.

--stream=xbstream | ssh REMOTE@SERVER "cat - | xbstream -x -C /증분백업"
스트림 내보내기 | 원격 서버에 ssh로 명령 "전송된 데이터 풀어놓기"

 

# 원격 증분 백업 스크립트

간단한 명령으로 자동화된 원격 백업을 할 수 있으면 좋겠지만 아쉽게도 그렇지 않다. XtraBackup의 다양한 명령을 조합하여 원격 백업을 구현해야 한다. 그러기 위해서는 서버의 ssh 설정과 권한 설정 등 추가적인 서버 운영 지식이 필요하다.

아래는 백업 서버에서 DB 서버로부터 MySQL의 데이터를 백업하는 스크립트 예제다. 각 코드에 대한 설명은 하지 않겠다. 서버별로 환경이 다양하므로 가져다 쓰기만 하면 되는 스크립트를 만들 수는 없다.

스크립트가 복잡해 보인다고 걱정하지 말자. 지금까지 설명한 XtraBackup의 사용법을 이해하고 있다면 아래 스크립트는 별 것 아닌 단순한 명령을 순서대로 나열한 것일 뿐이다.

#! /bin/bash

set -e

base_dir="/backup"
db_pw="abcd"
db_host="root@db"
backup_host="my@backup"

cd $base_dir

# 전체 백업
if [ ! -d ./full ]
then
    echo "[DB full backup] start"

    ssh ${db_host} "innobackupex --user=root --password=${db_pw} --no-timestamp --scpopt='-l 24000 -Cp -c arcfour' --remote-host=${backup_host} ${base_dir}/full"

    innobackupex --apply-log --redo-only ./full

    echo "[DB full backup] end"
fi

# 오래된 증분 백업 디렉토리가 남아있으면 제거
find . -name 'incremental' -type d -mmin +60 | xargs rm -rf

# 증분 백업
if [ ! -d ./incremental ]
then
    mkdir ./incremental

    to_lsn=`grep to_lsn ./full/xtrabackup_checkpoints | awk '{ print $3 }'`

    echo "[DB incremental backup:to_lsn=${to_lsn}] start"

    ssh ${db_host} "rm -rf /tmp/xtrabackup"
    ssh ${db_host} "innobackupex --user=root --password=${db_pw} --incremental --incremental-lsn=${to_lsn} --stream=xbstream --scpopt='-l 8000 -Cp -c arcfour' /tmp/xtrabackup | ssh ${backup_host} \"cat - | xbstream -x -C ${base_dir}/incremental/\""

    innobackupex --apply-log --redo-only --incremental-dir=${base_dir}/incremental ./full

    rm -rf ./incremental

    echo "[DB incremental backup] end"
fi

완성된 스크립트를 cron에 등록하여 주기적으로 백업이 되도록 하면 된다.

내가 생각하기에 XtraBackup이 가장 가볍고 빠르게 동작할 수 있는 환경은 MySQL의 모든 테이블이 InnoDB 엔진을 사용하며 innodb_file_per_table 옵션이 활성화되어 있을 때인 것 같다.

이제는 실무에서 MySQL을 백업하는 도구로 mysqldump를 대신 XtraBackup을 사용하자. 물론 mysqldump가 필요한 경우도 있다. 하지만 분명한 건 전체를 백업하는 용도로는 mysqldump는 불편하다.