Descripción del Escenario
Partimos de una instalación del PostgreSQL 9.2.18 con archivado de WAL comprimido sobre Centos7 utilizando únicamente los tablespaces por defecto. Las principales rutas son las siguientes:
- Directorio Data: /var/lib/pgsql/data
- Directorio Archivado de WAL comprimido: /var/lib/pgsql/PostgresqlWalCompr
- Directorio para almacenar los Backups: /var/lib/pgsql/PostgresqlBackup
La configuración del archivado de WAL en el fichero postgresql.conf es la siguiente:
wal_level = archive archive_mode = on archive_command = '/usr/local/bin/omnipitr-archive -D /var/lib/pgsql/data -l /var/log/wal/wal.log -dl gzip=/var/lib/pgsql/PostgresqlWalCompr -v "%p"' |
La idea es realizar un Backup físico en caliente (sin parada de servicio) a nivel de sistema de ficheros de toda la Instancia (ya sea con un tar comprimido o con algún otro método), y seguidamente simular una recuperación de dicha copia sobre la misma Instancia, sin necesidad de crear el fichero recovery.conf.
Esta es una forma típica de acometer el Backup de PostgreSQL. Una forma bastante rápida, poco intrusiva (impacta menos que un pg_dump), y que permite recuperar todas las bases de datos a un mismo momento. Sin embargo también tiene algunos inconvenientes o desventajas, ya que sólo se puede recuperar sobre otra Instancia de PostgreSQL que esté en la misma versión y además implica recuperar la Instancia completa (no es posible recuperar una única base de datos).
Backup listo para recuperar (Ready to Restore) en caliente de PostgreSQL (PostgreSQL Hot Physical Backup)
Para realizar el backup físico en caliente, conectados con el usuario postgres, lo primero que deberemos hacer es poner la Instancia en modo Backup utilizando la función pg_start_backup, seguidamente realizaremos la copia del directorio data utilizando la forma que más nos guste (por ejemplo un tar comprimido), para seguidamente salir del modo Backup utilizando la función pg_stop_backup. Todo esto lo podríamos resumir en los siguientes comandos:
su - postgres cd /var/lib/pgsql psql -c "select pg_start_backup('Daily Backup'), current_timestamp" tar -cvz -f /var/lib/pgsql/PostgresqlBackup/data.tar.gz data/ psql -c "select pg_stop_backup(), current_timestamp"
|
Si deseamos utilizar una herramienta de Backup como Veritas o DataProtector, podemos utilizar como Pre-Script el comando pg_start_backup, y como Post-Script el comando pg_stop_backup, y así poder realizar la copia del filesystem correctamente con Veritas, DataProtector, o la herramienta que más nos guste, con la ventaja de no necesitar espacio adicional pudiendo hacer el Backup directamente a cinta.
Cabe comentar, que al poner la Instancia en modo Backup con la función pg_start_backup, se crea automáticamente el fichero backup_label dentro del directorio data, que podremos consultar al hacer el restore si lo necesitamos.
IMPORTANTE: El principal problema, es que el directorio pg_xlog podría no tener todas las transacciones necesarias (WAL), en cuyo caso tendríamos un Backup inconsistente.
Ahora ha llegado el momento de ver cómo recuperar el Backup que acabamos de realizar. Una de las ventajas del método que vamos a ver es que no vamos a necesitar crear el fichero recovery.conf, simplificando el proceso de recuperación.
Para poder recuperar el Backup que acabamos de hacer, deberemos detener el servicio de PostgreSQL, eliminar o mover el directorio data (recomendable moverlo por si fuera necesario más adelante, o hacer un tar comprimido para ahorrar espacio), recuperar el directorio data, y arrancar PostgreSQL de nuevo. Esto lo podemos realizar con los siguientes comandos:
pg_ctl -D /var/lib/pgsql/data -m fast stop mv data data-old # También es posible hacer un tar comprimido tar -xvzf /var/lib/pgsql/PostgresqlBackup/data.tar.gz pg_ctl -D /var/lib/pgsql/data start
|
Después de arrancar la Instancia de PostgreSQL, comenzará la recuperación, de tal modo que al finalizar el Recovery, se renombrará automáticamente el fichero backup_label como backup_label.old. El fichero backup_label también le podemos consultar antes de hacer el restore para asegurarnos a que momento vamos a restaurar, es decir, la fecha del backup.
Si lo deseamos podemos consultar el comienzo del último fichero de log de PostgreSQL, para comprobar cómo ha ido el arranque de nuestra Instancia tras la recuperación, y asegurarnos que está todo OK. Como podemos ver en el siguiente pantallazo, hemos recuperado correctamente el estado de nuestra Instancia a las a las 20:46 del 14/01/2017 (en el pantallazo anterior podemos ver que el Backup comenzó a las 20:35 y finalizó a las 20:46). Para esto podemos utilizar un comando head como el siguiente.
head -n 20 data/pg_log/postgresql-2017-01-14_215344.csv |
La salida de su ejecución debería ser algo así.
Backup físico excluyendo el directorio de WAL
Ahora vamos a hacer el backup físico del filesystem excluyendo el directorio de WAL, que aunque resulta más complicado, didácticamente es bastante interesante para conocer mejor como funciona PostgreSQL en este tipo de situaciones. Al excluir el directorio de WAL (data/pg_xlog), deberemos incluir una copia del archivado de WAL, ya que necesitaremos dichas transacciones para poder recuperar nuestra Instancia, que es lo que complica un poco todo el proceso, tanto de Backup como de Restore. En cualquier caso, esto lo podríamos realizar con los siguientes comandos:
su - postgres cd /var/lib/pgsql psql -c "select pg_start_backup('Daily Backup'), current_timestamp" tar -cvz --exclude="pg_xlog/*" -f /var/lib/pgsql/PostgresqlBackup/data.tar.gz data/ psql -c "select pg_stop_backup(), current_timestamp" tar -cvzf /var/lib/pgsql/PostgresqlBackup/archive.tar.gz PostgresqlWalCompr/
|
Para poder recuperar el Backup que acabamos de hacer, deberemos detener el servicio de PostgreSQL, eliminar o mover el directorio data (recomendable moverlo por si fuera necesario más adelante, o bien hacer un tar comprimido), y recuperar el directorio data. Esto lo podemos realizar con los siguientes comandos:
pg_ctl -D /var/lib/pgsql/data -m fast stop mv data data-old # un tar comprimido también vale tar -xvzf /var/lib/pgsql/PostgresqlBackup/data.tar.gz
|
Si no tuviésemos los WAL archivados comprimidos, los recuperaríamos también (en nuestro caso de ejemplo, como no los hemos eliminado, no hace falta recuperarlos).
tar -xvzf /var/lib/pgsql/PostgresqlBackup/archive.tar.gz |
En nuestro Backup excluimos el directorio data/pg_xlog, por lo que no tendremos ningún WAL, aunque tenemos los WAL archivados comprimidos. Vamos a necesitar algunos ficheros WAL, pero a priori no sabemos cuáles. Para saber qué ficheros WAL necesitamos, en primer lugar visualizaremos el fichero backup_label del directorio data.
Podemos observar que el WAL de inicio es el 0000000100000000000000CE. Sabiendo esto, podemos consultar los ficheros archivados de WAL como se muestra en el siguiente ejemplo, para averiguar los ficheros WAL que hacen falta. En nuestro caso tan sólo hace falta uno (ver START WAL LOCATION y STOP WAL LOCATION), el fichero 0000000100000000000000CE, por lo que lo descomprimiremos y lo copiaremos a la carpeta de WAL, para seguidamente arrancar PostgreSQL.
ls PostgresqlWalCompr/0000000100000000000000CE*.* gzip -d PostgresqlWalCompr/0000000100000000000000CE.00000020.backup.gz cat PostgresqlWalCompr/0000000100000000000000CE.00000020.backup gzip -d PostgresqlWalCompr/0000000100000000000000CE.gz cp PostgresqlWalCompr/0000000100000000000000CE data/pg_xlog/ pg_ctl -D /var/lib/pgsql/data start
|
A continuación podemos ver un ejemplo de su ejecución.
Después de arrancar la Instancia de PostgreSQL, comenzará la recuperación, de tal modo que al finalizar el Recovery, se renombrará automáticamente el fichero backup_label como backup_label.old, y funcionando que es gerundio.
¿Y si no tenemos los ficheros WAL?
Si por cualquier motivo no tuviésemos los ficheros WAL necesarios para poder arrancar la Instancia de PostgreSQL, lo más probable es que al intentar arrancar PostgreSQL se aborté y registre en el LOG una secuencia de mensajes similares a la siguiente:
could not open file ""pg_xlog/0000000100000000000000CC"" (log file 0, segment 204): No such file or directory
invalid checkpoint record
could not locate required checkpoint record. If you are not restoring from a backup, try removing the file /var/lib/pgsql/data/backup_label
startup process (PID 6877) exited with exit code 1
aborting startup due to startup process failure
En el siguiente pantallazo se muestra un ejemplo del contenido que podemos encontrarnos en el LOG de PostgreSQL.
Mal asunto. Estamos jodidos. En esta situación existe riesgo de corrupción de datos debido a transacciones parcialmente confirmadas que NO han podido ser re-hechas (REDO) ni des-hechas (ROLLBACK). Así que, nos queda cómo última opción, utilizar el comando pg_resetxlog para resetear los WAL y la información de control asociada. Tras esto, lo más probable es que seamos capaces de arrancar PostgreSQL pero dado que puede almacenar información inconsistente, no debemos de confiar más en esta instancia de PostgreSQL, siendo muy recomendable hacer un export (pg_dumpall) de las bases de datos, reconstruir la instancia (initdb), cargar de nuevo los datos (pg_restore), y comprobar si hay alguna inconsistencia que deba ser reparada. Sin entrar en todos estos detalles, a continuación se muestra un ejemplo de cómo utilizar pg_resetxlog para resetear los WAL y conseguir arrancar PostgreSQL.
Poco más por hoy. Como siempre, confío que la lectura resulte de interés.