MongoDB docker 副本集连接错误 "Host not found"
MongoDB docker replica set connection error "Host not found"
我在 .
之后创建了一个本地 MongoDB 副本集
docker-撰写文件:
services:
mongo1:
container_name: mongo1
image: mongo:4.2
ports:
- 27017:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
mongo2:
container_name: mongo2
image: mongo:4.2
ports:
- 27018:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
mongo3:
container_name: mongo3
image: mongo:4.2
ports:
- 27019:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
replica_set:
image: mongo:4.2
container_name: replica_set
depends_on:
- mongo1
- mongo2
- mongo3
volume:
- ./initiate_replica_set.sh:/initiate_replica_set.sh
entrypoint:
- /initiate_replica_set.sh
initiate_replica_set.sh 文件:
#!/bin/bash
echo "Starting replica set initialize"
until mongo --host mongo1 --eval "print(\"waited for connection\")"
do
sleep 2
done
echo "Connection finished"
echo "Creating replica set"
mongo --host mongo1 <<EOF
rs.initiate(
{
_id : 'rs0',
members: [
{ _id : 0, host : "mongo1:27017" },
{ _id : 1, host : "mongo2:27017" },
{ _id : 2, host : "mongo3:27017" }
]
}
)
EOF
echo "replica set created"
副本集启动成功并运行良好,但当我尝试连接到副本集时出错:
$ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs"
MongoDB shell version v5.0.2
connecting to: mongodb://localhost:27017,localhost:27018,localhost:27019/?compressors=disabled&gssapiServiceName=mongodb&replicaSet=rs
{"t":{"$date":"2021-08-05T21:35:40.667Z"},"s":"I", "c":"NETWORK", "id":4333208, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"RSM host selection timeout","attr":{"replicaSet":"rs","error":"FailedToSatisfyReadPreference: Could not find host matching read preference { mode: \"nearest\" } for set rs"}}
Error: Could not find host matching read preference { mode: "nearest" } for set rs, rs/localhost:27017,localhost:27018,localhost:27019 :
connect@src/mongo/shell/mongo.js:372:17
@(connect):2:6
exception: connect failed
exiting with code 1
更详细的日志:
{
"t": {
"$date": "2021-08-05T21:35:54.531Z"
},
"s": "I",
"c": "-",
"id": 4333222,
"ctx": "ReplicaSetMonitor-TaskExecutor",
"msg": "RSM received error response",
"attr": {
"host": "mongo1:27017",
"error": "HostUnreachable: Error connecting to mongo1:27017 :: caused by :: Could not find address for mongo1:27017: SocketException: Host not found (authoritative)",
"replicaSet": "rs",
"response": "{}"
}
}
问题的原因是什么,我该如何解决?
这个问题在各个地方有一些不完整的答案,这里是我认为的完整答案。
原因
Mongo clients use the hostnames listed in the replica set config, not the seed list
虽然连接字符串是"mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs"
,mongo客户端并没有连接到种子地址为localhost:27017
等的副本集成员,而是连接到从种子主机返回的副本配置集,即 rs.initiate
调用中的副本配置集。这就是错误消息是 Error connecting to mongo1:27017
而不是 Error connecting to localhost:27017
.
的原因
容器主机名在容器网络外不可寻址
与 mongo 服务器容器位于同一容器网络中的 mongo 客户端可以通过 mongo1:27017
这样的地址连接到服务器;但是,位于容器网络外部的主机上的客户端无法将 mongo1
解析为 IP。这个问题的典型解决方案是代理,详见
修复
因为问题涉及 docker 网络和 docker 网络在 Linux 和 Mac 之间变化。两个平台上的修复不同。
Linux
代理修复(通过第 3 方软件或修改 /etc/hosts
文件)工作正常但有时不可行,例如 运行 在远程 CI 主机上。一个简单的自包含便携式解决方案是更新 intiate_replia_set.sh
脚本以使用成员 IP 而不是主机名启动副本集。
intiate_replia_set.sh
echo "Starting replica set initialization"
until mongo --host mongo1 --eval "print(\"waited for connection\")"
do
sleep 2
done
echo "Connection finished"
echo "Creating replica set"
MONGO1IP=$(getent hosts mongo1 | awk '{ print }')
MONGO2IP=$(getent hosts mongo2 | awk '{ print }')
MONGO3IP=$(getent hosts mongo3 | awk '{ print }')
read -r -d '' CMD <<EOF
rs.initiate(
{
_id : 'rs',
members: [
{ _id : 0, host : '${MONGO1IP}:27017' },
{ _id : 1, host : '${MONGO2IP}:27017' },
{ _id : 2, host : '${MONGO3IP}:27017' }
]
}
)
EOF
echo $CMD | mongo --host mongo1
echo "replica set created"
这样 mongo 副本集成员的地址中就有容器 IP 而不是主机名。容器 IP 可从主机访问。
或者,我们可以在 docker-compose 文件中为每个容器显式分配静态 IP,并在启动副本集时使用静态 IP。这是一个类似的修复,但需要更多工作。
Mac
不幸的是,上述解决方案不适用于 Mac,因为 Mac 上的 docker 容器 IP 未暴露在主机网络接口上。 https://docs.docker.com/docker-for-mac/networking/#per-container-ip-addressing-is-not-possible
使其工作的最简单方法是在 /etc/hosts
文件中添加以下映射:
127.0.0.1 mongo1 mongo2 mongo3
我认为将副本集“rs”的名称更改为“rs0”就足够了,这是在配置中为您的副本集指定的名称。
您将拥有:
$ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0"
我在
docker-撰写文件:
services:
mongo1:
container_name: mongo1
image: mongo:4.2
ports:
- 27017:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
mongo2:
container_name: mongo2
image: mongo:4.2
ports:
- 27018:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
mongo3:
container_name: mongo3
image: mongo:4.2
ports:
- 27019:27017
restart: always
command: ["--bind_ip_all", "--replSet", "rs" ]
replica_set:
image: mongo:4.2
container_name: replica_set
depends_on:
- mongo1
- mongo2
- mongo3
volume:
- ./initiate_replica_set.sh:/initiate_replica_set.sh
entrypoint:
- /initiate_replica_set.sh
initiate_replica_set.sh 文件:
#!/bin/bash
echo "Starting replica set initialize"
until mongo --host mongo1 --eval "print(\"waited for connection\")"
do
sleep 2
done
echo "Connection finished"
echo "Creating replica set"
mongo --host mongo1 <<EOF
rs.initiate(
{
_id : 'rs0',
members: [
{ _id : 0, host : "mongo1:27017" },
{ _id : 1, host : "mongo2:27017" },
{ _id : 2, host : "mongo3:27017" }
]
}
)
EOF
echo "replica set created"
副本集启动成功并运行良好,但当我尝试连接到副本集时出错:
$ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs"
MongoDB shell version v5.0.2
connecting to: mongodb://localhost:27017,localhost:27018,localhost:27019/?compressors=disabled&gssapiServiceName=mongodb&replicaSet=rs
{"t":{"$date":"2021-08-05T21:35:40.667Z"},"s":"I", "c":"NETWORK", "id":4333208, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"RSM host selection timeout","attr":{"replicaSet":"rs","error":"FailedToSatisfyReadPreference: Could not find host matching read preference { mode: \"nearest\" } for set rs"}}
Error: Could not find host matching read preference { mode: "nearest" } for set rs, rs/localhost:27017,localhost:27018,localhost:27019 :
connect@src/mongo/shell/mongo.js:372:17
@(connect):2:6
exception: connect failed
exiting with code 1
更详细的日志:
{
"t": {
"$date": "2021-08-05T21:35:54.531Z"
},
"s": "I",
"c": "-",
"id": 4333222,
"ctx": "ReplicaSetMonitor-TaskExecutor",
"msg": "RSM received error response",
"attr": {
"host": "mongo1:27017",
"error": "HostUnreachable: Error connecting to mongo1:27017 :: caused by :: Could not find address for mongo1:27017: SocketException: Host not found (authoritative)",
"replicaSet": "rs",
"response": "{}"
}
}
问题的原因是什么,我该如何解决?
这个问题在各个地方有一些不完整的答案,这里是我认为的完整答案。
原因
Mongo clients use the hostnames listed in the replica set config, not the seed list
虽然连接字符串是
的原因"mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs"
,mongo客户端并没有连接到种子地址为localhost:27017
等的副本集成员,而是连接到从种子主机返回的副本配置集,即rs.initiate
调用中的副本配置集。这就是错误消息是Error connecting to mongo1:27017
而不是Error connecting to localhost:27017
.容器主机名在容器网络外不可寻址
与 mongo 服务器容器位于同一容器网络中的 mongo 客户端可以通过
mongo1:27017
这样的地址连接到服务器;但是,位于容器网络外部的主机上的客户端无法将mongo1
解析为 IP。这个问题的典型解决方案是代理,详见
修复
因为问题涉及 docker 网络和 docker 网络在 Linux 和 Mac 之间变化。两个平台上的修复不同。
Linux
代理修复(通过第 3 方软件或修改 /etc/hosts
文件)工作正常但有时不可行,例如 运行 在远程 CI 主机上。一个简单的自包含便携式解决方案是更新 intiate_replia_set.sh
脚本以使用成员 IP 而不是主机名启动副本集。
intiate_replia_set.sh
echo "Starting replica set initialization"
until mongo --host mongo1 --eval "print(\"waited for connection\")"
do
sleep 2
done
echo "Connection finished"
echo "Creating replica set"
MONGO1IP=$(getent hosts mongo1 | awk '{ print }')
MONGO2IP=$(getent hosts mongo2 | awk '{ print }')
MONGO3IP=$(getent hosts mongo3 | awk '{ print }')
read -r -d '' CMD <<EOF
rs.initiate(
{
_id : 'rs',
members: [
{ _id : 0, host : '${MONGO1IP}:27017' },
{ _id : 1, host : '${MONGO2IP}:27017' },
{ _id : 2, host : '${MONGO3IP}:27017' }
]
}
)
EOF
echo $CMD | mongo --host mongo1
echo "replica set created"
这样 mongo 副本集成员的地址中就有容器 IP 而不是主机名。容器 IP 可从主机访问。
或者,我们可以在 docker-compose 文件中为每个容器显式分配静态 IP,并在启动副本集时使用静态 IP。这是一个类似的修复,但需要更多工作。
Mac
不幸的是,上述解决方案不适用于 Mac,因为 Mac 上的 docker 容器 IP 未暴露在主机网络接口上。 https://docs.docker.com/docker-for-mac/networking/#per-container-ip-addressing-is-not-possible
使其工作的最简单方法是在 /etc/hosts
文件中添加以下映射:
127.0.0.1 mongo1 mongo2 mongo3
我认为将副本集“rs”的名称更改为“rs0”就足够了,这是在配置中为您的副本集指定的名称。
您将拥有:
$ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0"