quarta-feira, 26 de agosto de 2015

MySQL - Backup full e incremental com innobackupex



     Em um dos últimos projetos em que atuei precisei configurar uma rotina de backup no MySQL. Com isto veio a necessidade do uso do 'innobackupex', pois o mesmo me possibilitaria gerar backups completos e incrementais, sejam eles compactados ou não, sem downtime da instância de 1TB.

      Depois de ler diversos materiais relacionados a execução de backup remoto ou local, cheguei aos comandos abaixo, bem como as conclusões que seguem: 

  1. O backup incremental não foi de fato efetivo, pois a redução de armazenamento foi de 50% por backup com relação ao backup completo;
  2. A compactação durante a execução mostrou-se eficaz no sentido de espaço em disco, entretanto péssima para o tempo de execução do backup, pois o uso do 'gzip' fez com que a opção de multi-thread parasse e a compressão ficasse em um só core do processador;
  3. O comando de extração xbstream, quando utilizada a compressão de stream não funcionou, precisando do outro lado no servidor de destino utilizar ele diretório por diretório;
  4. Tar sem compressão foi a solução mais aderente. 

    Comandos utilizados:

Backup FULL:

     innobackupex --stream=tar /tmp | cat - > /disk2/backup-dir/full-base-backup/backup.tar

Aplicando os binlogs em caso de restore(para abrir a instância):

cd /data/mysql ; tar xvfi /mnt/backup.tar ; innobackupex --apply-log /data/mysql

Script de backup:

#!/bin/bash
#     Full backup mysql percona
# Developped by:
#     Gabriel Prestes - helkmut@gmail.com
# Last update:
#     2015-08-21
#
## DEFAULT SETTINGS ###########################################
BACKUP_DESTINATION='/disk2/backup-dir/full-base-backup'
HOST_NAME="teste.athore.org"
DAYS=3

source /etc/profile

## STOP EDITING HERE ###########################################

