Dos de las características más importantes incluidas en la versión 9.0 de PostgreSQL que se lanzará a finales de verano del 2010 son Hot Standby (HS) y Streaming Replication (SR).
Estas dos características implementan en el núcleo de PostgreSQL lo necesario para instalar un sistema de replicación asincrónica maestro-esclavo (master-slave) en el que los nodos esclavos se pueden utilizar para realizar consultas de solo lectura. Un sistema de replicación de estas caracteristicas se podrá usar tanto para añadir redundancia a nuestras bases de datos, como para descargar de trabajo a nuestro servidor principal en lo referente a consultas de solo lectura.
NOTA: Este artículo está basado en la versión 9.0beta2 de PostgreSQL, todavia quedan por ajustar algunos detalles para la versión 9.0 final, pero lo más importante ya está implementado y decidido. Cuando la versión 9.0 sea lanzada, actualizaremos este artículo si es necesario.
Antes de seguir hablando en detalle sobre como configurar y usar HS y SR, tenemos que explicar ciertos conceptos fundamentales para poder entender como este sistema de replicación funciona.
Estos ficheros tienen un nombre único y un tamaño por defecto de 16MB y se generan en el subdirectorio pg_xlog que se encuentra en el directorio de datos ($PGDATA) usado por PostgreSQL. El número de ficheros WAL contenidos en pg_xlog dependerá del valor asignado al parámetro checkpoint_segments en el fichero de configuración postgresql.conf.
Los ficheros WAL generados en pg_xlog se reciclan continuamente y en un sistema muy ocupado solo tendremos disponibles en pg_xlog los últimos cambios ocurridos en la base de datos durante el periodo de tiempo registrado en los ficheros WAL existentes en pg_xlog.
Los ficheros WAL se pueden archivar automáticamente como copia de seguridad ó para usarlos con PITR - Point in Time Recovery. Para activar el archivo automático de ficheros WAL hay que definir los parámetros wal_level, archive_mode y archive_command en postgresql.conf. Un fichero WAL se archivará antes que sea reciclado en pg_xlog, pero no antes de que tenga registrado 16MB de información en el mismo.
Esta nueva funcionalidad nos permite transferir asincrónicamente registros WAL sobre la marcha (record-based log shipping) entre un servidor maestro y uno/varios esclavos. SR se configura mediante los parámetros primary_conninfo en el fichero recovery.conf y max_wal_senders, wal_sender_delay y wal_keep_segments en postgresql.conf.
En la práctica un proceso denominado receptor WAL (WAL receiver) en el servidor esclavo, se conecta mediante una conexion TCP/IP al servidor maestro. En el servidor maestro existe otro proceso denominado remitente WAL (WAL sender) que es el encargado de mandar los registros WAL sobre la marcha al servidor esclavo.
A continuación teneis un gráfico explicativo de como SR funciona:

Esta nueva funcionalidad nos permite acceder en modo de solo-lectura a todos los datos disponibles en el servidor esclavo a donde estamos replicando nuestras bases de datos. HS se configura mediante los parametros hot_standby y max_standby_delay en postgresql.conf
A continuación teneis un gráfico explicativo de como HS funciona usando SR y transferencia de ficheros WAL:

