Alisson Machado
16 April 2017

MongoDB Sharding

No MongoDB Sharding é uma forma de distribuir dados através de múltiplos servidores com o objetivo de ter um grande cluster de dados.


O Recurso de Sharding é utilizando quando se trabalha com grandes datasets, essa semana por exemplo vou precisar configurar um cluster de mongodb de com 3 máquinas que vão guardar 3 terabytes de dados, sendo assim neste caso é um Pré-Requisito criar um cluster com sharding e replicação.


Mas por que usar os dois recursos? por que a replicação vai garantir a redundancia dos seus dados e o sharding vai permitir que você aumente o tamanho do seu dataset, pois pode ser que no futuro 3 terabytes não sejam mais suficientes para armazenar todos os dados, sendo assim é só adicionar mais um servidor no sharding e o tamanho total de armazenamento irá aumentar.


Abaixo uma imagem da arquitetura do cluster:


Explicando essa arquitetura. No desenho é mostrado 1 servidor rodando um serviço chamado MongoService, esse serviço é o responsável por balancear essa carga entre os 2 servidores atrás dele que estarão replicados.


Em uma arquitetura ideal é interessante ter pelo menos 2 servidores com o MongoService para garantir uma alta disponibilidade e um proxy-loadbalancer na frente, nessa arquitetura só foi colocado um pois o cliente disponibilizou somente 3 máquinas.


Atrás desse servidor com o MongoService temos 2 servidores e em cada um deles será executado 2 serviços do MongoDB de forma diferente, sendo um deles para armazenar os dados, que chamados de DataServer e o outro será o ConfigServer que irá armazenar os metadados do cluster. Uma arquitetura minima viável para criar um cluster de sharding são pelo menos 6 máquinas, abaixo segue uma imagem da documentação oficial do mongodb.



Na imagem acima são:


  • 2 servidores MongoService -
  • 2 ConfigServer em Replica -
  • 2 Servidores em Shard em replica


Bom, agora chega de imagens e arquitetura e vamos mão na massa,

vim /etc/hosts

192.168.0.108 mongoservice
192.168.0.106 mongodb1
192.168.0.107 mongodb2

Acima são os apontamentos do meu arquivo /etc/hosts, fiz esse apontamento pois toda a configuração vou fazer através de nomes. Lembrando que esse arquivo deve ser copiado para todas as máquinas para que elas se conversem através de nomes. Para fazer a instalação do mongodb é necessário configurar o repositório e fazer a instalação dos pacotes nas 3 máquinas conforme as instruções abaixo: Configurando os repositórios.

vim  /etc/yum.repos.d/mongodb-org-3.4.repo

