quinta-feira, 8 de novembro de 2012

JON - Falha com Bundle

SINTOMA

Erro ao abrir a página do bundle no JON 'Failed to load bundle with the latest version data'.

PROBLEMA

Isto ocorre em função das versões excluídas das aplicações, ou seja, uma subquery retorna mais de uma linha, vide abaixo: 

 ERRO

more than one row returned by a subquery used as an expression 

 REPRODUÇÃO

Na KB da RedHat(https://access.redhat.com/knowledge/solutions/133893) mencionam que o erro ocorre somente para a coluna 'version', mas ocorre também se existe o mesmo valor, a mesma aplicação(name), no 'version_order', exemplo abaixo: 

 rhq=# SELECT * FROM rhq_bundle_version WHERE name LIKE 'manager' AND version_order=3 ORDER BY name; 

 id | name | description | version | version_order | action | config_def_id | bundle_id 
 -------+--------------+-------------+---------+---------------+------------------------------------------------------------------------------+---------------+---------- - 
 10882 | manager | Manager | 1.0i | 3 | | 11632 | 10131 : : : : : : : : : : : : : : : Deploying Test Bundle v1.0i to ${rhq.deploy.dir}... : : : : Done deploying Test Bundle v1.0i to ${rhq.deploy.dir}. : :
12213 | manager | Manager | 1.1b | 3 | | 12963 | 10131 : : : : : : : : : : : : : : : Deploying Test Bundle v1.1b to ${rhq.deploy.dir}... : : : : Done deploying Test Bundle v1.1b to ${rhq.deploy.dir}. : :

(2 rows) 

 SOLUÇÃO: Remover ou alterar o valor de uma das version_order duplicadas para a mesma aplicação, seja via JON ou via SQL.

segunda-feira, 24 de setembro de 2012

PostgreSQL - Política de retenção para o PGBarman

Segue abaixo um agente que fiz para criar uma política de retenção dos backups do PGBarman, enquanto não existe uma oficial:

IMPORTANTE:

Ajustar a variável $opt_path para onde ficam os backups dos seus servidores, ou seja o seu 'barman_home = /backup/vtl-backups' no 'barman.conf'.

---

#!/usr/bin/perl -w

# Rotate barman agent
# Author: Gabriel Prestes(LM2)
# Date: 21/09/2012

use File::stat;
use Time::Local;

($opt_db, $opt_ret, $opt_type) = @ARGV;

if($#ARGV<2){ print "Need args!\n"; exit(1);}

$opt_path="/backup/vtl-backups/$opt_db/$opt_type";
$opt_retinsec=($opt_ret*3600)*24;
print "|RETENTION POLICY - $opt_db - $opt_ret days - $opt_type |\n";

my @ls=`\$\(which ls\) -td $opt_path/*`;

foreach(@ls){

        chomp($_);

        if(($opt_type =~ "wals") and ($_ !~ "xlog.db")){

                $mtime=stat($_)->mtime;
                $timenow = timelocal(localtime());
                $tempo=$timenow-$mtime;
                $mtimereal=localtime($mtime);

                if($tempo>=$opt_retinsec){

                        $rm=`\$\(which rm\) -rf $_`;
                        print "WALS REMOVED : $_ - $mtimereal\n";

                } else{

                        print "WALS PRESERVED : $_ - $mtimereal\n";

                }

        }

        if($opt_type =~ "base"){

                $mtime=stat($_)->mtime;
                $timenow = timelocal(localtime());
                $tempo=$timenow-$mtime;
                $mtimereal=localtime($mtime);

                if($tempo>=$opt_retinsec){

                        $rm=`\$\(which rm\) -rf $_`;
                        print "BACKUP REMOVED : $_ - $mtimereal\n";

                } else{

                        print "BACKUP PRESERVED : $_ - $mtimereal\n";

                }

        }

}

exit(0);


---

Coloque depois no /etc/cron.d/barman o seguinte conteúdo: 

# m h     dom mon dow   user     command
  0 */4    *   *   *   barman   [ -x /usr/bin/barman ] && /usr/bin/barman -q cron
  0 */4    *   *   *   barman   [ -x /usr/bin/barman ] && /usr/bin/barman backup meu_pgsql
  1 0      *   *   *   barman   /backup/resources/bin/barman-retention.pl meu_pgsql 15 wals >> /backup/log/barman.log
  1 0      *   *   *   barman   /backup/resources/bin/barman-retention.pl meu_pgsql 15 base >> /backup/log/barman.log

Onde: 

/backup/resources/bin/barman-retention.pl - é o path do agente
meu_pgsql - é o nome que configurou na entrada do barman.conf
15 - 15 dias de retenção, pode ser menos ou mais, como queira
base - tipo de limpeza, esse argumento fará limpeza de todo backup sem archives com mais de 15 dias, por exemplo.
wals - tipo de limpeza, esse argumento fará limpeza de todos os wals com mais de 15 dias, por exemplo. 

Apenas um detalhe, PG-RMan dá de laço no Barman, pois Barman não tem incremental(versão 1.0.0).



terça-feira, 21 de agosto de 2012

Oracle - Duplicando bases no Oracle 10g R2

   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.



segunda-feira, 13 de agosto de 2012

Oracle - Tuning de sistema de arquivos


Requisitos: 

- Sistema Operacional: Baseado em RHEL 5/6; 
- Banco de Dados: Para Oracle, mas pode ser utilizado em partes para outros bancos;


Resumo das atividades executadas na aplicação dos parâmetros de tunning:

  • Alterar o elevator atual dos dois discos do sistema:

              echo deadline > /sys/block/sda/queue/scheduler
              echo deadline > /sys/block/sdb/queue/scheduler

*** Lembrando que 'sda' ou 'sdb' são os devices dos HDs onde o banco reside.

  • Alterar elevator padrão, para definir automaticamente na inicialização. Modificado grug.conf adicionando o parâmetro "levator=deadline" na linha do kernel:

        title RHEL (2.6.18-8.el5)
                  root (hd0,0)
                  kernel /vmlinuz-2.6.18-8.el5 ro root=/dev/VolGroup00/root elevator=deadline
                  initrd /initrd-2.6.18-8.el5.img

  • Alterar read_ahead_kb no sistema:

             echo 256 > /sys/block/sda/queue/read_ahead_kb
             echo 256 > /sys/block/sdb/queue/read_ahead_kb

*** Lembrando que 'sda' ou 'sdb' são os devices dos HDs onde o banco reside.

  • Alterar read_expire, referente ao elevator, para 300

              echo 300 > /sys/block/sda/queue/iosched/read_expire
              echo 300 > /sys/block/sdb/queue/iosched/read_expire

*** Lembrando que 'sda' ou 'sdb' são os devices dos HDs onde o banco reside.

  • Adicionar linhas no /etc/rc.local para carregar configurações de read_expire e read_ahead na inicialização:

               echo 256 > /sys/block/sda/queue/read_ahead_kb
               echo 256 > /sys/block/sdb/queue/read_ahead_kb
               echo 300 > /sys/block/sda/queue/iosched/read_expire
               echo 300 > /sys/block/sdb/queue/iosched/read_expire

*** Lembrando que 'sda' ou 'sdb' são os devices dos HDs onde o banco reside.

  • Adicionar as seguintes linhas no sysctl.conf:

kernel.sem = 250 32000 100 128
kernel.shmmni = 4096
fs.aio-max-nr = 1048576
net.ipv4.ip_local_port_range = 1024 65000
net.core.wmem_max = 1048576
net.core.wmem_default = 262144
net.core.rmem_max = 4194304
net.core.rmem_default = 4194304
fs.file-max = 6815744

vm.swappiness = 60

  • Recarregar as configurações do sysctl:

               sysctl -p

  • Editar o arquivo /etc/security/limits.conf e adicionar as seguintes linhas:

            oracle         soft    nofile  4096
            oracle         hard    nofile  63536
            oracle         soft    nproc   2047
            oracle         hard    nproc   16384

           Esses parâmetros (no limits.conf), uma vez setados, só serão válidos na próxima vez que o banco for levantado, pois são valores setados na sessão do usuário e neste caso a sessão é criada quando o banco é iniciado.

  • Também remountar os volumes com a opção "noatime", desabilitando o registro de horário de acesso aos arquivos.

          Alterar o /etc/fstab, ficando assim:

          /dev/VolGroup00/root    /                       ext3    defaults,noatime        1 1
          /dev/sdb1                        /oracle            ext3    defaults,noatime        1 1

  • Depois, remontar as duas partições:

          mount -o remount /
          mount -o remount /oracle

quarta-feira, 8 de agosto de 2012

GroundWork - Monitorar instâncias JBoss(mod_cluster)


Tirando o paraíso RHQ e JON podemos monitorar via Nagios core as instâncias que estão respondendo no mod_cluster do Apache. Ai vai uma forma muito simples e por que não dizer "sapeca" de monitorar se as instâncias do cluster JBoss são visualizadas:


Código fonte:



#!/usr/bin/perl

($opt_site, $opt_url, $opt_instance, $opt_warn, $opt_crit) = @ARGV;

my @instances = split(',', $opt_instance);
my $cmd;
my $msg;

if(!@instances){ print "CRITICAL - Set instances\n"; exit(2);}
if($#ARGV<3){ print "CRITICAL - Set arguments\n"; exit(2);}


foreach(@instances){

        chomp($_);
        @cmd = `/usr/local/groundwork/nagios/libexec/check_http -H $opt_site -u /$opt_url -R "$_" -w $opt_warn -c $opt_crit -t 60`;
        if($?!=0){$msg .= "$_ ";}

}

if($msg){

        print "CRITICAL - Instance DOWN: $msg\n";
        exit(2);

} else {

        print "OK - Instances($opt_instance) UP\n";
        exit(0);

}


Exemplo de uso:

./check-jbossmod.pl 127.0.0.1 mod_cluster-manager myinstancename1,myinstancename2,myinstancename3,myinstancename4 5 7

Onde:

127.0.0.1 - é o IP do Apache
mod_cluster-manager - esse é o location que você colocou no Apache para o handler do jkstatus, normalmente é mod_cluster-manager mesmo
myinstancename1,myinstancename2,myinstancename3,myinstancename4 - instâncias que quer verificar
5 - warning para tempo de resposta do Apache
7 - critical para tempo de resposta do Apache

sexta-feira, 20 de julho de 2012

GroundWork - Plotar gráficos de utilização de rede

No GWOS Community Edition é chato configurar o RRDTool para cada AIC. 

O gráfico de utilização de rede é extremamente chato de criar, tanto o comando de criação do gráfico quanto o de atualização. Então como solucionei: encapsulamento de plugin. 

Estranho né? Mas vou explicar. 

O plugin para Nagios que encontrei me dava a seguinte saída: 

/usr/local/groundwork/nagios/libexec/check_snmp_int.pl -n eth0 -H 10.1.112.1 -C 'public' -k -Y --label -w 1850,1950 -c 1951,1990 -B
eth0:UP (in=1084.1Kbps/out=9.6Kbps):1 UP: OK

Mas isto não me dá saída de performance para a plotagem do gráfico. 

Então, criei um outro plugin chamado 'check_interfaces_gwos.pl' e nele o seguinte conteúdo:

---

#!/usr/bin/perl

($opt_host, $opt_comm, $opt_if, $opt_warn, $opt_crit) = @ARGV;

# --- Variables prog --- #
our $path="/usr/local/groundwork/nagios/libexec/";
#our @DIV=();
our $cmd;
#our $input;
#our $output;
#our $speed;


# --- Var test --- #

if($#ARGV<2){


        print "ERROR - Check your parameters\n";
        exit(3);

}

# --- Run command --- #

my $cmd=`$path/check_snmp_int.pl -n $opt_if -H $opt_host -C '$opt_comm' -k -Y --label -w $opt_warn -c $opt_crit -B`;
chomp($cmd);

#DEBUG: eth0:UP (in=409.7Kbps/out=288.6Kbps):1 UP: OK

if($cmd =~m/in=([\d\.]+)([K|M|G]bps).+out=([\d\.]+)[K|M|G]bps(.+)/){

        my $status=$4;
        print "$cmd|in=$1Kbps;;;0; out=$3Kbps;;;0;\n";

        if($status=~"CRITICAL"){

                exit(2);

        }

        if($status=~"WARNING"){

                exit(1);

        }

        if($status=~"OK"){

                exit(0);

        }

        exit(3);

}

else {

        print "$cmd\n";
        exit(2);

}

---

Logo tinha a seguinte saída: 

eth0:UP (in=1329.3Kbps/out=10.7Kbps):1 UP: OK|in=1329.3Kbps;;;0; out=10.7Kbps;;;0;


Então foi só criar o gráfico no GWOS, conforme segue:

Graph Label:
Interface Bandwidth in Kbps
Service:
IF-Bandwidth
Use Service as a Regular Expression
OFF
Host:
*
Status Text Parsing Regular Expression:

Use Status Text Parsing instead of Performance Data
OFF
RRD Name
/usr/local/groundwork/rrd/$HOST$_$SERVICE$.rrd
RRD Create Command
$RRDTOOL$ create $RRDNAME$ --step 300 --start n-1yr $LISTSTART$DS:$LABEL#$:GAUGE:900:U:U$LISTEND$ RRA:AVERAGE:0.5:1:8640 RRA:AVERAGE:0.5:12:9480
RRD Update Command
$RRDTOOL$ update $RRDNAME$ -t $LABELLIST$ $LASTCHECK$:$VALUELIST$ 2>&1
Custom RRDtool Graph Command
''
Enable
ON

sábado, 19 de maio de 2012

Offtopic - Os próximos passos

Apesar do mesmo objetivo, o dilema é escolher qual dos caminhos pegar primeiro:



I've planned each charted course
Each careful step along the byway
And more, much more than this
I did it my way

Yes there were times, I'm sure you knew
When I bit off more than I could chew
But through it all when there was doubt
I ate it up and spit it out

I faced it all and I stood tall
And did it my way

quinta-feira, 12 de abril de 2012

Zimbra - Agente de backup Online para ZCSOS[BUGFIX]

    Senhores,

    Hoje encontrei um novo bug que não tinha mapeado no post anterior deste agente, ou seja, foi feito um bugfix, segue:

PROBLEMA:

- Falha na tentativa de restaurar contas específicas ou um restore de todas as contas do domínio;

SOLUÇÃO:

- Procure a entrada abaixo no agente:

                # --- Check if account exist in backup --- #
                @tgzbkp=`\$\(which find\) $path -name "$opt_account*tgz" | \$\(which sort\) -r`;
                @icsbkp=`\$\(which find\) $path -name "$opt_account*ics" | \$\(which sort\) -r`;


-  Ajuste para:


                # --- Check if account exist in backup --- #
                @tgzbkp=`\$\(which find\) $path/ -name "$opt_account*tgz" | \$\(which sort\) -r`;
                @icsbkp=`\$\(which find\) $path/ -name "$opt_account*ics" | \$\(which sort\) -r`;





terça-feira, 10 de abril de 2012

MySQL - Convertendo tabelas

Bueno, vamos aos dados problemas:

1 - Tenho uma tabela MyISAM e quero converter para o Innodb; 
2 - Tenho uma tabela em InnoDB e quero passar para MyISAM;


Mas por que converter?

http://www.oficinadanet.com.br/artigo/789/mysql_-_diferenca_entre_innodb_e_myisam

Dada a dica, vamos aos comandos:

1 - ALTER TABLE `teste` ENGINE = InnoDB;
2 - ALTER TABLE `teste` ENGINE = MYISAM;

Pelo teste que realizei, o processo de ALTER TABLE foi mais eficiente e mais rápido que exportar um dump e importar depois alterando o engine.

segunda-feira, 19 de março de 2012

PostgreSQL - Desafio de DBAs

Em razão do apreço que sinto pelo meu colega Sebastian Webber(http://swebber.me/blog/) e inspirado pelos resultados obtidos pelo tuning com a redução de mais de 50% no tempo de restore no post anterior, propus um desafio para meu colega DBA.

Ambos tem a mesma base, mesma VM, mesmo hospedeiro, o menor tempo de export e import do mesmo dump ganha. Veja bem pessoal, este desafio não tem como objetivo provar quem é o melhor, pois ambos, eu e o Sebastian, somos f*da, mas é pela zoação mesmo.

Regras:

1 - Sem ler documentação durante a prova, bem como sem leitura de documentação até 48 horas antes do desafio(sim, eu confio nele);
2 - Somente 1 tentativa de tuning;
3 - Sem tuning de S.O., só em nível de memória e configuração da instância, ou seja, postgresql.conf e pg_hba.conf;

Definições:

1 - Mesma VM e hospedeiro, ou seja, fatores iguais;
2 - Mesma base para ambos(15GB);

Que vença o melhor, ops, eu? Será? risos

Em breve publicaremos os resultados e o ganhador.

PostgreSQL - Agilizando a restauração de um DUMP

-Definições-

Nome da database que trabalharemos: producao
Owner: postgresql
Versão do banco: 9.1

-Primeiras avaliações-

Tipo de dump a se utilizar:

Tamanho da Database no PostgreSQL:  45MB
Dump em plaintext: 51MB
Em custom format: 20MB

Logo, defini que o tipo de dump que utilizarei é o custom para o restore.

Configuração atual do PostgreSQL: 

shared_buffers = 24MB
maintenance_work_mem = 16MB
checkpoint_segments = 3
effective_cache_size = 128MB

Tempo de restore:


 time pg_restore -d producao -U postgres -j 2 /opt/resources/producao.dmpc

real    0m9.264s
user    0m0.562s
sys     0m0.427s

-j : São jobs em paralelo, como tenho dois processadores, no caso vCPUs utilizei 2, se tiver mais utilize mais. 


Dimensionando o PostgreSQL:  


shared_buffers = 48MB
maintenance_work_mem = 32MB
checkpoint_segments = 6
effective_cache_size = 128MB



Tempo de restore:


 time pg_restore -d producao2 -U postgres -j 2 /opt/resources/producao.dmpc




real    0m8.295s
user    0m0.532s
sys     0m0.429s


Resultado: baixamos 1 segundo

Vamos adiante: 


shared_buffers = 96MB
maintenance_work_mem = 64MB
checkpoint_segments = 12
effective_cache_size = 128MB

Tempo de restore:


time pg_restore -d producao3 -U postgres -j 2 /opt/resources/producao.dmpc

real    0m8.367s
user    0m0.552s
sys     0m0.420s



Estranho, piorou, pode ser relacionado aos checkpoints, então vamos manter os seguimentos de checkpoints em 6 e duplicar os itens de memória.

Ajustes no PostgreSQL:


shared_buffers = 192MB
maintenance_work_mem = 128MB
checkpoint_segments = 6
effective_cache_size = 128MB

Tempo de restore: 


time pg_restore -d producao4 -U postgres -j 2 /opt/resources/producao.dmpc

real    0m8.699s
user    0m0.547s
sys     0m0.415s

Não funcionou como esperávamos, então baixamos o shared_mem, e só aumentamos o maintenance_work_mem. 

Vamos lá:

shared_buffers = 64MB
maintenance_work_mem = 256MB
checkpoint_segments = 6
effective_cache_size = 128MB

Tempo de restore:  


time pg_restore -d producao5 -U postgres -j 2 /opt/resources/producao.dmpc

real    0m9.683s
user    0m0.563s
sys     0m0.473s

Em momento algum o servidor fez swap: 

             total       used       free     shared    buffers     cached
Mem:           996        919         77          0         45        747
-/+ buffers/cache:        125        871
Swap:         2015          0       2015

Bom, nada funcionou, então vamos voltar a segunda configuração onde obtive 8.2s de restore:


time pg_restore -d producao8 -U postgres -j 2 /opt/resources/producao.dmpc

real    0m9.862s
user    0m0.598s
sys     0m0.461s

Ih, agora deu problema, como o tempo mudou tanto? Minha explicação é, utilização de recursos do servidor, por ser uma VM limitada os resultados variam de acordo com a utilização do hospedeiro e da VM, por exemplo, IO de disco, memória, processamento e etc. 

Bueno, mas lembra do -j, vamos utilizar mais do que 2 processos em paralelo, vamos para 4 sem alterar as configurações de memória do banco:

time pg_restore -d producao9 -U postgres -j 4 /opt/resources/producao.dmpc

real    0m7.559s
user    0m0.601s
sys     0m0.506s

Olha, que loucura! Vamos para 8? 

time pg_restore -d producao10 -U postgres -j 8 /opt/resources/producao.dmpc

real    0m7.601s
user    0m0.580s
sys     0m0.467s

Humm... melhor resultado obtido foi quando duplicamos os jobs em paralelo para o dobro do número de processadores. Mas vamos fazer uma loucura:

Tenho 100 conexões disponíveis, vamos rodar com -j em 50:

time pg_restore -d producao11 -U postgres -j 50 /opt/resources/producao.dmpc

real    0m6.959s
user    0m0.585s
sys     0m0.407s

Ok, melhor resultado obtido, então vamos aos 90? 

time pg_restore -d producao12 -U postgres -j 90 /opt/resources/producao.dmpc

real    0m5.216s
user    0m0.494s
sys     0m0.395s

Que loucura, estamos chegando a uma conclusão. Mas calma, vamos aos 100? 

time pg_restore -d producao13 -U postgres -j 100 /opt/resources/producao.dmpc

real    0m5.523s
user    0m0.547s
sys     0m0.414s

É, perdemos. Agora vamos para as conclusões: 

DICA 1 - Utilização do servidor: Não é novidade que o servidor do PostgreSQL deve ser dedicado, bem como deve ser feito um "hardening" dos processos em execução no servidor, quando menos processos rodando, melhor. 

DICA 2 - Tuning de restauração: Apesar dos tempos obtidos não deixarem claro quais parâmetros do banco devemos dimensionar para uma melhor performance, sugiro atuar nos parâmetros abaixo:

shared_buffers : não utilize valores muito grandes, deixe a maior parte de sua memória para o 'cache_effective_size'. 
maintenance_work_mem : Lembre-se que alterar este valor impacta na quantidade de memória utilizada nas conexões, então cuidado.
work_mem: Ajuste de forma compatível com o 'maintenance_work_mem'. 
checkpoint_segments : Não esqueça desse cara também, mas não aumente muito, pois lembre que este possui outros amigos, o 'checkpoint_timeout', entre outros que devem crescerem juntos. 
effective_cache_size : Esse cara faz com que seus índices funcionem, então deixe um bom valor de sua memória reservada para ele. 


DICA 3 - Jamais faça SWAP, se estiver fazendo, reduza os valores de memória. 


DICA 4 - Como restauração de uma base normalmente não é feita em horário de produção, utilize um bom valor de suas conexões disponíveis para os jobs paralelos do 'pg_restore', isso reduz muito o tempo de restauração. 

Espero não ter escrito muita besteira. Abraços.


sexta-feira, 3 de fevereiro de 2012

PostgreSQL - Liberando sessões "sem medo"

PostgreSQL: Imagine o seguinte cenário, 2 mil locks na base, banco parado e você precisa fazer algo.

Para estas situações recorra ao SHELL, ele é seu amigo:

Primeiro identifique os processos:

for id in `ps -ef | grep transaction | awk '{print $2}'`; do echo $id; done

Se todos realmente forem sessões prendendo ou você não tem tempo para identificar e pode contar com a perda de conexões, mande o comando:

for id in `ps -ef | grep transaction | awk '{print $2}'`; do `kill -15 $id`; done

Pronto, todo mundo que estava em transação pendente morreu. Mas os locks foram liberados e o banco segue feliz, agora o próximo passo é mais complexo, é descobrir, 'POR QUÊ?'. Esse fica pro próximo post.