Primeira consideração: RMAN é DEMAAAAISSS!!!
É, para quem já duplicou bases no 11g pelo RMAN que tem a barbada de duplicar base sem backup prévio, não sabe o quão chato é fazer pelo Oracle 10g R2 que precisa de backup full e mais archives para replicar a base. Mas para isso tem um monte de post de outros blogs e mais uma excelente documentação da Oracle para lhe ensinar.
Agora, o que vou colocar aqui em poucos ou nenhum blog tem, que é automatizar esse processo, os seja, colocar um job no cron do banco clone e ele ser atualizado todo dia a noite. E mais, esse processo todo com o clone rodando em um servidor diferente do master. Vamos pelo começo, ou seja, as necessidades de infra, o que precisamos fazer no sistema operacional dos dois servidores.
Requisitos:
1 - Sistema Linux;
2 - Tudo isso foi feito em ambiente com RHEL 5;
3 - Ajustes no LISTENER;
4 - Backup da base de produção;
5 - Agente de duplicação;
No master:
1 - Configurar um servidor NFS e exportar o diretório onde ficarão os backups do RMAN, pois eles tem que ser visíveis no servidor que ficará com o clone;
2 - Comunicação de SSH por chave RSA;
1 - NFS Como fazer:
1.1 - Ativar o serviço 'netfs', 'nfs', 'nfslock' e 'portmap';
1.2 - Adicionar no arquivo /etc/exports o seguinte conteúdo:
/oracle/duplicate-rman 10.0.0.2/32(rw,sync,no_root_squash)
Observe que o diretório é onde será armazenado o backup do RMAN e o resto é para que o servidor clone consiga montar o compartilhamento.
1.3 - Feito isso adicione no /etc/fstab do servidor clone o seguinte:
10.0.0.1:/oracle/duplicate-rman /oracle/duplicate-rman nfs defaults 0 0
Depois dê um 'mount -a'.
IMPORTANTE: Tem que ser a mesma estrutura no clone, ou seja, '/oracle/duplicate-rman'.
No servidor clone os serviços 'nfslock', 'portmap' e 'netfs' precisam estar ativos e no boot para caso de reinicialização.
2 - SSH por RSA Como fazer:
2.1 - No clone como root execute: ssh-keygen -t rsa o resto que for solicitado é só ir no Enter.
2.2 - Copie o conteúdo do arquivo '/root/.ssh/id_rsa.pub' e coloque no '/root/.ssh/authorized_keys' do servidor master.
Teste e veja se conecta sem senha do clone no servidor master, se sim, estamos indo bem.
3 - Ajustes no TNSNAMES Como fazer:
No listener.ora do servidor do clone o LISTENER deve ser registrado estaticamente:
Exemplo:
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = dbclone)
(ORACLE_HOME = /oracle/app/oracle/product/10.2.0/db)
(SID_NAME = dbclone)
)
4 -
Backup da base de produção Como fazer:
Bom, se você não sabe nada de Perl, não se preocupe, alguém já fez para você, sim, eu, então é só usar, lá vai:
4.1 - No servidor master crie a seguinte estrutura de diretórios:
/opt/resources/duplicate-rman/lib
/opt/resources/duplicate-rman/log
4.2 - Crie o arquivo '/opt/resources/duplicate-rman/lib/oracle.params' e coloque o seguinte conteúdo:
TARGETDB=DBPROD
TARGETUSER=sys
TARGETPASS=pwd
Onde os parâmetros são, o nome para conectar na base de produção, este nome deve ser o mesmo que você utiliza para conectar na base de dados do master no master, no meu caso 'DBPROD', usuário e senha para conectar, sim, pode ser um usuário 'backup' desde que seja DBA.
4.3 - Crie o arquivo '/opt/resources/duplicate-rman/lib/rman-backup.sql' e coloque o seguinte conteúdo:
# Oracle RMAN Command File
# This script opens a single channel to the target database and does a
# backup of all the database.
run {
CROSSCHECK ARCHIVELOG ALL;
BACKUP DATABASE PLUS ARCHIVELOG;
SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT';
DELETE FORCE NOPROMPT OBSOLETE;
}
# EOF
Sim lindo, ou linda(vai que é uma mulher DBA), este é o seu backup, não vou explicar um por um das linhas, isso tem na doc da Oracle. Vai ter que confiar em mim, ou ler a doc.
4.4 - Conecte na base DBPROD no RMAN, logado no master e faça as seguintes configurações:
CONFIGURE
CHANNEL DEVICE TYPE DISK FORMAT '/oracle/duplicate-rman/%U';
Ah, se quiser e não usar o RMAN pra mais nada só para duplicar, pode setar a política de retenção dos backups para 1 dia, ai toda noite quando atualizar a base CLONE ficará mantido só um backup no repositório do RMAN, que se já observou e conhece um pouquinho de Oracle sabe que o controle do catálogo de backups do RMAN está no control file e não em uma base 'CATDB', se quiser pode usar, para o meu post não será o caso.
4.5 - Configurando Agente de Backup:
Crie o arquivo '/opt/resources/duplicate-rman/rman-backup_oracle.pl', transforme-o em executável e coloque nele o seguinte conteúdo:
#!/usr/bin/perl
#
# Description: Backup Oracle Database (RMAN backup)
#
#
#Author:
# Gabriel Prestes (helkmut@gmail.com)
#
#08-14-2012 : Created
#08-18-2012 : Modified
# Modules
use strict;
use POSIX;
use Getopt::Long;
use File::Basename;
# ENVs
$ENV{"USER"}="oracle";
$ENV{"HOME"}="/home/oracle";
$ENV{"ORACLE_BASE"}="/oracle/app/oracle";
$ENV{"ORACLE_HOME"}="$ENV{'ORACLE_BASE'}/product/10.2.0/db";
$ENV{"ORACLE_SID"}="dbprod";
$ENV{"LD_LIBRARY_PATH"}="$ENV{'ORACLE_HOME'}/lib:$ENV{'ORACLE_HOME'}/oc4j/j2ee/home/lib/";
# Global variables
our $name = basename($0, ".pl");
our $version="1.0";
our $date=strftime("%m-%d-%Y",localtime);
our $path = "/opt/resources/duplicate-rman";
our $log= "$path/log/rman-$date.log";
our ($opt_help, $opt_verbose, $opt_version, $opt_choice);
sub main {
# --- Get Options --- #
getoption();
# --- Init agent --- #
logger("INIT AGENT - $date");
# --- Run RMAN job --- #
backup();
# --- End agent --- #
logger("END AGENT - $date");
exit;
}
sub getoption {
Getopt::Long::Configure('bundling');
GetOptions(
'V|version' => \$opt_version,
'h|help' => \$opt_help,
'v|verbose=i' => \$opt_verbose,
);
if($opt_help){
printHelp();
exit;
}
if($opt_version){
print "$name - '$version'\n";
exit;
}
if(!$opt_verbose){
$opt_verbose = 0;
}
}
sub logger {
return (0) if (not defined $opt_verbose);
my $msg = shift (@_);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
$wday++;
$yday++;
$mon++;
$year+=1900;
$isdst++;
if ($opt_verbose == 0){
print "$msg\n";
}
else {
open(LOG, ">>$log") or do error();
printf LOG ("%02i/%02i/%i - %02i:%02i:%02i => %s\n",$mday,$mon,$year,$hour,$min,$sec,$msg);
close(LOG);
}
}
sub printHelp {
my $help = <<'HELP';
This is a agent to Backup Oracle instance - RMAN
Arguments:
-V : Version
-h : Help
-v 1: Send to log
-v 0: Show log in console
Required APIs:
use strict;
use Getopt::Long;
use POSIX;
use File::Basename;
E.g: $path/rman-backup_oracle.pl -v 1
HELP
system("clear");
print $help;
}
sub backup {
# --- Defs --- #
my $counter = 0;
my $script = "$path/lib/rman-backup.sql";
my $rman = "$ENV{'ORACLE_HOME'}/bin/rman";
my $oracle_targetdb;
my $oracle_targetuser;
my $oracle_targetpass;
my @prop_split=();
my @cmd=();
# --- Read props file --- #
open (PROPS, "$path/lib/oracle.params") or error();
my @props_array = <PROPS>;
close(PROPS);
foreach(@props_array){
chomp($_);
@prop_split = split(/=/,$_);
if($counter == 0){$oracle_targetdb = $prop_split[1];}
if($counter == 1){$oracle_targetuser = $prop_split[1];}
if($counter == 2){$oracle_targetpass = $prop_split[1];}
$counter++;
}
# --- Run backup job --- #
logger("Running RMAN JOB please wait");
@cmd = `/bin/su - $ENV{'USER'} -c '$rman TARGET $oracle_targetuser/$oracle_targetpass\@$oracle_targetdb \@$script'`;
if($? == 0){logger("BACKUP FINISHED - SUCCESS");}
else {logger("BACKUP FINISHED - ERROR");}
# --- RMAN out --- #
logger("RMAN log");
open(LOG, ">>$log");
foreach(@cmd){
printf LOG ("$_");
}
close(LOG);
}
&main
Sim, este é o agente que faz o backup, mas quem roda ele? O servidor do clone e não o master, que é o próximo passo.
5 - Agente de duplicação Como Fazer:
5.1 - Vamos para o servidor clone crie a seguinte estrutura de diretórios:
/opt/resources/duplicate-rman/log/
/opt/resources/duplicate-rman/lib/
5.2 - Crie o arquivo '/opt/resources/duplicate-rman/lib/after-duplicate.sh' e coloque nele o seguinte conteúdo:
#!/bin/bash
# Script to sinc Master and Standby Oracle
source /etc/profile
source ~/.bash_profile
sqlplus / as sysdba<<EOS
SPOOL /opt/resources/duplicate-rman/log/sqlplus-after.log
SHUTDOWN IMMEDIATE
STARTUP MOUNT PFILE='/opt/resources/duplicate-rman/lib/initdbclone.ora'
ALTER DATABASE NOARCHIVELOG;
ALTER DATABASE OPEN;
ALTER TABLESPACE TEMP ADD TEMPFILE '/oracle/oradata/dbclone/temp01.dbf' SIZE 3G REUSE;
SPOOL OFF
exit
EOS
# EOF
Resumindo a função deste arquivo é depois que for feita a duplicação você precisa iniciar ele com um PFILE como os parâmetros que quiseres, tirar o banco clone de ARCHIVEMODE e também adicionar um tempfile para a TEMP TABLESPACE, pois depois da duplicação os TEMPFILES não virão.
5.3 - Crie o arquivo '/opt/resources/duplicate-rman/lib/before-duplicate.sh' e coloque nele o seguinte conteúdo:
#!/bin/bash
# Script to sinc Master and Standby Oracle
source /etc/profile
source ~/.bash_profile
sqlplus / as sysdba<<EOS
SPOOL /opt/resources/duplicate-rman/log/sqlplus-before.log
SHUTDOWN IMMEDIATE
STARTUP NOMOUNT PFILE='/opt/resources/duplicate-rman/lib/initdbclone.ora'
SPOOL OFF
exit
EOS
# EOF
Resumindo, a função do arquivo é antes da duplicação parar o banco de dados DBCLONE e colocar ele para NOMOUNT
5.4 - Criando um PFILE para o DBCLONE:
Rode na base de produção: CREATE PFILE='/opt/resources/duplicate-rman/lib/initdbclone.ora' FROM SPFILE;
Pronto, agora coloque no mesmo caminho no servidor do DBCLONE.
5.5 - Ajuste de permissões:
O seu usuário 'oracle' deve ter permissão de escrita na pasta '/opt/resources/duplicate-rman/log', ajuste isso.
5.6 - Crie o arquivo '/opt/resources/duplicate-rman/rman-duplicate_dbprod.pl' no servidor clone, transforme-o em executável e coloque nele o seguinte conteúdo:
#!/usr/bin/perl
#
# Description: Clone Oracle Database (RMAN duplicate feature)
#
#
#Author:
# Gabriel Prestes (helkmut@gmail.com)
#
#08-18-2012 : Created
#08-19-2012 : Modified
# Modules
use strict;
use POSIX;
use Getopt::Long;
use File::Basename;
# ENVs
$ENV{"USER"}="oracle";
$ENV{"HOME"}="/home/oracle";
$ENV{"ORACLE_BASE"}="/oracle/app/oracle";
$ENV{"ORACLE_HOME"}="$ENV{'ORACLE_BASE'}/product/10.2.0/db";
$ENV{"ORACLE_SID"}="dbclone";
$ENV{"LD_LIBRARY_PATH"}="$ENV{'ORACLE_HOME'}/lib:$ENV{'ORACLE_HOME'}/oc4j/j2ee/home/lib/";
# Global variables
our $name = basename($0, ".pl");
our $version="1.1";
our $date=strftime("%m-%d-%Y",localtime);
our $path = "/opt/resources/duplicate-rman";
our $log= "$path/log/rman-duplicate-$date.log";
our ($opt_help, $opt_verbose, $opt_version);
sub main {
# --- Get Options --- #
getoption();
# --- Init agent --- #
logger("INIT AGENT - $date");
# --- Before duplicate dbclone --- #
beforeduplicate();
# --- RMAN job --- #
duplicate();
# --- After duplicate dbclone --- #
afterduplicate();
# --- End agent --- #
logger("END AGENT - $date");
exit;
}
sub getoption {
Getopt::Long::Configure('bundling');
GetOptions(
'V|version' => \$opt_version,
'h|help' => \$opt_help,
'v|verbose=i' => \$opt_verbose,
);
if($opt_help){
printHelp();
exit;
}
if($opt_version){
print "$name - '$version'\n";
exit;
}
if(!$opt_verbose){
$opt_verbose = 0;
}
}
sub logger {
return (0) if (not defined $opt_verbose);
my $msg = shift (@_);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
$wday++;
$yday++;
$mon++;
$year+=1900;
$isdst++;
if ($opt_verbose == 0){
print "$msg\n";
}
else {
open(LOG, ">>$log");
printf LOG ("%02i/%02i/%i - %02i:%02i:%02i => %s\n",$mday,$mon,$year,$hour,$min,$sec,$msg);
close(LOG);
}
}
sub printHelp {
my $help = <<'HELP';
This is a agent to Duplicate database with RMAN
Arguments:
-V : Version
-h : Help
-v 1: Send to log
-v 0: Show log in console
Required APIs:
use strict;
use Getopt::Long;
use POSIX;
use File::Basename;
E.g: $path/rman-duplicate_dbprod.pl -v 1
HELP
system("clear");
print $help;
}
sub afterduplicate {
# --- After duplicate commands --- #
my $cmd;
logger("Shutdown AUXILIARY DATABASE and startup in noarchivemode");
$cmd=`/bin/su - $ENV{'USER'} -c '$path/lib/after-duplicate.sh'`;
if($?!=0){logger("DUPLICATE AFTER FINISHED - ERROR"); exit;}
}
sub beforeduplicate {
# --- After duplicate commands --- #
my $cmd;
logger("Shutdown AUXILIARY DATABASE and startup in nomount mode");
$cmd=`/bin/su - $ENV{'USER'} -c '$path/lib/before-duplicate.sh'`;
if($?!=0){logger("DUPLICATE BEFORE FINISHED - ERROR"); exit;}
}
sub duplicate {
# --- Defs --- #
my $counter = 0;
my $script = "$path/lib/rman-duplicate.sql";
my $rman = "$ENV{'ORACLE_HOME'}/bin/rman";
my $oracle_targetdb;
my $oracle_targetuser;
my $oracle_targetpass;
my $oracle_targetssh_host;
my $oracle_targetssh_port;
my $oracle_targetssh_user;
my $oracle_targetssh_cmd;
my $oracle_auxdb;
my $oracle_auxuser;
my $oracle_auxpass;
my $scn;
my $until_rman;
my @prop_split=();
my @cmd=();
# --- Read props file --- #
open (PROPS, "$path/lib/oracle.params");
my @props_array = <PROPS>;
close(PROPS);
foreach(@props_array){
chomp($_);
@prop_split = split(/=/,$_);
if($counter == 0){$oracle_targetdb = $prop_split[1];}
if($counter == 1){$oracle_targetuser = $prop_split[1];}
if($counter == 2){$oracle_targetpass = $prop_split[1];}
if($counter == 3){$oracle_targetssh_host = $prop_split[1];}
if($counter == 4){$oracle_targetssh_port = $prop_split[1];}
if($counter == 5){$oracle_targetssh_user = $prop_split[1];}
if($counter == 6){$oracle_targetssh_cmd = $prop_split[1];}
if($counter == 7){$oracle_auxdb = $prop_split[1];}
if($counter == 8){$oracle_auxuser = $prop_split[1];}
if($counter == 9){$oracle_auxpass = $prop_split[1];}
$counter++;
}
# --- Run RMAN job in master --- #
logger("Run backup job in master to duplicate please wait");
@cmd = `/usr/bin/ssh -p $oracle_targetssh_port $oracle_targetssh_user\@$oracle_targetssh_host '$oracle_targetssh_cmd'`;
$scn=`/usr/bin/ssh -p $oracle_targetssh_port $oracle_targetssh_user\@$oracle_targetssh_host '/bin/ls -l /oraarchive/dbprod/*.arc | /usr/bin/tail -1'`;
if($scn =~ m/^*._.*_(.*)_.*$/){
$scn=$1;
logger("Duplicate database in SEQUENCE $scn");
}
else {
logger("Agent fail please contact your DBA - but Crond started");
@cmd=`/etc/init.d/crond start`;
exit;
}
@cmd = `/bin/rm -rf $script`;
# --- This var($until_rman) set context duplicate job --- #
logger("Generate DUPLICATE script with UNTIL TIME");
$until_rman = "
# Oracle RMAN Command File
# This script opens a single channel to the target database and does a
# backup of all the database.
run \{
SET UNTIL SEQUENCE $scn THREAD 1;
DUPLICATE TARGET DATABASE TO DBCLONE PFILE=\'/opt/resources/duplicate-rman/lib/initdbclone.ora\' DB_FILE_NAME_CONVERT=\(\'/oracle/oradata/dbprod/\'\,\'/oracle/oradata/dbclone/\'\,\'/oracle/oradata/dbprod/\'\,\'/oracle /oradata/dbclone/\'\,\'/oracle/oradata/dbprod/\'\,\'/oracle/oradata/dbclone/\'\) LOGFILE \'/oracle/oradata/dbclone/redo01a.log\' SIZE 100M REUSE\,\'/oracle/oradata/dbclone/redo02a.log\' SIZE 100M REUSE;
\}
# EOF
";
open(RMAN, ">$script");
printf RMAN ($until_rman);
close(RMAN);
# --- Duplicate run --- #
logger("Running DUPLICATE DATABASE please wait");
@cmd = `/bin/su - $ENV{'USER'} -c '$rman TARGET $oracle_targetuser/$oracle_targetpass\@$oracle_targetdb AUXILIARY $oracle_auxuser/$oracle_auxpass\@$oracle_auxdb \@$script'`;
if($? == 0){logger("DUPLICATE FINISHED - SUCCESS");}
else {logger("DUPLICATE FINISHED - ERROR");}
# --- RMAN out --- #
logger("RMAN log");
open(LOG, ">>$log");
foreach(@cmd){
printf LOG ("$_");
}
close(LOG);
}
&main
5.7 - Criei o arquivo '/opt/resources/duplicate-rman/lib/oracle.params' e coloque o seguinte conteúdo:
TARGETDB=DBPROD
TARGETUSER=sys
TARGETPASS=minhasenha
TARGETSSH_HOST=10.0.0.1
TARGETSSH_PORT=22
TARGETSSH_USER=root
TARGETSSH_CMD=/opt/resources/duplicate-rman/rman-backup_oracle.pl -v 1
AUXILIARYDB=DBCLONE
AUXILIARYUSER=sys
AUXILIARYPASS=minhasenha
5.8 - Teste a execução: nohup /opt/resources/duplicate-rman/rman-duplicate_dbprod.pl -v 1 > /root/nohup &
O log da execução estará em '/opt/resources/duplicate-rman/log/', avalie e se funcionar(não sei que documentei por completo o processo, se não me avise comentando na postagem)
5.9 - Vamos colocar no crontab do root do servidor clone o seguinte:
# --- Maintenance JOBs --- #
#Duplicate DBPROD to DBCLONE - Every day in 22:25
25 22 * * * /opt/resources/duplicate-rman/rman-duplicate_dbprod.pl -v 1 >> /dev/null 2>&1
# Rman Agent log - 60 days
00 23 * * * /usr/bin/find /opt/resources/duplicate-rman/log -mtime +60 -exec /bin/rm -f {} \;
# --- Maintenance JOBs --- #
Resumindo a base clone é atualizada todos os dias 22:45 e o log das execuções são limpos todos os dias para logs mais antigos que 60 dias.
5.10 - Vamos colocar no crontab do root do servidor de produção o seguinte:
# --- Maintenance jobs --- #
# Rman Agent log - 60 days
00 23 * * * /usr/bin/find /opt/resources/duplicate-rman/log -mtime +60 -exec /bin/rm -f {} \;
# --- Maintenance jobs --- #
Faz a mesma coisa de limpeza que no clone.
6 - Resumo da opera:
Toda noite o agente do clone executará e fará o seguinte:
1 - Passa a base DBCLONE para nomount;
2 - Loga por SSH e executar o backup full da base de produção com RMAN;
3 - Obtem última sequence dos archives do DBPROD e inicia a duplicação com o RMAN;
4 - Abre a base duplicada tira de archivemode e adiciona um TEMPFILE para a TABLESPACE TEMP;
Fim.