El tipo de replicación incluido en el núcleo de PostgreSQL está basado en la transferencia de registros WAL entre servidores. Esta transferencia se puede realizar registro a registro (record-based log shipping) ó en ficheros WAL completos (file-based log shipping).
Si usamos solamente SR tendremos que tener cuidado que los ficheros WAL en el servidor maestro no sean reciclados antes de ser transferidos al servidor esclavo. Y si transferimos solamente ficheros WAL sin utilizar SR, perderemos las últimas transacciones registradas en el servidor maestro en caso de caida del servidor maestro. Es por esto que para tener un sistema más robusto, se suelen usar los dos métodos conjuntamente.
Una replicación basada en la transferencia de registros WAL significa que se replicaran absolutamente todas las bases de datos y cambios que realicemos en el servidor maestro. Si lo que necesitamos es replicar solamente algunas de las bases de datos existentes en el maestro ó algunas tablas, tendremos que usar otro tipo de replicación.
Para configurar un sistema HS usando SR y transferencia de registros WAL, tendremos que realizar lo siguiente:
Todo este proceso es más fácil de lo que parece. A continuación vamos a ver paso por paso como hacerlo.
Lo primero que tenemos que hacer es instalar dos servidores lo más idénticos posibles. En nuestro caso hemos instalado dos servidores Ubuntu 10.04 server idénticos e instalados con la misma configuración.
A continuación tenemos que instalar la versión PostgreSQL 9.0 en los dos servidores. Podeis utilizar el método de instalación que más os guste, yo soy de la antigua escuela y PostgreSQL lo suelo instalar desde el código fuente y compiladolo yo mismo. Más información sobre esta manera de instalación se puede encontrar en los artículos "Instalación e inicialización básica de PostgreSQL desde el código fuente" y "Configuración básica de PostgreSQL".
En nuestro caso hemos instalado PostgreSQL 9.0 en el directorio /usr/local/pgsql-9.0/ de nuestros sistemas.
Lo primero que tenemos que hacer en el servidor maestro (server01 - 10.1.1.100) y en el esclavo (server02 - 10.1.1.101) es crear los directorios que vamos a utilizar en nuestro sistema de replicación:
root@server01:~# mkdir -p /var/pgsql/data root@server01:~# mkdir -p /var/pgsql/wal_arch root@server01:~# chown -R postgres:postgres /var/pgsql root@server01:~# chmod -R 0700 /var/pgsql root@server02:~# mkdir -p /var/pgsql/data root@server02:~# mkdir -p /var/pgsql/wal_arch root@server02:~# mkdir -p /var/pgsql/wal_shipped root@server02:~# chown -R postgres:postgres /var/pgsql root@server02:~# chmod -R 0700 /var/pgsql
Después configuramos el acceso mediante llaves SSH entre el maestro y el esclavo. Más información sobre este tema se puede encontrar en el artículo "Combinando SSH, cron y at".
Primero tenemos que generar nuestras claves pública y privada en el servidor maestro y el esclavo:
root@server01:~# su - postgres postgres@server01:~$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/postgres/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/postgres/.ssh/id_rsa. Your public key has been saved in /home/postgres/.ssh/id_rsa.pub. The key fingerprint is: 97:56:f8:a7:c3:71:90:5d:54:0f:e1:71:98:00:a6:ea postgres@server01
root@server02:~# su - postgres postgres@server02:~$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/postgres/.ssh/id_rsa): Created directory '/home/postgres/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/postgres/.ssh/id_rsa. Your public key has been saved in /home/postgres/.ssh/id_rsa.pub. The key fingerprint is: 51:8b:37:7c:e3:8f:27:4e:48:c5:df:eb:c2:bb:c1:04 postgres@server02
A continuación creamos el fichero /home/postgres/.ssh/authorized_keys en el maestro con el contenido del fichero /home/postgres/.ssh/id_rsa.pub en el esclavo. Y viceversa, creamos el fichero /home/postgres/.ssh/authorized_keys en el esclavo con el contenido del fichero /home/postgres/.ssh/id_rsa.pub en el maestro.
El siguiente paso es inicializar el cluster PostgreSQL en el servidor maestro:
postgres@server01:~$ /usr/local/pgsql-9.0/bin/initdb -D /var/pgsql/data --locale=C --encoding=UTF8
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale C.
The default text search configuration will be set to "english".
fixing permissions on existing directory /var/pgsql/data ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 32MB
creating configuration files ... ok
creating template1 database in /var/pgsql/data/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok
WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.
Success. You can now start the database server using:
/usr/local/pgsql-9.0/bin/postgres -D /var/pgsql/data
or
/usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l logfile start
Antes de arrancar el servidor maestro por primera vez tenemos que actualizar el fichero postgresql.conf con ciertos parámetros.
En este artículo solamente mostraremos los parametros mínimos que se necesitan para nuestro sistema de replicación. Más información sobre otros parametros extras que tambien se pueden modificar para controlar diversos aspectos de la replicación se pueden consultar en la documentación de PostgreSQL.
En un sistema que se vaya a utilizar en producción, tendremos que cambiar tambien otros parámetros, especialmente los que definen como se va a usar la memoria. Una introducción mínima a este tema se puede encontrar en el artículo "Configuración básica de PostgreSQL".
listen_addresses = '10.1.1.100' wal_level = hot_standby archive_mode = on archive_command = '/usr/local/pgsql-9.0/bin/archive_wal.sh -P %p -F %f' max_wal_senders = 5 wal_keep_segments = 10
Los parámetros que hemos cambiado en el maestro son:
El script archive_wal.sh en nuestro artículo es un script en BASH simple y minimo. Mediante el mismo copiamos los archivos WAL a archivar al directorio /var/pgsql/wal_arch en el maestro y al directorio /var/pgsql/wal_shipped en el esclavo.
En un sistema de producción este script deberia de comprobar y tener en cuenta posibles fallos ó errores que se pueden producir en el día a día. En un próximo artículo veremos como hacer este script más robusto.
#!/bin/bash
CHMOD="/bin/chmod"
COPY="/bin/cp"
SCP="/usr/bin/scp"
# Directorio usado por PostgreSQL para generar los ficheros WAL
PG_XLOG_DIR="/var/pgsql/data/pg_xlog"
# Directorio usado por PostgreSQL para archivar los ficheros WAL
PG_WAL_ARCH_DIR="/var/pgsql/wal_arch"
# Servidor PostgreSQL esclavo
STANDBY_SERVER="10.1.1.101"
# Directorio en servidor esclavo donde transferimos los ficheros
# WAL archivados en el servidor maestro.
PG_WAL_SHIPPED="/var/pgsql/wal_shipped"
NO_ARGS=0
E_OPTERROR=65
# ########################################
# ########################################
#
# Function archive_wal()
#
# ########################################
# ########################################
archive_wal(){
if $COPY -dp $ABSOLUTE_PATH $PG_WAL_ARCH_DIR/$WAL_FILE
then
$CHMOD 400 $PG_WAL_ARCH_DIR/$WAL_FILE
$SCP $PG_WAL_ARCH_DIR/$WAL_FILE $STANDBY_SERVER:$PG_WAL_SHIPPED
else
sleep 1
exit 1
fi
}
# ########################################
# ########################################
# Script invoked with no command-line args?
# ########################################
# ########################################
if [ $# -eq "$NO_ARGS" ]
then
help
exit $E_OPTERROR
fi
# ########################################
# ########################################
# Getting command options
# ########################################
# ########################################
while getopts "P:F:" Option
do
case $Option in
P)
ABSOLUTE_PATH=$OPTARG;;
F)
WAL_FILE=$OPTARG;;
esac
done
shift $(($OPTIND - 1))
# ########################################
# ########################################
# Sanity check
# ########################################
# ########################################
if [ -z $ABSOLUTE_PATH ]
then
echo "Error: Absolute path not defined"
echo
exit $E_OPTERROR
fi
if [ -z $WAL_FILE ]
then
echo "Error: WAL filename not defined"
echo
exit $E_OPTERROR
fi
archive_wal
exit 0
#
# EOF
Como vamos a utilizar Streaming Replication (SR), tenemos que definir tambien en el fichero pg_hba.conf del servidor maestro una linea que permita el acceso del proceso receptor WAL (WAL receiver) al servidor maestro.
host replication all 10.1.1.101 255.255.255.255 trust
Para facilitar las cosas en este artículo, hemos definido un acceso que no use SSL para encriptar el tráfico y que no necesite el uso de ninguna clave de acceso.
En un sistema en producción yo personalmente utilizaria SSL para encriptar el tráfico y el uso de certificados para la autentificación del servidor esclavo. Todo dependerá del nivel de seguridad que se quiera implementar. Más informacion sobre el tema se puede encontrar en el artículo "PostgreSQL y el uso de SSL".
Si utilizais un sistema de autentificación que necesite clave de acceso, por ejemplo "md5", tendreis que definir un fichero .pgpass en los servidores esclavos.
En estos momentos ya estamos preparados para arrancar el servidor maestro:
postgres@server01:~$ /usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l /var/pgsql/data/logserver.log start
Una vez arrancado el servidor maestro vamos a realizar una copia de seguridad 'base' mediante el mismo procedimiento que se utiliza con PITR. A continuación, restauraremos esta copia de seguridad 'base' en el servidor esclavo
postgres@server01:~$ /usr/local/pgsql-9.0/bin/psql template1 -c "SELECT pg_start_backup('copia base inicial')"
pg_start_backup
-----------------
0/1000020
(1 row)
postgres@server01:~$ tar -cvf /home/postgres/pg_base_backup.tar /var/pgsql/data/
postgres@server01:~$ /usr/local/pgsql-9.0/bin/psql template1 -c "SELECT pg_stop_backup()"
pg_stop_backup
----------------
0/10000D8
(1 row)
postgres@server01:~$ scp /home/postgres/pg_base_backup.tar 10.1.1.101:~/tmp/
postgres@server01:~$ ssh postgres@10.1.1.101 "cd / && tar -xvf /tmp/pg_base_backup.tar"
postgres@server01:~$ ssh postgres@10.1.1.101 "rm /var/pgsql/data/postmaster.pid"
Para terminar, lo único que nos queda hacer es modificar el fichero postgresql.conf y crear el fichero recovery.conf en el servidor esclavo.
En el fichero postgresql.conf definiremos los siguientes parámetros:
listen_addresses = '10.1.1.101' hot_standby = on
Los parámetros que hemos cambiado en el esclavo son:
Y en el fichero /var/pgsql/data/recovery.conf definiremos los siguientes parámetros:
standby_mode = 'on' primary_conninfo = 'host=10.1.1.100 port=5432 user=postgres' trigger_file = '/var/pgsql/data/pg_failover_trigger' restore_command = 'cp /var/pgsql/wal_shipped/%f "%p"'
Los parámetros que definimos en este fichero son:
Una vez realizado estos cambios podemos arrancar postgreSQL en el servidor esclavo.
postgres@server02:~$ /usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l /var/pgsql/data/logserver.log start
Y comprobar como el servidor maestro y el esclavo contienen los mismos datos.
postgres@server01:~$ /usr/local/pgsql-9.0/bin/psql
psql (9.0beta2)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access privileges
-----------+----------+----------+-----------+-------+-----------------------
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
postgres=#
postgres@server02:~$/usr/local/pgsql-9.0/bin/psql
psql (9.0beta2)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access privileges
-----------+----------+----------+-----------+-------+-----------------------
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
postgres=#
Después de ejecutar ciertos comandos en el servidor maestro, podemos comprobar como los cambios son replicados automáticamente al esclavo.
postgres@server01:~$ /usr/local/pgsql-9.0/bin/psql psql (9.0beta2) Type "help" for help. postgres=# CREATE DATABASE test001; CREATE DATABASE postgres=# \c test001 You are now connected to database "test001". test001=# CREATE TABLE test01 (id bigint, value bigint, primary key (id)); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "test01_pkey" for table "test01" CREATE TABLE test001=# INSERT INTO test01 (id, value) VALUES (1,1); INSERT 0 1
postgres@server02:~$ /usr/local/pgsql-9.0/bin/psql
psql (9.0beta2)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access privileges
-----------+----------+----------+-----------+-------+-----------------------
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
test001 | postgres | UTF8 | C | C |
(4 rows)
postgres=# \c test001
You are now connected to database "test001".
test001=# \d test01
Table "public.test01"
Column | Type | Modifiers
--------+--------+-----------
id | bigint | not null
value | bigint |
Indexes:
"test01_pkey" PRIMARY KEY, btree (id)
test001=# SELECT * from test01 ;
id | value
----+-------
1 | 1
(1 row)
test001=#
A partir de este momento ya solo os queda disfrutar de vuestro sistema de replicación.
Una vez que todo está funcionando, tendremos que mantener el sistema y administrarlo en caso de fallo del servidor maestro. Las tareas que tendremos que implementar/realizar serán:
Estas tareas las trataremos en próximos artículos. En el momento de escribir este artículo, y como ya hemos dicho, la versión 9.0 está todavía en fase beta y aun quedan algunos detalles por pulir. Además se están preparando por la comunidad algunos programas que ayudarán a la implementación de algunas de estas tareas de mantenimiento.
Más información:
http://www.postgresql.org/docs/9.0/static/high-availability.html
http://www.postgresql.org/docs/9.0/static/runtime-config-wal.html
http://www.postgresql-es.org/node/218
http://www.postgresql-es.org/node/219
http://www.linux-es.org/node/246
http://www.postgresql-es.org/node/384
http://www.postgresql-es.org/node/238
http://www.postgresql.org/docs/9.0/static/libpq-pgpass.html
Por favor, para preguntas sobre problemas y errores sobre el tema, utilizar el foro sobre Hot standby y Streaming replication de este servidor.
AUTOR: Rafael Martinez / PostgreSQL-es.org
Comentarios
Problemas con el script
Jue, 20/01/2011 - 21:20 — edhHola, he realizado todos los pasos (incluso he cambiado las rutas en el script acorde a mi Sistema Operativo), pero al momento de hacer el psql template1 -c "SELECT pg_stop_backup()" el postgres se queda aí un buen rato mostrando este mensaje:
NOTICE: finalización de pg_stop_backup completa, esperando que se archiven los segmentos WAL requeridos
WARNING: pg_stop_backup todavía espera que todos los segmentos WAL requeridos sean archivados (han pasado 60 segundos).
Así hasta que lo cancelo, no tengo problemas con las llaves de SSH pues puedo hacer el SCP sin ningún problema. Que puede estar mal?.
Estoy utilizando Postgres 9.0.2 en un FreeBSD 8.0
Gracias
No encuentro el archivo recovery.conf
Lun, 09/07/2012 - 20:03 — rantonanahola grupo, que tal, muy bueno el articulo, queria saber donde encontrar el archivo recovery.conf, instale postgres, anda perfecto, pero intento realizar la replicacion y no encuentro por ningun lado el archivo recovery.conf, me gustaria saber si lo debo crear yo, de ser asi, que contenido debe tener? gracias
No encuentro el archivo recovery.conf
Dom, 08/07/2012 - 03:30 — AnónimoHola, que tal, muy bueno el tutorial, pero la verdad soy nuevo en esto, instale postgres desde consola, pero no encuentro el archivo recovery.conf, me podrian decir si la instalacion esta mal echa, o si debo crear el archivo, de ser este ultimo el caso, que lineas debe contener, gracias.
Posible problema del postgresql.conf del esclavo
Mar, 09/08/2011 - 13:18 — AnónimoEn mi caso, (opensuse 11.4) los ficheros de configuracion estan en el PGDATA, como hemos copiado todo el directorio PGDATA de una maquina a otra, el fichero postgresql.conf y el pg_hba.conf son los del maestro, los que previamente hemos modificado.
Tendra activado todo esto :
listen_addresses = '10.1.1.100'
wal_level = hot_standby
archive_mode = on
archive_command = '/usr/local/pgsql-9.0/bin/archive_wal.sh -P %p -F %f'
max_wal_senders = 5
wal_keep_segments = 10
supongo que hay que comentarlo e incluir
listen_addresses = '10.1.1.101'
hot_standby = on
Gracias y usando DRBD
Dom, 05/08/2012 - 17:41 — phenobarbitalGracias por este post!, ha sido uno de los más esclarecedores acerca de un streaming replication en postgreSQL 9.
Sabes que he eliminado el problema de transferir los archivos de WAL de un servidor a otro creando un nodo DRDB two-primary entre ambos servidores (maestro y esclavo) usando DRBD+OCFS2, de tal manera que cuando el servidor primario ejecuta la tarea de *archive copying*, la realiza a la carpeta del nodo DRDB y este está automáticamente disponible para el servidor esclavo.
Muchas gracias por el post!, si lo deseas te envío la documentación del proceso!
Gracias nuevamente!, saludos!
Error de conexion
Mar, 19/04/2011 - 14:59 — AnónimoHola, muy bueno el articulo. Me funcionó todo perfecto, salvo que veo mensajes de error en el log del servidor master con la siguiente descripción:
2011-04-19 09:45:14 EDT LOG: replication connection authorized: user=admin host=10.120.90.120 port=58211
Host key verification failed.
lost connection
alguna sugerencia?
gracias,
adrian
Ok!!
Lun, 25/04/2011 - 20:44 — AnónimoMuy buen articulo.. lo segui y me funcione perfectamente. Pero me gustaria sabar como hago para que el servidor escalvo entre a funcionar en el momento que el maestro se caiga. ?
Listo, el problema era que
Mié, 20/04/2011 - 05:28 — AnónimoListo, el problema era que hay que confirmar el certificado del servidor al conectarse por scp por primera vez.
Otra pregunta, cómo sería la forma de depurar los archivos replicados entre ambos servers? se me están acumulando muchos gigas de wal files y no estoy seguro cuándo los puedo eliminar.
gracias,
adrian
Es un HADR de DB2 y Log
Mié, 30/06/2010 - 06:33 — AnónimoEs un HADR de DB2 y Log Shipping de SQL Server (implementados ambos hace muchos años). En hora buena para PostgeSQL !
Por lo que puedo ver... bastante rebuscado para configurar y administrar...
Quedo a la expectativa del siguiente articulo para ver como se produce el failover de manera automática y si existe la posibildad de que el cliente sea reconectado al servidor Standby de manera automática
Muy intersante y completo el artículo !
Da gusto leerlo !
Felicitaciones
Re: Es un HADR de DB2 y Log
Mié, 30/06/2010 - 08:25 — rafaelmaPor lo que puedo ver... bastante rebuscado para configurar y administrar...
Totalmente de acuerdo contigo en que el proceso de inicialización y administración de HS + SR es totalmente manual y se tiene que hacer algo al respecto.
El grupo de desarrolladores de PostgreSQL se ha centrado en esta versión en proveer la infraestructura necesaria para poder implementar y usar este tipo de replicación. La siguiente fase, será facilitar la administración y la monitorización de la replicación.
Yo supongo que en los próximos meses veremos aparecer una serie de programas/scripts por parte de la comunidad de usuarios de PostgreSQL que automatizaran muchos de los pasos que hay que realizar para inicializar este tipo de replicación en PostgreSQL.
--
Rafael Martinez
PostgreSQL-es.org