Armadilhas do Replica Set do MongoDB com Podman (Compose)

Autor
Damian
Terlecki
6 minutos de leitura
Bancos de Dados

Você pode encontrar facilmente na internet configurações funcionais de docker/docker-compose para configurar um replica set do MongoDB. No entanto, nenhuma funcionou quando precisei configurar uma conexão a partir de uma máquina host Linux com o podman-compose. Para delinear o problema, digamos que um replica set é geralmente iniciado através de um shell após conectar-se a uma instância mongod rodando com --replSet e --bind_ip (opcionalmente --port):

rs.initiate(
   {
      _id: "myReplSet",
      version: 1,
      members: [
         { _id: 0, host : "mongodb0.example.net:27017" },
         { _id: 1, host : "mongodb1.example.net:27017" },
         { _id: 2, host : "mongodb2.example.net:27017" }
      ]
   }
)

Um hostname para governar todos

Os membros host definidos na configuração devem ser acessíveis tanto de cada nó quanto do lado do cliente. Muitas vezes, você encontrará host.docker.internal sendo usado no lugar de mongodbN.example.net. Em uma rede bridge padrão, ele é mapeado para o host-gateway da rede docker usando a propriedade extra_hosts do serviço. Ao usar o Podman, existe um equivalente host.containers.internal que deve funcionar sem definir os extra_hosts.

Nota: se você se deparar com um 'invalid IP address in add-host: "host-gateway"', você pode estar rodando uma versão antiga do docker engine/podman no Linux. Nos casos mais básicos, você pode trocá-lo por 172.17.0.1, mas isso pode variar ao rodar com múltiplas redes. Confira este tópico do SO para encontrar soluções melhores, mas mais complexas.

Alternativamente, você pode usar o nome do serviço ou configurar container_name e hostname do serviço para configurar um RS com hostnames personalizados.

Vínculos de porta

A segunda armadilha é a configuração das portas. Uma única rede bridge significa que as portas para cada nó devem ser diferentes (ex: 27017, 27018, 27019) e corresponder à definição dos membros, à configuração do mongod e aos vínculos de porta do host.

Incompatibilidades podem levar a:

  • remoção de um nó da visão do cluster pelo cliente (o endereço canônico do nó não corresponde ao endereço do servidor);
  • problemas para acessar todos os nós da máquina host;
  • erros de iniciação do RS ao tentar encontrar a si mesmo na configuração do replset.

UnknownHostException

A terceira armadilha é que host.docker.internal não é resolvível em hosts Linux e requer a adição de uma entrada no /etc/hosts que mapeia para o endereço de loopback padrão 127.0.0.1. No entanto, com o Podman, os /etc/hosts do host são incluídos nos /etc/hosts dos contêineres, causando erros de iniciação do RS.

Captura de tela da entrada 'host.docker.internal' duplicada do host da máquina no '/etc/hosts' do contêiner. Captura de tela de um erro de replicação devido à duplicação de 'host.docker.internal' no '/etc/hosts', resultando no IP errado.

A opção --no-hosts foi disponibilizada recentemente (Podman 4.0), mas entra em conflito com host.containers.internal/host.docker.internal. Uma solução grosseira que funciona é atualizar o /etc/hosts do contêiner antes da inicialização do mongod. No entanto, é muito melhor desabilitar esse comportamento globalmente também através da mais recente propriedade de configuração (Podman 4.1) em $HOME/.config/containers/containers.conf:

[containers]
base_hosts_file="none"

Resumo

Se você encontrar problemas ao configurar um replica set do MongoDB dockerizado para desenvolvimento, especialmente no Linux ou usando Podman, revise o seguinte:

  • Replica sets devem ser configurados com combinações de hostnames e portas alcançáveis de cada nó, bem como do cliente (ex: máquina host);
  • Use portas distintas para cada nó para que a visão do RS corresponda ao vínculo de porta em uma rede bridge;
  • Use host.docker.internal (pode exigir extra_hosts)/host.containers.internal (Podman) para a propriedade host dos membros;
  • Alternativamente, use container_name e hostname do serviço se precisar configurar um RS com hostnames personalizados;
  • Em caso de erro de hostname inalcançável na máquina host, adicione os hostnames do RS ao /etc/hosts do host;
  • Desabilite a cópia do /etc/hosts da máquina host através do arquivo containers.conf para o Podman;
  • Como último recurso no Linux, use a propriedade do serviço network_mode: host e conecte-se ao RS no localhost imediatamente.