Replicação Master-to-Master com MySQL + HA com heartbeat


Essa solução é muito bem aplicada em ambientes que necessitem alta disponibilidade para serviço web e banco de dados.
Se sua necessidade é baseada em MySQL a partir da versão 5.5, leia esse outro post sobre a replicação com MySQL 5.5 e faça a parte do heartbeat a partir deste próprio.

O ambiente é montado por 2 servidores X (indiferente do hardware utilizado). O Software é basicamente um LAMP + heartbeat rodando em Debian e o tempo de configuração deve ser de uns 10 minutos.

Existem diversos artigos na internet de ambas soluções aplicadas aqui, porém decidi escrever um artigo sobre o assunto para que eu mesmo possa utilizado na posteridade, quando não me lembrar mais do procedimento.

Primeiros passos
Além da instalação do sistema LAMP (Linux, Apache, PHP, MySQL) e heartbeat será necessário separar 3 IPs fixos, sendo 1 para cada servidor e 1 para utilização de IP virtual. O IP virtual é o IP que será divulgado; as solicitações vindas dos peers remotos terão como destino o IP virtual. Desse modo, a máquina que estiver disponível como master responderá às solicitações. Caso essa máquina se torne indisponível, a outra assumirá automaticamente o IP virtual e passará a responder as solicitações.

No exemplo, utilizarei os IPs 172.0.0.200, 172.0.0.201 e virtual 172.0.0.202. Todas as requisições devem ir para 172.0.0.202.

A base de dados se comunica por background, diretamente pelos seus respectivos IPs. Qualquer operação que seja feita em uma, imediatamente é replicada para a outra e vice-versa. É possível adicionar slaves também para possuir um backup extra, mas não fará parte deste artigo.

Os servidores envolvidos serão nomeados aqui (e preferencialmente utilize a mesma denominação em seu ambiente) como node01 e node02.

* Configurando o heartbeat
Os arquivos de configuração do serviço heartbeat ficam em /etc/ha.d. Alguns arquivos não existem, mas basta criá-los com o respectivo conteúdo.

/etc/ha.d/authkeys
Seu conteúdo:

auth 2
2 sha1 test-ha

/etc/ha.d/haresources
Nesse arquivo especifica-se o nome do servidor master seguido do IP virtual e do script de reload das variáveis de ambiente de trabalho necessárias de se recarregar. Esse script pode ter qualquer nome e deve ficar em /etc/init.d/ como um serviço. Seu conteúdo será explanado mais adiante.

node01 172.0.0.202 reload_environment.sh

/etc/ha.d/ha.cf
Esse arquivo contém as pré-definições do funcionamento do heartbeat.

#arquivo de debug
debugfile /var/log/ha-debug
#arquivo de log
logfile /var/log/ha-log
#nivel de log
logfacility local0
#tempo de tolerância
keepalive 2
#tempo para considerar como morto
deadtime 10
#tempo de warning
warntime 10
#diretiva utilizada em tempo de inicialização do heartbeat
initdead 30
#...
udpport 694
#unicast respectivo ao host oposto; 201 no node01, 200 no node02
ucast eth0 172.0.0.201
# O auto_failback retoma como master automaticamente quando se encontra em condições de fazê-lo. Deixá-lo como
#off fará com que retorne somente se o host que assumir como master se perca.
auto_failback off
# node nodename ... -- must match uname -n
node node01 node02
#um host para ping. No caso, o gateway da rede de exemplo.
ping 172.0.0.254

#opcional
respawn root /usr/lib/heartbeat/ipfail
apiauth ipfail gid=root uid=root
apiauth default gid=root
apiauth cl_status gid=root

/etc/hostname
O nome curto deve aparecer como node01 ou node02. Constate isso com hostname -s.

/etc/hosts
Não experimentei resolver os nomes de outra maneira, então deixo a recomendação de incluir os IPs reais dos hosts no arquivo de hosts:

172.0.0.200 node01
172.0.0.201 node02

/etc/network/interfaces

Configure apenas os IPs reais em ambos os hosts. Por exemplo:

iface eth0 inet static
address 172.0.0.200
netmask 255.255.255.0
network 172.0.0.0
broadcast 172.0.0.255
gateway 172.0.0.254

/etc/apache2/ports.conf
Nesse arquivo deve-se especificar o IP virtual, tal como abaixo:

NameVirtualHost 172.0.0.202:80
Listen 80

# SSL name based virtual hosts are not yet supported, therefore no
# NameVirtualHost statement here
Listen 443

/etc/init.d/reload_environment.sh
Se preferir, renomeie o arquivo a contento, lembrando que se alterado, não esqueça de mudar o nome também no arquivo haresources.

O conteúdo desse arquivo é para exemplo. No caso, estou fazendo HA para o Asterisk:

#! /bin/sh
# /etc/init.d/reload_environment.sh
#

# Some things that run always
touch /var/lock/reload_environment.sh

# Carry out specific functions when asked to by the system
case “$1” in
start)
#para o asterisk
/usr/sbin/asterisk -rx “restart now”
#matar o script XXX
$(which kill) -9 $(ps ax|egrep ‘XXX.py’|egrep -v ‘grep’|awk ‘{print $1}’) 2>/dev/null
exit 0
;;
stop)
echo “Nothing to do”
;;
*)
echo “Usage: /etc/init.d/blah {start|stop}”
exit 1
;;
esac

