quarta-feira, 19 de fevereiro de 2014

Escalando MongoDB - Replica Sets - Passo 1

E ai pessoal?

Depois de muito tempo sem postar nada, resolvi compartilhar o que venho estudando sobre MongoDB. São meus primeiros passos e por enquanto não há nada muito profundo, mas como existem poucas referencias em nosso idioma, acho que vale a pena escrever o que venho aprendendo.

Nesse post vamos montar uma estrutura de Replica Set do MongoDB com três servidores da forma mais simples possível. Vou usar Amazon EC2 para montar essa estrutura, mas você pode usar máquinas virtuais locais de forma fácil, por exemplo com VirtualBox ou Vagrant.

Antes de colocar a mão na massa, acho importante explicar que existem duas formas de escalar o MongoDB. A primeira delas chama-se Replica Set e consiste em replicar os dados entre instancias de MongoDB permitindo maior tolerancia a falhas. A segunda chama-se Sharding e consiste em dividir os dados (e a carga de I/O) entre instancias de MongoDB ou entre Replica Sets permitindo maior performance em leitura e escrita.

A criação de um Replica Set pode ser considerado um dos primeiros passos no processo de escala do MongoDB pois ele garante redundância dos dados e tolerância a falhas caso uma ou mais instancias parem de funcionar graças ao mecanismo de failover.

Bom, vamos ao que interessa =D

Passo 1 - Instalar o MongoDB


Como o objetivo do post não é mostrar a criação de instancias EC2 nem a instalação do MongoDB, vou colocar aqui dois links para estas tarefas. O importante é que você deve proceder com a instalação normalmente, como um standalone.


IMPORTANTE!

Use instancias EC2 de 64 bits devido a limitação do MongoDB. Mais infos neste link


Passo 2 - Criando as replicas


Depois de ter instalado o MongoDB, precisamos criar mais duas replicas (ou quantas você achar necessário) deste servidor. Você pode refazer o passo 1 para cada uma das novas instancias, ou criar uma imagem da instancia que preparamos e usa-la como base para as outras instancias. Ferramentas como VirtualBox e Vagrant também permitem "clonar" VMs.


Passo 3 - Configurar Replica Set


Aqui vamos ver o que realmente importa. Em cada um dos servidores acesse o arquivo de configuração que fica em /etc/mongod.conf e adicione a seguinte linha:

replSet = <nome do seu replica set>

É importante colocar o mesmo nome em todas as instancias que farão parte do Replica Set. Depois disso reinicie o MongoDB.

Quando usamos um Replica Set um dos servidores assume o papel de servidor primário e os outros servidores são secundários, isso quer dizer que todas as operações de escrita devem ser feitas no servidor primário e os servidores secundários aplicam estas mudanças de forma assíncrona.

Escolha um servidor para ser o primário, acesse o terminal do mongo e digite os seguintes comandos:

> rs.initiate()

Ele vai responder:

{
"info2" : "no configuration explicitly specified -- making one",
"me" : "ip-10-172-237-120:27017",
"info" : "Config now saved locally.  Should come online in about a minute.",
"ok" : 1
}

Depois disso vamos adicionar as demais instancias. É recomendado que você crie entradas de DNS para cada servidor a fim de flexibilizar a configuração. Como estou fazendo apenas um exemplo, farei com os IPs mesmo.

rs0:PRIMARY> rs.add('192.168.148.153')
{ "ok" : 1 }

rs0:PRIMARY> rs.add('192.168.73.213')
{ "ok" : 1 } 

Veja que o terminal mudou. Eu chamei meu Replica Set de rs0 e ele já aparece como primário.
Vamos ver como ficou nossa configuração:

rs0:PRIMARY> rs.conf()
{
"_id" : "rs0",
"version" : 3,
"members" : [
{
"_id" : 0,
"host" : "192.168.237.120:27017"
},
{
"_id" : 1,
"host" : "192.168.148.153:27017"
},
{
"_id" : 2,
"host" : "192.168.73.213:27017"
}
]
}

Voilà! Replica Set feito! Vamos brincar um pouco agora:

rs0:PRIMARY> db.teste.insert({'a':1})

rs0:PRIMARY> db.teste.find()
{ "_id" : ObjectId("530416cefb31eb86c9fbe3d4"), "a" : 1 }

rs0:SECONDARY> db.teste.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }


"Pow Rafael, deu pau aqui cara!"

Calma! Esta é uma proteção que existe nas instancias secundárias devido a eventuais inconsistências que podem existir entre o servidor primário e os secundários. Nesse modelo todas as escritas devem ser feitas no servidor primário, mas as leituras podem ser feitas nos servidores secundários, que, sob grande carga, podem demorar para aplicar as alterações feitas no servidor primário. A isso se da o nome de replication lag.

Para permitir a leitura basta usar o seguinte comando nos servidores secundários:

rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.teste.find()
{ "_id" : ObjectId("530416cefb31eb86c9fbe3d4"), "a" : 1 }


Conclusão


Bom, nada do que falamos aqui é muito diferente do que esta na documentação oficial. O que tentei fazer foi passar essa informação de forma mais simples e direta, explicando alguns conceitos importantes que me ajudaram a começar a entender as coisas.

Existem diversos outros aspectos importantes relacionados a escala de serviços como MongoDB e se você esta trabalhando na nuvem o numero de fatores é maior ainda. Eles vão desde o sistema de arquivos que você esta usando (prefira o ext4 por exemplo), passando por opções de RAID até a velocidade de acesso ao disco (no caso da AWS os IOPS). Se você quiser se aprofundar mais, leia os links que deixei de referência.

Se ficou algum dúvida pode me perguntar, eu provavelmente não saberei responder, mas vou tentar! Perguntas, dicas, sugestões, correções e etc são sempre bem vindos!

Referências