function remove() {
        log_this "Removing older than ${DAYS_TO_KEEP}"
find ${BACKUP_DESTINATION}/* -type f -name "${HOST_NAME}*" -mtime +${DAYS_TO_KEEP} | xargs ls -lha find ${BACKUP_DESTINATION}/* -type f -name "${HOST_NAME}*" -mtime +${DAYS_TO_KEEP} | xargs rm -fv

}

function log() {
        echo $(date "+[ %d-%m-%Y %H:%M:%S ]:") "${1}"
}

function start() {

        log "Starting backup job..."

        innobackupex --stream=tar /tmp | cat - > ${BACKUP_DESTINATION}/${HOST_NAME}-$(date "+%d-%m-%Y_%H-%M-%S").tar
        flag=$?

        if [ ${flag} -eq 0 ]; then

                echo "0" > /opt/resources/backup/percona-mysql-backup-agent.mon

        else

                echo "1" > /opt/resources/backup/percona-mysql-backup-agent.mon

        fi


        remove

        log "Job fished."
}


start


Crontab:

## Backup full daily Percona - MySQL

00 12   * /opt/resources/backup/percona-mysql-backup-agent.sh > /opt/resources/backup/percona-mysql-backup-agent.out 2>&1








      

quarta-feira, 27 de maio de 2015

Puppet - Exportando site.pp para CouchDB



   Quando estamos no caminho da automação de processos e garantia de estado uma das ferramentas poderosas e nossa aliada é o Puppet. 

   Quanto mais se conhece de Puppet, mais quer integrá-lo ao CouchDB para que o mesmo responda de forma inteligente consultas no formato JSON via Hiera. 

   O que essa publicação de propõe é a facilitar, apoiando-o na migração do site.pp dos seus ambientes, hoje com todas informações de classes, includes e tasks carregadas dos agents no site.pp e que serão exportadas para o CouchDB. 

   Segue abaixo a explicação de uso de um programa que criei para automatizar o processo de exportação. Ele já faz a análise se o agent existe no CouchDB, caso exista o mesmo ignora, do contrário, exporta para a base. 

   Repositório GIT do programa: https://github.com/helkmut/couchdb-populate

   Exemplo de execução: 

[root@Teste-CouchDB rb-couchdb-populate]# ./bin/rb-couchdb-populate.rb
| Starting agent Current Time : 2015-02-04 23:41:22 -0200 |
| Test database connection > configuration |
| Connection PASS |
| Directory /etc/puppet exist |
| Parse file /etc/puppet/environments/prd/manifests/site.pp |
| Import node 1.teste.com.br to CouchDB |
| Import node 2.teste.com.br to CouchDB |
| Import node 3.teste.com.br to CouchDB |
| Import node 4.teste.com.br to CouchDB |
| Import node 5.teste.com.br to CouchDB |
| Import node 6.teste.com.br to CouchDB |
| Import node 7.teste.com.br to CouchDB |
| Import node 8.teste.com.br to CouchDB |
| Import node 9.teste.com.br to CouchDB |
| Import node 10.teste.com.br to CouchDB |
| Import node 11.teste.com.br to CouchDB |
| Import node 12.teste.com.br to CouchDB |
| Import node 13.teste.com.br to CouchDB |
| Import node 14.teste.com.br to CouchDB |
| Import node 15.teste.com.br to CouchDB |
| Import node 16.teste.com.br to CouchDB |
| Import node 17.teste.com.br to CouchDB |
| Import node 18.teste.com.br to CouchDB |
| Import node 19.teste.com.br to CouchDB |
| Import node 20.teste.com.br to CouchDB |
| Parse file /etc/puppet/environments/dev/manifests/site.pp |
| Node 21.teste.com.br already exists in CouchDB |
| Import node 22.teste.com.br to CouchDB |
| Import node 23.teste.com.br to CouchDB |
| Import node 24.teste.com.br to CouchDB |
| Node 25.teste.com.br already exists in CouchDB |
| Node 26.teste.com.br already exists in CouchDB |
| Import node 27.teste.com.br to CouchDB |
| Node vagrant-teste.local already exists in CouchDB |
| Parse file /etc/puppet/environments/hlg/manifests/site.pp |
| Import node 28.teste.com.br to CouchDB |
| Import node 29.teste.com.br to CouchDB |
| Import node 30.teste.com.br to CouchDB |
| Node 31.teste.com.br already exists in CouchDB |
| Import node 32.teste.com.br to CouchDB |
| Import node 33.teste.com.br to CouchDB |
| Import node 34.teste.com.br to CouchDB |
| OK - Program end with success |

   O que o programa não contempla: 

1 - se o Document existir ele não atualiza os fields;
2 - ainda não está fazendo parse das 'tasks', mas faz de chamadas 'includes' e 'classes'.  

   Sugiro testar em um laboratório com uma Vagrant de CounchDB, hiera e Puppet Master. Naturamente que isto não se aplica a versão Enterprise do Puppet, que já não faz o gerenciamento dos agents por site.pp. 


quinta-feira, 4 de dezembro de 2014

MySQL - SQL thread is not running?



Imagine o seguinte cenário, você tem um MySQL master e um slave configurado com replicação assíncrona. Por alguma razão você descobre através da consulta de status do slave que o SQL thread parou de funcionar, mas o IO thread continua em execução buscando logs do master. 

COMO IDENTIFICAR A CAUSA DO PROBLEMA? 

        - Verifique o log de erro do MySQL, lá constará o código de erro gerado durante o processo de replicação, seja ela por row ou statement, e este código já lhe ajudará a buscar soluções na documentação. 

        - No mesmo log você poderá verificar qual a posição e binlog do master em que ocorreu o erro, isso será muito útil no futuro. 

        Consideraremos aqui neste exemplo que o erro foi causado por um SQL que violou uma CONSTRAINT de PK ou FK.

        O ERRO É DE VIOLAÇÃO E AGORA? 

 1 - Primeiramente utilizando o mysqlbinlog acesse o log binário do master indicado no erro do MySQL do slave e com o -j informe a posição;
2 - Ao identificar o STATEMENT ou ROW que foi executado/alterada você poderá acessar o MySQL master e comparar os dados da tabela alvo com a do slave;
3 - Aqui é certo que você identificará diferenças de integridade entre as tabelas, pois lembre que o slave está atrasado com relação ao master;
4 - Pule uma posição no log do master no slave: 

CHANGE MASTER TO MASTER_LOG_FILE = '$master_log_name' MASTER_LOG_POS = $master_log_pos+1; 

Viu agora o porquê da importância da posição do log lido no master? Onde $master_log_name e $master_log_pos podem ser obtidos ao consultar o status do slave

 5 - Feito isto ative o slave novamente:
     
START SLAVE;

Se nenhum outro erro ocorreu após o slave se atualizar em relação ao master(veja no status do slave) então é hora de verificar como ficaram os dados da tabela que apresentou o erro. COMO? 

        Se seu slave não tem delay setado e o fluxo de alterações e inserções da tabela não é constante podes realizar a validação por MD5SUM. COMO? 
mysql > \P md5sum 
mysql > select * from tabela_alvo; (não faça isso em tabelas gigantes!)
O resultado no master e slave devem ser o mesmo valor de md5. Para normalizar o client basta executar \P. 

Bom, se ainda assim tem diferenças na tabela alvo, considere ajustar os dados dela via dump com a function slave parada, ou ressincronizar o slave. 







quarta-feira, 13 de agosto de 2014

PostgreSQL - Acompanhando SQL via log

Ei você! Sim, você que precisa acompanhar DML/DDL/DCL via log, mas não sabe como, ai vai uma dica para acompanhar o mesmo via log em tempo real(depois que já executou, evidente): 


1 - logado por SSH no banco alvo execute :

watch -n 10 'tail -n 1500 /var/log/pgsql.log | grep -i -A 1 "MEU SQL"'

Em resumo de 10 em 10 segundos você ficará visualizando os SQLs, e caso seja um UPDATE/DELETE que os parâmetros sejam vistos somente na próxima linha será apresentado. 

Requisitos: 

1 - Cluster PGSQL com log configurado para o syslog/rsyslog;
2 - Log ser o pgsql.log no '/var/log';
3 - Ser Linux; 
4 - Log configurado para logar tudo ou pelo menos SQL por duration. 

quinta-feira, 8 de maio de 2014

Oracle - ORA-01017 usando JDBC

ERRO:

root@appserver:/opt/resources/oracletest/jdbctool-1.0/bin# CLASSPATH=/opt/resources/oracletest/ojdbc5.jar ./jdbctool -u teste -p meuteste jdbc:oracle:thin:@10.0.1.16:1521/DB_MYAPP

Unable to connect to database: java.sql.SQLException: ORA-01017: invalid username/password; logon denied


SINTOMA: Ao integrar uma aplicação em um servidor de aplicação Java onde a comunicação seja estabelecida via JDBC ainda que usuário e senha estejam corretos e você consiga conectar através do SQLPlus o erro gerado no servidor de aplicação será o item mencionado no ERRO.


SOLUÇÃO: Existem duas formas de solucionar este erro, uma é atualizar seu JDBC para o ojdbc mais atual, outra é executar o comando abaixo como sys ou system na base alvo:

ALTER SYSTEM SET SEC_CASE_SENSITIVE_LOGON = FALSE; 


JUSTIFICATIVA: Por padrão o  Oracle11g ou superior vem configurado para senhas case_sensitive e o  ojdbc5(baseado em client 10g) ou inferior enviam as senhas em maiúsculo, logo o erro e a solução de ou atualizar o ojdbc6(baseado em client 11g) ou a desativação do recurso.

SUGESTÃO: Atualizar o driver JDBC.