Dada a popularidade de nossa postagem sobre a conexão do MongoDB SSL com certificados autoassinados no Node.js, decidimos escrever um tutorial sobre como conectar o MongoDB com o Ruby. Neste blog, mostraremos como se conectar a um servidor MongoDB configurado com certificados autoassinados para SSL usando o driver Ruby MongoDB e o popular Object-Document-Mapper (ODM) mongoid.
O ScaleGrid atualmente usa certificados autoassinados para SSL ao criar nós para um novo cluster. Além disso, também oferecemos a opção de adquirir seus próprios certificados SSL e configurá-los no servidor MongoDB, e você pode enviar um e-mail para [email protected] para saber mais sobre esta oferta.
Conectando-se a um conjunto de réplicas usando o driver Ruby MongoDB
Nós usaremos a versão mais recente do driver Ruby MongoDB estável, versão 2.8, para este exemplo. As versões 2.5.x do driver têm um bug conhecido que as impede de trabalhar com implantações do ScaleGrid. A versão Ruby usada em ambos os exemplos abaixo é 2.6.3.
As opções de conexão disponíveis para o driver estão documentadas aqui, e as opções que precisaremos são:
- :ssl
- :ssl_verify
- :ssl_ca_cert .
Primeiro, localize e copie sua string de conexão do MongoDB da página de detalhes do cluster no console do ScaleGrid:
O arquivo de certificado de CA também está disponível para download na página de detalhes do cluster. Baixe e armazene o arquivo cert em um local que esteja disponível para o aplicativo:
Aqui está um trecho mostrando como se conectar a um conjunto de réplicas MongoDB do Ruby:
requer 'mongo'Mongo::Logger.logger.level =::Logger::DEBUGMONGODB_CA_CERT ="/path/to/ca_cert.pem"MONGODB_CONN_URL ="mongodb://testuser:@SG-example- 17026.servers.mongodirector.com:27017,SG-example-17027.servers.mongodirector.com:27017,SG-example-17028.servers.mongodirector.com:27017/test?replicaSet=RS-example-0&ssl=true" options ={ ssl:true, ssl_verify:true, :ssl_ca_cert => MONGODB_CA_CERT }client =Mongo::Client.new(MONGODB_CONN_URL, options)db =client.databasecollections =db.collection_namesputs "db #{db.name} tem coleções # {collections}"client.close
Para manter o exemplo simples, especificamos a string de conexão e o caminho do arquivo cert diretamente no trecho de código – você geralmente os colocaria em um arquivo yaml ou os especificaria como variáveis ambientais. Além disso, o exemplo define o nível de log para DEBUG
para que quaisquer problemas de conectividade possam ser depurados. Ele deve ser alterado para um nível menos detalhado assim que os problemas de conectividade forem resolvidos.
Como conectar o MongoDB a um aplicativo Ruby com SSLClick To Tweet
Conectando usando Mongoid
A versão mongoide que usaremos em nosso exemplo é a versão estável mais recente – 7.0.2. Usaremos um arquivo yaml para fornecer configuração ao mongoid, e os detalhes desse arquivo de configuração estão documentados aqui. As opções de configuração específicas de SSL que precisaremos para conectar ao nosso conjunto de réplicas são:
- ssl
- ssl_verify
- ssl_ca_cert
Nosso arquivo yml:
desenvolvimento:# Configura clientes de banco de dados disponíveis. (obrigatório) clientes:# Defina o cliente padrão. (obrigatório) padrão:# Um uri pode ser definido para um cliente:# uri:'mongodb://user:[email protected]:27017/my_db' # Consulte a documentação do driver para obter detalhes. Como alternativa, você pode definir o seguinte:# # Defina o nome do banco de dados padrão ao qual o Mongoid pode se conectar. # (requerido). database:test # Forneça os hosts aos quais o cliente padrão pode se conectar. Deve ser um array # de pares host:port. (obrigatório) hosts:- SG-example-17026.servers.mongodirector.com:27017 - SG-example-17027.servers.mongodirector.com:27017 - SG-example-17028.servers.mongodirector.com:47100 options:# O nome do usuário para autenticação. usuário:'usuário de teste' # A senha do usuário para autenticação. senha:'pwd' # As funções de banco de dados do usuário. roles:- 'readWrite' # Muda o mecanismo de autenticação padrão. As opções válidas são::scram, # :mongodb_cr, :mongodb_x509 e :plain. (o padrão em 3.0 é :scram, o padrão # em 2.4 e 2.6 é :plain) auth_mech::scram # O banco de dados ou fonte para autenticar o usuário. (padrão:admin) auth_source:teste # Força o driver a se conectar de uma maneira específica em vez de auto- # descoberta. Pode ser um dos seguintes::direct, :replica_set, :sharded. Defina como :direct # ao conectar-se a membros ocultos de um conjunto de réplicas. connect::replica_set ... ... # O nome do conjunto de réplicas ao qual se conectar. Os servidores fornecidos como sementes que # não pertencem a este conjunto de réplicas serão ignorados. replica_set:RS-example-0 # Se conectar aos servidores via SSL. (padrão:falso) ssl:verdadeiro # Se deve ou não fazer a validação de certificação por pares. (padrão:verdadeiro) ssl_verify:verdadeiro # O arquivo contendo um conjunto de certificações concatenadas de autoridade de certificação # usado para validar certificados passados da outra extremidade da conexão. ssl_ca_cert:/path/to/ca_cert.pem # Configure as opções específicas do Mongoid. opções (opcionais):# Defina os níveis de log do driver Mongoid e Ruby. (padrão::info) log_level::debug
Exemplo de conexão:
gem 'mongoid', '7.0.2'require 'mongoid'Mongoid.load!("/path/to/mongoid.yml", :development)# Não usando nenhum dos recursos do ODM - apenas busque o mongo subjacente cliente e tente conectarcliente =Mongoid::Clients.defaultdb =client.databasecollections =db.collection_namesputs "db #{db.name} tem coleções #{collections}"Mongoid::Clients.disconnect
Novamente, em aplicações Ruby on Rails de produção, o caminho do arquivo yaml seria obtido das variáveis de ambiente.
Testando o comportamento de failover
Como outros drivers MongoDB, o driver Ruby MongoDB também foi projetado para reconhecer internamente mudanças na topologia devido a eventos como failover. No entanto, é bom testar e validar o comportamento do driver durante os failovers para evitar surpresas na produção.
Como meu post anterior no MongoDB PyMongo, podemos escrever um programa de teste de gravador perpétuo para observar o comportamento de failover do driver.
A maneira mais fácil de induzir o failover é executar o comando rs.stepDown():
RS-example-0:PRIMARY> rs.stepDown()2019-04-18T19:44:42.257+0530 E QUERY [thread1] Erro:erro ao fazer consulta:falhou:erro de rede ao tentar executar o comando 'replSetStepDown' no host 'SG-example-1.servers.mongodirector.com:27017' :DB.prototype.runCommand@src/mongo/shell/db.js:168:1DB.prototype.adminCommand@src/mongo/shell/db. js:185:1rs.stepDown@src/mongo/shell/utils.js:1305:12@(shell):1:12019-04-18T19:44:42.261+0530 I NETWORK [thread1] tentando reconectar ao SG-example -1.servers.mongodirector.com:27017 (X.X.X.X) falhou2019-04-18T19:44:43.267+0530 I NETWORK [thread1] reconectar SG-example-1.servers.mongodirector.com:27017 (X.X.X.X) okRS-example- 0:SECUNDARY>
Aqui estão as partes relevantes do nosso código de teste:
requer 'mongo'...logger =Logger.new(STDOUT)logger.level =Logger::INFOMONGODB_CA_CERT ="/path/to/ca_cert.pem"MONGODB_CONN_URL ="mongodb://testuser:@ SG-example-17026.servers.mongodirector.com:27017,SG-example-17027.servers.mongodirector.com:27017,SG-example-17028.servers.mongodirector.com:27017/test?replicaSet=RS-example- 0&ssl=true"options ={ ssl:true, ssl_verify:true, :ssl_ca_cert => MONGODB_CA_CERT }begin logger.info("Tentando conectar...") client =Mongo::Client.new(MONGODB_CONN_URL, options) i =0 loop do db =client.database collection =db[:test] begin doc ={"idx":i, "date":DateTime.now, "text":SecureRandom.base64(3) } result =collection.insert_one( doc) logger.info("Registro inserido - id:#{result.inserted_id}") i +=1 sleep(3) rescue Mongo::Error => e logger.error("Mong Error visto:#{e.message }") logger.error(e.backtrace) logger.i nfo("Retentando...") end end logger.info("Done")rescue => err logger.error("Exception visto:#{err.message}") logger.error(err.backtrace)garantir cliente. fechar a menos que client.nil?end
Isso grava continuamente entradas como estas na coleção de teste no banco de dados de teste:
RS-test-0:PRIMARY> db.test.find(){ "_id":ObjectId("5cf50ff1896cd172a4f7c6ee"), "idx":0, "date":ISODate("2019-06-03T12:17 :53.008Z"), "texto" :"HTvd" }{ "_id" :ObjectId("5cf50ff6896cd172a4f7c6ef"), "idx" :1, "data" :ISODate("2019-06-03T12:17:58.697Z" ), "texto" :"/e5Z" }{ "_id" :ObjectId("5cf50ff9896cd172a4f7c6f0"), "idx" :2, "data" :ISODate("2019-06-03T12:18:01.940Z"), " text" :"quuw" }{ "_id" :ObjectId("5cf50ffd896cd172a4f7c6f1"), "idx" :3, "date" :ISODate("2019-06-03T12:18:05.194Z"), "texto" :" gTyY" }{ "_id" :ObjectId("5cf51000896cd172a4f7c6f2"), "idx" :4, "data" :ISODate("2019-06-03T12:18:08.442Z"), "texto" :"VDXX" }{ "_id" :ObjectId("5cf51003896cd172a4f7c6f3"), "idx" :5, "data" :ISODate("2019-06-03T12:18:11.691Z"), "texto" :"UY87" }...Vamos ver o comportamento durante um failover:
I, [2019-06-03T17:53:25.079829 #9464] INFO -- :Tentando conectar...I, [2019-06-03T17:53:30.577099 #9464] INFO -- :Registro inserido - id:5cf5113f896cd124f8f31062I, [2019-06-03T17:53:33.816528 #9464] INFO -- :Registro inserido - id:5cf51145896cd124f8f31063I, [2019-06-03T17:53:37.0470] INFO #964:53:37.0470:43 5cf51148896cd124f8f31064I, [2019-06-03T17:53:40.281537 #9464] INFO -- :Record inserted - id:5cf5114c896cd124f8f31065I, [2019-06-03T17:53:43.520010 #9464] INFO -- :Record inserted - id:5cf5114f896cd124f8f31066I, [2019-06-03T17:53:46.747080 #9464] INFO -- :Registro inserido - id:5cf51152896cd124f8f31067I, [2019-06-03T17:53:49.978077 #9464] INFO -- :Registro inserido - id:50cf51155896cd Failover iniciado aquiE, [2019-06-03T17:53:52.980434 #9464] ERRO -- :Erro de Mong visto:EOFError:fim do arquivo atingido (para x.x.x.x:27017 (sg-example-17026.servers.mongodirector.com:27017 , TLS))E, [2019-06-03T17:53:52.980533 #9464] ERRO -- :["C:/Ruby 26-x64/lib/ruby/gems/2.6.0/gems/mongo-2.8.0/lib/mongo/socket.rb:300:in `rescue in handle_errors'", "C:/Ruby26-x64/lib/ ruby/gems/2.6.0/gems/mongo-2.8.0/lib/mongo/socket.rb:294:in `handle_errors'", "C:/Ruby26-x64/lib/ruby/gems/2.6.0/ gems/mongo-2.8.0/lib/mongo/socket.rb:126:in `read'", "C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/mongo-2.8.0/ lib/mongo/protocol/message.rb:139:in `desserialize'",......I, [2019-06-03T17:53:52.980688 #9464] INFO -- :Retrying...W, [ 2019-06-03T17:53:52.981575 #9464] WARN -- :Tentando novamente o ismaster em sg-example-17026.servers.mongodirector.com:27017 devido a:Mongo::Error::SocketError EOFError:fim do arquivo atingido (para x.x.x.x:27017 (sg-example-17026.servers.mongodirector.com:27017, TLS))I, [2019-06-03T17:54:06.293100 #9464] INFO -- :Registro inserido - id:5cf51163896cd124f8f3106aI, [2019- 06-03T17:54:09.547716 #9464] INFO -- :Registro inserido - id:5cf51169896cd124f8f3106bI, [2019-06-03T17:54:12.806636 #9464] INFO -- :Registro inserido - id:5cf51É evidente que, se os erros corretos forem detectados e a leitura/gravação for repetida, o driver detectará automaticamente a alteração da topologia e se reconectará ao novo mestre. Para gravações, a opção :retry_writes certifique-se de que o driver tentará novamente uma vez por conta própria antes de notificar o aplicativo sobre um erro.
Há também vários tempos limite de driver que podem ser ajustados com base no comportamento exato e na latência que você vê em sua configuração. Estes estão documentados aqui.
Solução de problemas
Se você estiver tendo problemas para se conectar à sua implantação do MongoDB habilitada para SSL, aqui estão algumas dicas para depuração:
- Primeiro, verifique se você pode realmente se conectar ao servidor MongoDB a partir do servidor em que seu aplicativo está sendo executado. A maneira mais simples de fazer isso é instalar o shell mongo na máquina cliente. No Linux, você não precisaria instalar todo o servidor MongoDB – você pode optar por instalar apenas o shell separadamente. Quando o shell estiver disponível, tente usar a "Sintaxe da linha de comando" que fornecemos para tentar se conectar ao servidor.
- Se você não conseguir se conectar através do shell mongo, isso significa que a máquina cliente não consegue alcançar a porta 27017 dos servidores MongoDB. Observe as configurações de firewall do Security Group, VPC e ScaleGrid para garantir que haja conectividade entre as máquinas cliente e servidor.
- Se a conectividade de rede estiver correta, a próxima coisa a verificar é se você está usando versões de Ruby, mongoid e mongo gem compatíveis com a versão do seu servidor MongoDB.
- Se você confirmou que as versões do driver estão corretas, tente executar um script Ruby de amostra, semelhante ao exemplo fornecido acima, no IRB. Uma execução passo a passo pode apontar onde está o problema.
- Se o script de teste funcionar bem, mas você ainda não conseguir se conectar ao mongoid, tente executar um script de teste simples, como o exemplo que fornecemos acima .
- Se você ainda tiver problemas para se conectar à sua instância, escreva para [email protected] com os resultados detalhados das etapas de solução de problemas acima e com as versões exatas do driver Ruby, mongoid e mongo que você está usando. O Gemfile.lock fornecerá as versões exatas.
Se você é novo no ScaleGrid e deseja experimentar este tutorial, inscreva-se para uma avaliação gratuita de 30 dias para explorar a plataforma e testar conectando o MongoDB ao seu aplicativo Ruby.