Backup MySQL mediante snapshots LVM

Este artículo muestra de forma práctica como montar un sistema de backup de MySQL basado en snapshots de LVM.

Básicamente, se siguen los siguientes pasos:


  • Crear un Volumen Lógico de X MB dentro de un VG con Y MB de espacio libre. Se dimensionan los anteriores espacios de forma que (Y-X) sea capaz de almacenar todos los cambios en la BBDD durante el proceso de backup. Por norma general y a menos que la BBDD tenga muchos cambios, bastará con crear un LV que deje al menos un 25% de su espacio libre en el VG.
  • Mover /var/lib/mysql al nuevo LV.
  • Utilizar un script (mylvmbackup) que haga lo siguiente:
    • FLUSH TABLES WITH READ LOCK
    • lvm snapshot de /var/lib/mysql sobre /backup/lvsnapshot (lvcreate -s)
    • UNLOCK TABLES (MySQL sigue funcionando con sólo unos segundos de bloqueo)
    • Montaje y backup del contenido del snapshot LVM.
    • Destrucción del snapshot LVM (lvremove).


Este mecanismo permite hacer backup de MySQL evitando las herramientas mysqldump y mysqlhotcopy, que para BBDD de tamaños grandes producen bloqueos de hasta minutos de duración con el consecuente corte de servicio.


Instalación del software de LVM

Si no está ya incluído en nuestro sistema, instalamos los paquetes de LVM y cargamos en memoria los módulos dm_mod, dm_mirror y dm_snapshot:

root@server:~# apt-get install lvm2 mylvmbackup
root@server:~# modprobe dm_mod
root@server:~# modprobe dm_mirror
root@server:~# modprobe dm_snapshot


Particionado y creación del VG y LV

Creamos una partición de tipo LVM que sea, al menos, del 125% del tamaño que pensemos dejar para /var/lib/mysql. Ese 25% de espacio adicional (en mi ejemplo va a ser casi un 75% adicional) son bloques LVM libres que permitan generar el snapshot del filesystem de datos de MySQL:

root@server:~# fdisk -l /dev/cciss/c0d1

Disk /dev/cciss/c0d1: 146.7 GB, 146778685440 bytes
255 heads, 32 sectors/track, 35132 cylinders
Units = cylinders of 8160 * 512 = 4177920 bytes
Disk identifier: 0x0d909842

           Device Boot      Start         End      Blocks   Id  System
/dev/cciss/c0d1p1               1       15559    63480704   83  Linux
/dev/cciss/c0d1p2           15560       35132    79857840   8e  Linux LVM

root@server:~# pvcreate /dev/cciss/c0d1p2
  Physical volume "/dev/cciss/c0d1p2" successfully created

root@server:~# pvdisplay /dev/cciss/c0d1p2
  --- NEW Physical volume ---
  PV Name               /dev/cciss/c0d1p2
  VG Name               
  PV Size               76,16 GB
  Allocatable           NO
  PE Size (KByte)       0
  Total PE              0
  Free PE               0
  Allocated PE          0
  PV UUID               LB4H0W-8pwL-y0MN-cWUH-fYX3-gD1U-hm5saW
   
root@server:~# vgcreate -s 8 vgmysql /dev/cciss/c0d1p2 
  Volume group "vgmysql" successfully created

root@server:~# vgdisplay vgmysql
  --- Volume group ---
  VG Name               vgmysql
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               76,16 GB
  PE Size               8,00 MB
  Total PE              9748
  Alloc PE / Size       0 / 0   
  Free  PE / Size       9748 / 76,16 GB
  VG UUID               Az7RTH-DqxW-wXRJ-l3GF-vgT4-t35f-lkumuf
   
root@server:~# lvcreate -L 40G -n data vgmysql
  Logical volume "data" created

root@server:~# mkfs.ext3 /dev/vgmysql/data 
mke2fs 1.40.8 (13-Mar-2008)

root@server:~# tune2fs -m 1 /dev/vgmysql/data
tune2fs 1.40.8 (13-Mar-2008)
Setting reserved blocks percentage to 1% (104857 blocks)


Paso de los datos de MySQL al LV

A continuación detenemos MySQL y movemos los datos al nuevo Logical Volume:

root@server:~# /etc/init.d/mysql stop
 * Stopping MySQL database server mysqld     [ OK ]

root@server:~# cd /var/lib/

root@server:/var/lib# ls -ld mysql
drwxr-xr-x 9 mysql mysql 4096 2010-04-08 09:20 mysql

root@server:/var/lib# mv mysql mysql2

root@server:/var/lib# mkdir mysql

root@server:/var/lib# chown mysql:mysql mysql

root@server:/var/lib# grep mysql /etc/fstab 
/dev/vgmysql/data   /var/lib/mysql  ext3  relatime  0 2

