Ejemplo de PostgreSQL con Docker haciendo copias de seguridad incrementales (Continuous Archiving, Point In Time Recovery (PITR))
Enlaces de interés:
- Documentación de PostgreSQL: Continuous Archiving and Point-in-Time Recovery (PITR)
- Documentación de PostgreSQL: Configuración Write Ahead Log
- Herramientas de backup y restauración: wal-g, barman, pitrery, pg_backrest, pg_probackup
- Tutorial de scalingpostgres.com (Ojo, con la versión 10. Algunas cosas cambian)
- Info: reemplazo del fichero recovery.conf por recovery.signal en la v12
archive_mode = on
wal_level = replica
archive_command = 'arch-command %f %p'
restore_command = 'rest-command %f %p'
Podemos hacerlo a través de la sentencia ALTER SYSTEM
, que guarda los cambios en postgres.auto.conf
, en vez de editar el fichero postgres.conf
.
Copiando este script en el directorio /docker-entrypoint-initdb.d/
se ejecutará la primera vez que se inicie el contenedor.
/* Habilita el archivado */
ALTER SYSTEM SET archive_mode = on;
/* Nivel de información de los ficheros WAL. Necesitaremos replica o logical */
ALTER SYSTEM SET wal_level = replica;
-- Podríamos crear e indicar scripts más refinados para los comandos
-- %f se sustituirá por el nombre del fichero y %p por la ruta del mismo
/* Cada vez que se completa un fichero WAL (16 MB default) se ejecuta archive_command */
ALTER SYSTEM SET archive_command = 'test ! -f /var/lib/postgresql/backups/wal/%f && cp %p /var/lib/postgresql/backups/wal/%f';
/* Cuando la base de datos entre en modo de recuperación ejecutará restore_command */
ALTER SYSTEM SET restore_command = 'cp /var/lib/postgresql/backups/wal/%f %p';
Utilizamos pg_basebackup
para copiar el contenido del directorio $PGDATA (/var/lib/postgresql/data/
)
IMPORTANTE: Primero debemos ajustar los permisos de los directorios en los que guardaremos las copias base y los ficheros WAL. El usuario postgres debe tener acceso.
# Creamos y hacemos propietario a postgres de los directorios donde se guardarán las copias
mkdir /var/lib/postgresql/backups/wal/
mkdir /var/lib/postgresql/backups/base/
chown -R postgres:postgres /var/lib/postgresql/backups/
Nota: Podemos crear copias base cada cierto tiempo y conseguir así restauraciones más ágiles
# Generamos una copia base
# Flags interesantes:
# -z: comprime los tar
# -v: verboso
# -P: muestra el progreso
su - postgres
pg_basebackup -Ft -D /var/lib/postgresql/backups/base/
psql -c "CREATE DATABASE dbtest;"
psql dbtest postgres -c "
CREATE TABLE tabla (
id SERIAL,
hora timestamp without time zone default now(),
texto varchar(100)
);
"
psql dbtest postgres -c "
INSERT INTO tabla (texto) VALUES ('En un lugar de la mancha');
INSERT INTO tabla (texto) VALUES ('de cuyo nombre no quiero acordarme');
SELECT * FROM tabla;
"
# Forzamos el cambio de fichero wal en vez de esperar a que se llene el actual
psql -U postgres -c "select pg_switch_wal();"
psql dbtest postgres -c "
INSERT INTO tabla (texto) VALUES ('Country roads, take me home');
INSERT INTO tabla (texto) VALUES ('To the place I belong');
INSERT INTO tabla (texto) VALUES ('West Virginia, mountain mama');
INSERT INTO tabla (texto) VALUES ('Take me home, country roads');
SELECT * FROM tabla;
"
# Forzamos el cambio de fichero wal en vez de esperar a que se llene el actual
psql -U postgres -c "select pg_switch_wal();"
psql dbtest postgres -c "
INSERT INTO tabla (texto) VALUES ('Dale a tu cuerpo alegria Macarena');
INSERT INTO tabla (texto) VALUES ('que tu cuerpo es para darle alegria y cosa buena');
INSERT INTO tabla (texto) VALUES ('eeeh Macarena.... aaahe');
SELECT * FROM tabla;
"
# Esta vez no forzamos el cambio del fichero wal. A ver qué pasa
Podemos simular un fallo grave del sistema borrando ficheros de postgres:
# Podemos borrar algunos directorios y salir del contenedor
rm -rf /var/lib/postgresql/data/global /var/lib/postgresql/data/base
# O podemos borrar todo el directorio y automáticamente se detendrá el contenedor
# Allá vamos
rm -rf /var/lib/postgresql/data/*
Ahora pasamos a ejecutar los comandos desde nuestra máquina
Debemos restaurar la copia base y hacer que postgres entre en modo recuperación
NOTA: Especificar correctamente el path de los volúmenes
# Desde nuestra máquina restauramos el directorio $PGDATA con la copia base que tengamos
tar xvf path-volumen_backups/_data/base/base.tar -C path-volumen_data/_data/
tar xvf path-volumen_backups/_data/base/pg_wal.tar -C path-volumen_data/_data/pg_wal/
# Para que se inicie en modo de recuperación creamos el fichero recovery.signal
# Solo importa su presencia en $PGDATA, desapareciendo al finalizar la restauración
touch path-volumen_data/_data/recovery.signal
Si queremos realizar la recuperación hasta cierto punto del tiempo (PITR) debemos indicar ese momento en postgres.conf
o postgres.auto.conf
Ahora arrancamos el contenedor y se terminará la restauración a partir de los ficheros WAL.
Podemos comprobar que se restauró correctamente
psql dbtest postgres -c "select * from tabla;"
Nota: Si restauramos con una marca de tiempo deberemos volver a habilitar manualmente el archivado:
psql -U postgres -c "select pg_wal_replay_resume();"