Opa!
O artigo de hoje será mais prático, e quero 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 conhece, MongoDB é um tipo de banco de dados orientado a documentos ou seja, utiliza o conceito de dados e documentos autocontidos e autodescritivos, 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 contêineres Docker como backend. 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
Veja 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 artigo, veremos como utilizar essa abordagem. Não entrarei 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 réplicas, 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, é necessário 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 às configurações agora:
$ docker exec -it mongo /bin/bash root@node1:/# 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 root@node1:/# exit
Remova o contêiner 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 contêiner para inicializar o cluster:
$ docker exec -it mongo /bin/bash root@node1:/# 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 contêineres 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 contêiner de mongo e execute os comandos:
$ docker exec -it mongo /bin/bash root@node1:/# 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 deve 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/.
Grande abraço!