root@server:/var/lib# mount -a

root@server:/var/lib# chown mysql:mysql mysql

root@server:/var/lib# ls -ld mysql
drwxr-xr-x 3 mysql mysql 4096 2010-05-12 17:36 mysql

root@server:/var/lib# df -h /var/lib/mysql
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/vgmysql-data
                       40G  177M   40G   1% /var/lib/mysql

root@server:/var/lib# mv mysql2/* mysql/

root@server:/var/lib/mysql# /etc/init.d/mysql start
 * Starting MySQL database server mysqld   [ OK ] 
 * Checking for corrupt, not cleanly closed and upgrade needing tables.


Configuramos mylvmbackup

Creamos los directorios necesarios para alojar nuestros backups, y configuramos mylvmbackup con los datos de MySQL, VGs, LVs y directorios adecuados:

root@server:~# mkdir /backup/

root@server:~# mkdir /backup/lvmysql

root@server:~# mkdir /backup/lvsnapshot

root@server:~# cat /etc/mylvmbackup.conf 
[mysql]
user=root
password=passwd_root_mysql
host=localhost
port=3306
socket=
mycnf=/etc/mysql/my.cnf

# LVM-specific options
[lvm]
vgname=vgmysql
lvname=data
backuplv=backup
lvsize=10G

# File system specific options
[fs]
xfs=0
mountdir=/backup/lvsnapshot/
backupdir=/backup/lvmysql/
relpath=

# Full path names of required external utilities
[tools]
lvcreate=/sbin/lvcreate
lvremove=/sbin/lvremove
mount=/bin/mount
tar=/bin/tar
rsync=/usr/bin/rsync
umount=/bin/umount

# Other configuration options
[misc]
backuptype=tar
prefix=backup
tararg=cvzf
tarsuffixarg=
rsyncarg=-avWP
datefmt=%Y%m%d_%H%M%S
innodb_recover=0
pidfile=/var/tmp/mylvmbackup_recoverserver.pid
skip_flush_tables=0
extra_flush_tables=0

# Logging options. The Sys::Syslog module is required for syslog option
[logging]
# 'console' (STDOUT, STDERR) or 'syslog' or 'both'.
log_method=console
# 'native', 'tcp', 'udp'. Default is 'native'
syslog_socktype=native
syslog_facility=
# If using remote syslog, don't forget to specify the socket type to tcp or udp.
syslog_remotehost=


Probamos el backup

Ejecutamos mylvmbackup, que leerá su fichero de configuración y realizará el backup de la BBDD. Como podemos ver en el siguiente ejemplo, el tiempo de corte real en esta base de datos es de menos de 1 segundo (en el mismo minuto 09:18:16 se hace tanto el FLUSH como el UNLOCK).

root@server:~# mylvmbackup 
20100513 09:18:16 Info: Connecting to database...
20100513 09:18:16 Info: Flushing tables with read lock...
20100513 09:18:16 Info: Taking position record...
20100513 09:18:16 Info: Taking snapshot...
File descriptor 3 left open
  Logical volume "backup" created
20100513 09:18:16 Info: Unlocking tables...
20100513 09:18:16 Info: Disconnecting from database...
20100513 09:18:16 Info: Mounting snapshot...
20100513 09:18:16 Info: Copying my.cnf...
20100513 09:18:16 Info: Taking actual backup...
20100513 09:34:18 Info: Creating tar archive /backup/lvmysql/backup-20100513_093418_mysql.tar.gz
backup/
backup/phpmyadmin/
backup/phpmyadmin/pma_relation.MYD
backup/phpmyadmin/pma_history.MYI
backup/phpmyadmin/pma_table_coords.MYI
(...)
backup/accesorios/db.opt
backup/accesorios/usuarios.MYD
backup/accesorios/usuarios.MYI
backup/accesorios/usuarios.frm
backup-pos/backup-20100513_093418_mysql.pos
backup-pos/backup-20100513_093418_my.cnf
20100513 09:36:46 Info: DONE
20100513 09:36:46 Info: Cleaning up...
20100513 09:36:46 Info: LVM Usage stats:
20100513 09:36:46 Info:   LV     VG      Attr   LSize  Origin Snap%  Move Log Copy% 
20100513 09:36:46 Info:   backup vgmysql swi-a- 10,00G data     0,00                
  Logical volume "backup" successfully removed

Finalmente, programamos el backup en el cron del sistema:

root@server:~# grep -i my /etc/crontab 
# Backups MySQL
0  3 * * * root mylvmbackup > /dev/null
50 3 * * * root find /backup/lvmysql -name "*.gz" -mtime +8 -exec rm {} \;

De esta forma tenemos backups diarios totalmente consistentes sin afectación al servicio de BBDD.



<Volver a la sección de GNU/Linux>