mongo 副本集上的 ServerSelectionTimeoutError 使用 pymongo

ServerSelectionTimeoutError on mongo replica set using pymongo

我正在尝试使用 pymongo 连接到 MongoDB 副本集,但我不断收到错误消息:pymongo.errors.ServerSelectionTimeoutError: No replica set members match selector。在错误消息中,它还指定我的拓扑类型是 ReplicaSetNoPrimary,这很奇怪,因为与 mongo bash 的连接显示了一个清晰的主节点。

请注意,副本集工作正常并且可以通过 mongo bash 在主节点上使用。 此外,我还添加了防火墙规则以允许指定端口上的入站和出站流量,只是为了确保这不是问题所在。 我正在为集群使用 docker-compose。文件:

version: "3.9"
services:
  mongo-master:
    image: mongo:latest
    container_name: mongo_master
    volumes:
      - ./data/master:/data/db
    ports:
      - 27017:27017
    command: mongod --replSet dbrs & mongo --eval rs.initiate(`cat rs_config.json`)
    stdin_open: true
    tty: true

  mongo-slave-1:
    image: mongo:latest
    container_name: mongo_slave_1
    volumes:
      - ./data/slave_1:/data/db
    ports:
      - 27018:27017
    command: mongod --replSet dbrs
    stdin_open: true
    tty: true

  mongo-slave-2:
    image: mongo:latest
    container_name: mongo_slave_2
    volumes:
      - ./data/slave_2:/data/db
    ports:
      - 27019:27017
    command: mongod --replSet dbrs
    stdin_open: true
    tty: true

上面使用的rs_config.json文件:

{
    "_id" : "dbrs",
    "members" : [
        {
            "_id" : 0,
            "host" : "mongo_master:27017",
            "priority" : 10
        },
        {
            "_id" : 1,
            "host" : "mongo_slave_1:27017"
        },
        {
            "_id" : 2,
            "host" : "mongo_slave_2:27017"
        }
    ]
}

此处最后一行出现错误:

self.__client = MongoClient(["localhost:27017", "localhost:27018", "localhost:27019"], replicaset="dbrs")
self.__collection = self.__client[self.__db_name][collection.value]
self.__collection.insert_one(dictionary_object)

为了简洁起见,我省略了一些代码,但您可以假设所有 class 属性和 dictionary_object 都根据 pymongo 文档进行了明确定义。 另请注意,我尝试了许多不同的方法来初始化 MongoClient,包括连接字符串(如文档中所示)和某些博客中建议的 connect=False 可选参数。问题仍然存在...

编辑:我尝试将“mongo_master”添加到指向 127.0.0.1 的 etc/hosts 文件中,并将连接字符串从 localhost 更改为那个,它适用于副本集。这是一个糟糕的解决方法,但也许可以帮助找出解决方案。

在此先感谢您的帮助!

要从外部客户端连接到 MongoDB 副本集,您必须能够从本地客户端解析主机名。

https://docs.mongodb.com/manual/tutorial/deploy-replica-set/#connectivity

Ensure that network traffic can pass securely between all members of the set and all clients in the network.

因此,将以下内容添加到您的 /etc/hosts 文件中:

127.0.0.1 mongodb-1
127.0.0.1 mongodb-2
127.0.0.1 mongodb-3

为了能够同时连接内部和外部,您将需要 运行 每个 MongoDB 服务在不同的端口上。

以下脚本将启动 3 节点 MongoDB 副本集和 运行 测试客户端。我建议使用 Bitnami 图像,因为它会为您处理 replset 启动。 (大量借鉴 this configuration

#!/bin/bash

PROJECT_NAME=replset_test

MONGODB_VERSION=4.4
PYTHON_VERSION=3.9.6
PYMONGO_VERSION=4.0.1

cd "$(mktemp -d)" || exit

cat << EOF > Dockerfile
FROM python:${PYTHON_VERSION}-slim-buster
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY ${PROJECT_NAME}.py .
CMD [ "python", "./${PROJECT_NAME}.py" ]
EOF

cat << EOF > requirements.txt
pymongo==${PYMONGO_VERSION}
EOF

cat << EOF > ${PROJECT_NAME}.py
from pymongo import MongoClient

connection_string = 'mongodb://root:password123@mongodb-1:27017,mongodb-2:27018,mongodb-3:27019/mydatabase?authSource=admin&replicaSet=replicaset'
client = MongoClient(connection_string)
db = client.db
db['mycollection'].insert_one({'a': 1})
record = db['mycollection'].find_one()
if record is not None:
    print(f'{__file__}: MongoDB connection working using connection string "{connection_string}"')
EOF

cp ${PROJECT_NAME}.py ${PROJECT_NAME}_external.py

cat << EOF > docker-compose.yaml
version: '3.9'

services:
  mongodb-1:
    image: docker.io/bitnami/mongodb:${MONGODB_VERSION}
    ports:
      - 27017:27017
    environment:
      - MONGODB_ADVERTISED_HOSTNAME=mongodb-1
      - MONGODB_PORT_NUMBER=27017
      - MONGODB_REPLICA_SET_MODE=primary
      - MONGODB_ROOT_PASSWORD=password123
      - MONGODB_REPLICA_SET_KEY=replicasetkey123
    volumes:
      - 'mongodb_master_data:/bitnami/mongodb'

  mongodb-2:
    image: docker.io/bitnami/mongodb:${MONGODB_VERSION}
    ports:
      - 27018:27018
    depends_on:
      - mongodb-1
    environment:
      - MONGODB_ADVERTISED_HOSTNAME=mongodb-2
      - MONGODB_PORT_NUMBER=27018
      - MONGODB_REPLICA_SET_MODE=secondary
      - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary
      - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=password123
      - MONGODB_REPLICA_SET_KEY=replicasetkey123

  mongodb-3:
    image: docker.io/bitnami/mongodb:${MONGODB_VERSION}
    ports:
      - 27019:27019
    depends_on:
      - mongodb-1
    environment:
      - MONGODB_ADVERTISED_HOSTNAME=mongodb-3
      - MONGODB_PORT_NUMBER=27019
      - MONGODB_REPLICA_SET_MODE=secondary
      - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary
      - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=password123
      - MONGODB_REPLICA_SET_KEY=replicasetkey123

  ${PROJECT_NAME}:
    container_name: ${PROJECT_NAME}
    build: .
    depends_on:
      - mongodb-1
      - mongodb-2
      - mongodb-3

volumes:
  mongodb_master_data:
    driver: local
EOF

docker rm --force $(docker ps -a -q --filter name=mongo) 2>&1 > /dev/null
docker rm --force $(docker ps -a -q --filter name=${PROJECT_NAME}) 2>&1 > /dev/null
docker-compose up --build -d
python ${PROJECT_NAME}.py
docker ps -a -q --filter name=${PROJECT_NAME}
docker logs $(docker ps -a -q --filter name=${PROJECT_NAME})

如果一切正常,您将获得确认内部和外部连接的输出:

/tmp/tmp.QM9tQPE8Dj/replset_test.py: MongoDB connection working using connection string "mongodb://root:password123@mongodb-1:27017,mongodb-2:27018,mongodb-3:27019/mydatabase?authSource=admin&replicaSet=replicaset"
d53e8c41ad20
//./replset_test.py: MongoDB connection working using connection string "mongodb://root:password123@mongodb-1:27017,mongodb-2:27018,mongodb-3:27019/mydatabase?authSource=admin&replicaSet=replicaset"