terça-feira, 4 de janeiro de 2011

PostgreSQL - Configurando um 'Warm-standby'

   Primeiramente precisamos entender o que é e como funciona uma solução do PostgreSQL em 'Warm-standby'. Este tipo de configuração consiste em ter duas instâncias, uma master em archive mode e outra em slave importando os archives gerados pelo master. Importante, a instância slave não aceita conexões, está em standby e só é ativada quando um arquivo definido pelo DBA existe.

   Então sem mais papo, vamos ao cenário:


Sistema Operacional: CentOS 5.5 x86
Banco de dados: PostgreSQL 8.4

Pacotes RPM instalados:

postgresql84-test-8.4.5-1.el5_5.1
postgresql84-8.4.5-1.el5_5.1
postgresql84-docs-8.4.5-1.el5_5.1
postgresql84-libs-8.4.5-1.el5_5.1
postgresql84-devel-8.4.5-1.el5_5.1
postgresql84-contrib-8.4.5-1.el5_5.1
postgresql-libs-8.1.18-2.el5_4.1
postgresql84-server-8.4.5-1.el5_5.1

Ambiente: A ideia é configurar duas instâncias no mesmo servidor, sendo que a master responde na porta TCP 5432(padrão PostgreSQL) e a slave na porta TCP 5433.


Ajuste dos services do /etc/init.d, acesse este diretório e execute os comandos abaixo para configurar duas instâncias:

# cp postgresql postgresql-5432
# cp postgresql postgresql-5433

Vamos ativar os serviços no boot do Linux e também desativar o serviço padrão do PostgreSQL:

# chkconfig --del postgresql
# chkconfig --add postgresql-5432
# chkconfig --level 345 postgresql-5432 on
# chkconfig --add postgresql-5433
# chkconfig --level 345 postgresql-5433 on

Agora precisamos ajustar os parâmetros de inicialização do serviço para que os parâmetros de uma instância não conflite com a outra, para isso ajuste cada arquivo ficando assim:

/etc/init.d/postgresql-5432:

PGPORT=5432
PGDATA=/var/lib/pgsql/data1
PGLOG=/var/lib/pgsql/pgstartup1.log

/etc/init.d/postgresql-5433:
PGPORT=5433
PGDATA=/var/lib/pgsql/data2
PGLOG=/var/lib/pgsql/pgstartup2.log

Agora crie a cluster database do Master com o seguinte comando:

# service postgresql-5432 initdb

Feito isto precisamos ajustar os seguintes parâmetros no postgresql.conf do Master:

archive_mode = on                                                                      
archive_command = 'rsync -a %p /data1/archive_log/%f'
archive_timeout = 60

Salve e crie o diretório /data1/archive_log onde o usuário 'postgres' deve ser o dono.

Pode iniciar o banco com o comando: # service postgresql-5432 start

Se tudo deu certo o PostgreSQL está respondendo na porta padrão 5432 onde você pode conectar se ajustou o pg_hba.

Agora vamos ao slave.

Precisamos colocar o banco master para backup e depois fazer uma cópia física que será repassada para o slave. Seguem comandos para executar quando logado no banco master:

postgres=# SELECT pg_start_backup('test1');
postgres=# \q


Agora faça a cópia para o data2, pode ser feito via rsync, aqui vou usar o copy mesmo:

# cp -rfp /var/lib/pgsql/data1 /var/lib/pgsql/data2
# rm -rf /var/lib/pgsql/data2/pg_xlog/*

Vamos parar o processo de backup do master:

postgres=# SELECT pg_stop_backup();
postgres=# \q


Ajuste o arquivo '/var/lib/pgsql/data2/postgresql.conf' e deixe da seguinte forma:

archive_mode = on                                                                    
archive_command = 'rsync -a %p /data2/archive_log/%f'  

Crie o diretório /data2/archive_log onde o usuário 'postgres' deve ser o dono.

Crie a arquivo '/var/lib/pgsql/data2/recovery.conf' com o seguinte conteudo:                  

restore_command = 'pg_standby -d -s 1 -t /tmp/pgsql.trigger.5432 /data2/archive_log %f %p %r 2>> /var/lib/pgsql/data2/pg_standby.log'
recovery_end_command = 'rm -f /tmp/pgsql.trigger.5432'

Agora precisamos criar o cara que irá criar a trigger caso o master pare de funcionar, então vamos criar o '/root/slave.pl' com o seguinte conteúdo:

#!/usr/bin/perl

($opt_port, $opt_timeout) = @ARGV;

if(!$opt_port or !$opt_timeout){

        print "Missing arguments\n";
        exit(2);

}

my $path="/var/log";
my @cmd=();
my $flag=0;
my $counter=0;

for(;$counter<=$opt_timeout;$counter++){

        @cmd=`/bin/netstat -anp | /bin/grep .s.PGSQL.$opt_port`;

        unless($cmd[0]){$flag++; print "$cmd[0]\n";}

        sleep(01);

}

# Date log
my $date=`/bin/date`;
chomp($date);

# --- Thresholds to slave trigger --- #
if($flag>=$opt_timeout){

        print "ERROR - $date - Master is down - starting slave\n";
        @cmd=`su - postgres -c 'touch /tmp/pgsql.trigger.5432'`;
        exit(0);

}

if($flag<$opt_timeout){

        print "OK - $date - Master up\n";
        exit(0);

}


# EOF


Este arquivo deve ser executável e vamos colocar ele na Cron do root da seguinte forma:

*/2 * * * * /root/slave.pl 5432 15 >> /var/log/slave.log 2>&1

Agora precisamos ainda migrar os archives do master para o diretório do slave, isso é feito com o seguinte comando no Crontab do root:

*/2 * * * * rsync -avzo --remove-sent-files /data1/archive_log/ /data2/archive_log/ >> /dev/null 2>&1


Pronto, pode subir o slave 'service postgresql-5433' e acompanhar os logs de importação através do pg_standby.log no diretório 'data2'. Para testar basta parar o master com um 'service postgresql-5432 stop' e aguarde até o slave ser acionado.

Nesta configuração foi utilizado o 'smart-failover' que é o mais adequado para garantir a recuperação dos dados.

Importante, para um ambiente de produção pode utilizar um LVS monitorando a porta 5432, pois assim você terá um IP de serviço para suas aplicações.







                                                                  

6 comentários:

  1. tá, blz... massss....
    nessas configurações, quando o master cair e o slave subir, ele (o slave) permitirá insert,update e delete?
    por que utilizando o stream-replication o slave fica em read-only mesmo após o master cair.

    ResponderExcluir
  2. Sim, permitirá sem problemas. Podes simular.

    ResponderExcluir
  3. nao funcionou colega, mas vlw por ter me respondido.
    a propósito, revise o que deve ser escrito no postgresql.conf do master, acho que voce esqueceu de dizer que o wal_level deve ser igual a "archive", o log reclamou disso (nao versao 9.0.1 do PG)

    ResponderExcluir
  4. Na versão 8.4 a qual este post se aplica não existe 'wal_level' isso foi implementado no HOT-standby.

    Ainda assim, se quiser envie os logs com erros do pgstartup ou do log do pgsql para que eu possa te ajudar.

    ResponderExcluir