Opa!

Hoje o post será mais prático, e queremos trazer para vocês uma forma de criar um cluster de MongoDB utilizando Docker. O próprio Docker tem um tutorial de como você pode criar a sua imagem de mongo e executá-lo dentro do Docker, para quem não viu ainda, segue o link. Para quem não conhece, MongoDB é um tipo de banco de dados orientado a documentos ou seja, utiliza o conceito de dados e documentos auto contidos e auto descritivos, e isso implica que o documento em si já define como ele deve ser apresentado e qual é o significado dos dados armazenados na sua estrutura.

O MongoDB também é chamado de banco de dados NoSql, mas isso é outro assunto 🙂 . O que veremos neste artigo é como você pode montar um cluster de instâncias do mongo utilizando como backend containers Docker. Isso é possível pois o existe na “caixa” do mongo algumas formas de se construir um cluster, uma delas é utilizando o método de replica set, que consiste em ter instâncias  para onde a informação será replicada, e dessa forma garantir a persistência das informações, abaixo segue uma imagem ilustrativa de como isso funciona:

Dessa forma garantimos a persistência e tolerância a falha dos dados que estão sendo armazenados. Neste post mostraremos como utilizar essa abordagem, não entraremos em detalhes mais fundos do Mongo pois não é este o objetivo.

Vamos lá?

Antes de tudo, é recomendável que os servidores possam ser acessíveis via nome, caso não seja possível, você pode adicionar nos hosts os endereços para que isso seja temporariamente possível.

Em seguida precisamos criar o diretório no host onde será persistido os dados do banco:

$ mkdir /opt/mongo
$ mkdir /opt/mongo/data $ cd /opt/mongo

É importante lembrar que essas pastas devem estar criadas em todos os nós, para que dessa forma seja possível persistir os dados em todos os nós.

Agora você precisa gerar uma chave que será utilizada para realizar a sincronia entre as replicas, e garantir que os dados além de trafegar de forma segura, estejam replicados entre todos os nós, essa chave deve ser copiada para todos os nós também.

$ openssl rand -base64 741 > mongodb-keyfile $ chmod 600 mongodb-keyfile $ chown 999 mongodb-keyfile

Configurado este arquivo, precisamos subir um container para realizar algumas tarefas administrativas, para depois subir o cluster propriamente dito:

$ docker run --name mongo -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node1" -p 27017:27017 -d mongo:2.6.5 --smallfiles

Vamos as configurações agora:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
db.createUser( {
     user: "admin",
     pwd: "SENHA_DE_ADMIN",
     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
   });

db.createUser( {
     user: "root",
     pwd: "SENHA_DE_ROOT",
     roles: [ { role: "root", db: "admin" } ]
   });
> exit
[email protected]:/# exit

Remova o container para que possamos agora subir o ambiente todo:

$ docker stop mongo
$ docker rm mongo

Agora sim vamos subir o ambiente:

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node1" --add-host node1.db:${node1} --add-host node2.db:${node2} --add-host node3.db:${node3} -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Acesse este container para inicializar o cluster:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
> db.auth("root", "SENHA_DE_ROOT");
> rs.initiate()
{
         "info2" : "no configuration explicitly specified -- making one",
         "me" : "node1.db:27017",
         "info" : "Config now saved locally.  Should come online in about a minute.",
         "ok" : 1
}

> rs0:PRIMARY> rs.conf()
{
        "_id" : "rs0",
        "version" : 1,r
        "members" : [
              {
                  "_id" : 0,
                  "host" : "node1.db:27017"
              }
        ]
}

Nos demais nós, suba os containers da seguinte forma:

Servidor 2

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node2" -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Servidor 3

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node3" -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Volte até o servidor 1, acesse o container de mongo e execute os comandos:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
> db.auth("root", "SENHA_DE_ROOT");
> rs0:PRIMARY> rs.add("node2.db") 
> rs0:PRIMARY> rs.add("node3.db") 
> rs0:PRIMARY> rs.status()

O status devem ser algo parecido com este retorno:

rs0:PRIMARY> rs.status()
{
 "set" : "rs0",
 "date" : ISODate("2017-05-16T15:43:30Z"),
 "myState" : 1,
 "members" : [
 {
 "_id" : 0,
 "name" : "node1:27017",
 "health" : 1,
 "state" : 1,
 "stateStr" : "PRIMARY",
 "uptime" : 578713,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "electionTime" : Timestamp(1494371963, 1),
 "electionDate" : ISODate("2017-05-09T23:19:23Z"),
 "self" : true
 },
 {
 "_id" : 1,
 "name" : "node2.db:27017",
 "health" : 1,
 "state" : 2,
 "stateStr" : "SECONDARY",
 "uptime" : 577454,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "lastHeartbeat" : ISODate("2017-05-16T15:43:29Z"),
 "lastHeartbeatRecv" : ISODate("2017-05-16T15:43:29Z"),
 "pingMs" : 0,
 "syncingTo" : "node1:27017"
 },
 {
 "_id" : 2,
 "name" : "node3.db:27017",
 "health" : 1,
 "state" : 2,
 "stateStr" : "SECONDARY",
 "uptime" : 577354,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "lastHeartbeat" : ISODate("2017-05-16T15:43:29Z"),
 "lastHeartbeatRecv" : ISODate("2017-05-16T15:43:29Z"),
 "pingMs" : 0,
 "syncingTo" : "node1:27017"
 }
 ],
 "ok" : 1
}
rs0:PRIMARY>

Caso tenha ocorrido algum erro na sincronização das informações, você poderá visualizar através deste retorno, e claro terá informações para realizar a correção do caso. Existem muitas maneiras de realizar esse tipo de configuração de cluster, algumas mais complexas e outras mais simples, em nosso teste esta foi a forma onde tivemos mais objetividade e assertividade na configuração, baseado nessas informações você pode montar/adaptar os passos que atendem a sua demanda.

Um ponto muito importante nesse ambiente é a forma como ocorre a recuperação em caso de desastre, pois você pode parar o master (primário) e você poderá ver através do rs.status(); o comportamento do cluster elegendo um novo master, ou seja, dessa forma seu cluster ficará quase que 100% a prova de falhas ;).

Esperamos ter ajudado, e se tiver alguma dificuldade ou dúvida com relação a este ambiente por favor entre em contato conosco para que possamos ajudar o/. E como sempre, nos ajude divulgando o blog, isso é muito importante para nós.

Grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.
  • Henrique

    Muito bom, estava procurando algo parecido, mas meu objetivo é fazer algo com Hadoop.

    Aproveitando, ao invés de você usar seu primeiro nó como imagem do docker ele poderia o seu banco de dados local? MongoLocal -> MongoNo1 e MongoNo2, entendeu?

    Aliás existe alguma comunidade/fórum/grupo docker BR? precisando tirar uma dúvidas sobre cluster com docker =/
    Valeu amigo, obrigado elo post!

  • Oi @henriquesantana:disqus,
    É possível utilizar dessa forma sim, sem problema, você só precisa que todos se comuniquem.
    Sobre o grupo, existe um no telegram: https://t.me/dockerbr

    Que dúvidas você tem com relação a cluster?