exit 0

Adeque-o para sua necessidade.

Iniciando o HeartBeat
Feitas essas configurações, o serviço pode ser iniciado:
/etc/init.d/heartbeat start

Se tiver algum erro, verifique suas configurações, os logs e finalmente faça uma busca no google. Se tudo estiver perdido, escreva o problema nos comentários e __se__ realmente for algo que julgar válido, escrevo a resposta.

Em alguns momentos o IP virtual deverá subir na interface eth0 (que foi a utilizada nesse exemplo) do node01. Isso representa o sucesso da operação.

Se desejar fazer um teste com o HA, insira em /var/www um arquivo html com o nome do host. Com ambos os hosts em um mesmo switch, a partir de um client acesse o IP virtual e veja o nome de host que aparece; faça refreshs a vontade. Enquanto isso, peça a alguém que remova o cabo do node que estiver sendo exibido durante seus refreshs. O IP virtual subirá no outro node.

No arquivo ha.cf você poderá então mudar o tempo de vida conforme desejado, para mais ou para menos, mas tudo dependerá da aplicação que pretende rodar em seu ambiente. Fatores como qualidade de rede física e lógica também podem influenciar.

Configurando o MySQL para Master-to-Master
O MySQL é impressionante por sua facilidade de configuração. Além de estar toda concentrada no arquivo /etc/mysql/my.cnf, não é necessário instalar nenhum pacote adicional para a tarefa. Não estou certo de que posso ser claro na explicação dos parâmetros, mas seguramente é uma configuração funcional. Seu conteúdo:

#padrao
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
#
# * Basic Settings
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
language = /usr/share/mysql/english
skip-external-locking
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
#
# * Fine Tuning
#
key_buffer = 16M
max_allowed_packet = 16M
thread_stack = 128K
thread_cache_size = 8
# This replaces the startup script and checks MyISAM tables if needed
# the first time they are touched
myisam-recover = BACKUP
#max_connections = 100
#table_cache = 64
#thread_concurrency = 10
#
# * Query Cache Configuration
#
query_cache_limit = 1M
query_cache_size = 16M
#
# * Logging and Replication
#
# Both location gets rotated by the cronjob.
# Be aware that this log type is a performance killer.
#log = /var/log/mysql/mysql.log
#
# Error logging goes to syslog. This is a Debian improvement :)
#
# Here you can see queries with especially long duration
#log_slow_queries = /var/log/mysql/mysql-slow.log
#long_query_time = 2
#log-queries-not-using-indexes
#
# The following can be used as easy to replay backup logs or for replication.
# note: if you are setting up a replication slave, see README.Debian about
# other settings you may need to change.

#configuracoes para o servico
#no node02 utilize 2 e no node01 utilize 1 no server-id
server-id = 2
replicate-same-server-id= 0
auto-increment-increment= 2
auto-increment-offset = 2
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 500M

#bases que nao serao replicadas devem ser explicitamente ignoradas
binlog_ignore_db = mysql

#bases a replicar
binlog_do_db = asterisk
replicate-do-db = asterisk

binlog_do_db = base_2
replicate-do-db = base_2

binlog_do_db = base_3
replicate-do-db = base_3

binlog_do_db = base_n
replicate-do-db = base_n
#o master para o nodeX é o outro node
master-host = 172.0.0.201
#sugestao: replicar com o mesmo usuario e senha para facilitar a configuracao
master-user = replicador
master-password = escravo
master-connect-retry = 60

#
# * BerkeleyDB
#
# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12.
skip-bdb

[mysqldump]
quick
quote-names
max_allowed_packet = 16M

[mysql]
#no-auto-rehash # faster start of mysql but no tab completition

[isamchk]
key_buffer = 16M

#
# * IMPORTANT: Additional settings that can override those from this file!
# The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/

Terminada a configuração do my.cnf, deve-se ainda executar a permissão para o usuário de replicação. Algo como isto funciona bem:

mysql> grant all privileges on *.* to replicador@'%' identified by 'escravo';

Pode ser necessário (porque tenho certeza de que funcionou sem, mas já tive que fazê-lo) utilizar um comando para levantar o slave:

mysql> start slave;

A partir de então, pode-se testar fazendo um insert em qualquer um dos nodes e verificando sua inserção no outro e vice-versa. Alguns problemas podem ser resolvidos a partir desses documentos:
http://dev.mysql.com/doc/refman/4.1/pt/replication-problems.html
http://dev.mysql.com/doc/refman/4.1/pt/replication-faq.html

Alguns comandos também podem auxiliar em diagnósticos e informações, como:

show slave statusG;
show master statusG;
show processlist;

O comando show slave statusG deverá retornar em seu primeiro campo a informação:

Slave_IO_State: Waiting for master to send event

E mais abaixo, outras duas informações essenciais:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

Infelizmente é inviável explicar todos os parâmetros, todos os comandos diagnósticos, ações a tomar em casos de falhas etc, mas ao menos você poderá iniciar um projeto de replicação com HA a partir de um ambiente funcional e os avanços dependerão apenas de você. ;-)

Comments

comments

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.