# conteudo abaixo foi adicionado no arquivo acima
[mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

Agora a instalação.

yum -y install mongodb-org


Criando o Sharding e o ConfigServer. Primeiro acesse o servidor mongodb1 e rode os seguintes comandos:

mkdir -p /data/{db,configdb}

mongod --replSet "rs0" --shardsvr --port 27017 &

mongod --configsvr --replSet configReplSet &

Sendo executados os comandos acima, devem existir 2 serviços rodando na máquina, um na porta 27017 e outro na porta 27019. Para validar execute o comando abaixo:

[root@localhost system]# ss -ntpl
State      Recv-Q Send-Q                                       Local Address:Port                                                      Peer Address:Port
LISTEN     0      128                                                      *:27019                                                                *:*                   users:(("mongod",pid=11751,fd=6))
LISTEN     0      128                                                      *:22                                                                   *:*                   users:(("sshd",pid=1147,fd=3))
LISTEN     0      100                                              127.0.0.1:25                                                                   *:*                   users:(("master",pid=1947,fd=13))
LISTEN     0      128                                                      *:27017                                                                *:*                   users:(("mongod",pid=11685,fd=6))
LISTEN     0      128                                                     :::22                                                                  :::*                   users:(("sshd",pid=1147,fd=4))
LISTEN     0      100                                                    ::1:25                                                                  :::*                   users:(("master",pid=1947,fd=14))

Veja que os serviços que eu citei acima estão sendo mostrados na saida do comando ss -ntpl. Agora faça a mesma coisa no mongodb2. Criando a replica dos dados. Para criar a replica dos dados em qualquer um dos servidores você pode digitar as instruções abaixo:

[root@localhost ~]# mongo
MongoDB shell version v3.4.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.3
Server has startup warnings:
2017-04-16T23:19:10.154-0400 I CONTROL  [initandlisten]
2017-04-16T23:19:10.154-0400 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-04-16T23:19:10.154-0400 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-04-16T23:19:10.154-0400 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-04-16T23:19:10.154-0400 I CONTROL  [initandlisten]
> rs.initiate( {
   _id : "rs0",
   members: [ { _id : 0, host : "mongodb1:27017" } ]
})


Com os comandos acima o próprio servidor foi adicionado ao replicaset rs0 criado anteriormente, agora para adicionar o segundo servidor execute as linhas abaixo:

[root@localhost ~]# mongo
MongoDB shell version v3.4.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.3
> rs0:PRIMARY> rs.add("mongodb2")
{ "ok" : 1 }


Para validar que eles estão em replica, você pode acessar o console do segundo servidor e validar se ele está aparecendo como SECONDARY:

[root@localhost ~]# mongo
MongoDB shell version v3.4.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.3

rs0:SECONDARY>


Na saida do comando acima é possível ver que logo no console já aparece que o servidor faz parte do rs0 e está como SECONDARY. Agora para fazer a replica do configserver, em qualquer um dos dois servidores você pode executar as seguinte instrução dentro do console do mongodb.

[root@localhost ~]# mongo localhost:27019
MongoDB shell version v3.4.3
connecting to: localhost:27019
MongoDB server version: 3.4.3
Server has startup warnings:
2017-04-16T23:15:10.890-0400 I CONTROL  [initandlisten]
2017-04-16T23:15:10.890-0400 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-04-16T23:15:10.890-0400 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-04-16T23:15:10.890-0400 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-04-16T23:15:10.890-0400 I CONTROL  [initandlisten]
>
> rs.initiate( {
...    _id: "configReplSet",
...    configsvr: true,
...    members: [
...       { _id: 0, host: "mongodb1:27019" },
...       { _id: 1, host: "mongodb2:27019" }
...    ]
... } )
{ "ok" : 1 }
configReplSet:OTHER>


No exemplo acima o cliente do mongodb foi executando para acessar a parta 27019 que é a responsável por fazer a replicação do configserver e a replica foi criada nela. Com todas as replicas configuradas é hora de configurar o service. Acesse agora o servidor mongoservice e execute o seguinte comando:

mongos --configdb configReplSet/mongodb1:27019,mongodb2:27019 &


Esse comando irá iniciar um processo na porta 27017.

[root@localhost ~]# ss -ntpl
State      Recv-Q Send-Q                                       Local Address:Port                                                      Peer Address:Port
LISTEN     0      128                                                      *:22                                                                   *:*                   users:(("sshd",pid=1078,fd=3))
LISTEN     0      100                                              127.0.0.1:25                                                                   *:*                   users:(("master",pid=1546,fd=13))
LISTEN     0      128                                                      *:27017                                                                *:*                   users:(("mongos",pid=11497,fd=4))
LISTEN     0      128                                                     :::22                                                                  :::*                   users:(("sshd",pid=1078,fd=4))
LISTEN     0      100                                                    ::1:25                                                                  :::*                   users:(("master",pid=1546,fd=14))


A saida do comando acima mostra isso. O mongoservice será o ponto de acesso para as replicas configuradas no começo do artigo, agora para acessa-lo será da seguinte maneira:

[root@localhost ~]# mongo mongoservice/admin
Welcome to the MongoDB shell.
mongos>


Note que agora o console está diferente e ao invés de ficar somente o sinal de maior ( > ), fica aparecendo mongos>, pois estamos acessando o service e não a base diretamente. Agora para adicionar o nosso cluster de shard execute a linha abaixo:

> sh.addShard( "rs0/mongodb1:27017,mongodb2:27017" )
2017-04-16T23:45:03.496-0400 I NETWORK  [conn2] Starting new replica set monitor for rs0/mongodb1:27017,mongodb2:27017
{ "shardAdded" : "rs0", "ok" : 1 }


A saida do comando acima mostra que o shard foi adicionado com sucesso. No MongoService você pode adicionar quantos clusters de shard você precisar. Agora para criar um banco de dados dentro do seu cluster é necessário executar a instrução abaixo:

mongos> sh.enableSharding("alisson")
2017-04-16T23:46:51.918-0400 I SHARDING [conn2] distributed lock 'alisson' acquired for 'enableSharding', ts : 58f43aab9202a5538a1cb23c
2017-04-16T23:46:51.919-0400 I ASIO     [NetworkInterfaceASIO-ShardRegistry-0] Connecting to mongodb1:27017
2017-04-16T23:46:51.920-0400 I ASIO     [NetworkInterfaceASIO-ShardRegistry-0] Successfully connected to mongodb1:27017
2017-04-16T23:46:51.921-0400 I SHARDING [conn2] Placing [alisson] on: rs0
2017-04-16T23:46:51.921-0400 I SHARDING [conn2] Enabling sharding for database [alisson] in config db
2017-04-16T23:46:51.979-0400 I SHARDING [conn2] distributed lock with ts: 58f43aab9202a5538a1cb23c' unlocked.
{ "ok" : 1 }


Agora você pode acessar o banco de de dados e criar um documento e fazer uma busca para ver se já funciona:

mongos> use alisson;
switched to db alisson
mongos> db.documentos.insert({"nome":"teste"})
WriteResult({ "nInserted" : 1 })
mongos> db.documentos.find()
2017-04-16T23:48:19.957-0400 I ASIO     [NetworkInterfaceASIO-TaskExecutorPool-0-0] Connecting to mongodb1:27017
2017-04-16T23:48:19.958-0400 I ASIO     [NetworkInterfaceASIO-TaskExecutorPool-0-0] Successfully connected to mongodb1:27017
{ "_id" : ObjectId("58f43afd058b0dbab981298e"), "nome" : "teste" }
mongos> show dbs;
admin    0.000GB
alisson  0.000GB
config   0.000GB
mongos> show collections;
documentos


Note que ao fazer o find do documento é mostrado no log que o dado é buscado dentro do servidor mongodb1. Agora eu reiniciei o servidor primary e fiz a mesma busca, note que agora ele busca no segundo servidor:

mongos> db.documentos.find()
2017-04-17T00:02:22.177-0400 I ASIO     [NetworkInterfaceASIO-TaskExecutorPool-0-0] Connecting to mongodb2:27017
2017-04-17T00:02:22.179-0400 I ASIO     [NetworkInterfaceASIO-TaskExecutorPool-0-0] Successfully connected to mongodb2:27017
{ "_id" : ObjectId("58f43afd058b0dbab981298e"), "nome" : "teste" }


Agora para finalizar é importante adicionar autenticação ao mongodb, pois por padrão o usuário é o anonimo. Para fazer isso é só executar os comandos abaixo:

use admin
db.createUser(
  {
    user: "alisson",
    pwd: "alisson123",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)