MongoDB Docker副本集连接错误“Host not found”

eh57zj3b  于 2023-10-16  发布在  Go
关注(0)|答案(3)|浏览(111)

我在this SO answer之后创建了一个本地MongoDB副本集。
docker-compose文件:

  1. services:
  2. mongo1:
  3. container_name: mongo1
  4. image: mongo:4.2
  5. ports:
  6. - 27017:27017
  7. restart: always
  8. command: ["--bind_ip_all", "--replSet", "rs" ]
  9. mongo2:
  10. container_name: mongo2
  11. image: mongo:4.2
  12. ports:
  13. - 27018:27017
  14. restart: always
  15. command: ["--bind_ip_all", "--replSet", "rs" ]
  16. mongo3:
  17. container_name: mongo3
  18. image: mongo:4.2
  19. ports:
  20. - 27019:27017
  21. restart: always
  22. command: ["--bind_ip_all", "--replSet", "rs" ]
  23. replica_set:
  24. image: mongo:4.2
  25. container_name: replica_set
  26. depends_on:
  27. - mongo1
  28. - mongo2
  29. - mongo3
  30. volume:
  31. - ./initiate_replica_set.sh:/initiate_replica_set.sh
  32. entrypoint:
  33. - /initiate_replica_set.sh

initiate_replica_set. sh文件:

  1. #!/bin/bash
  2. echo "Starting replica set initialize"
  3. until mongo --host mongo1 --eval "print(\"waited for connection\")"
  4. do
  5. sleep 2
  6. done
  7. echo "Connection finished"
  8. echo "Creating replica set"
  9. mongo --host mongo1 <<EOF
  10. rs.initiate(
  11. {
  12. _id : 'rs0',
  13. members: [
  14. { _id : 0, host : "mongo1:27017" },
  15. { _id : 1, host : "mongo2:27017" },
  16. { _id : 2, host : "mongo3:27017" }
  17. ]
  18. }
  19. )
  20. EOF
  21. echo "replica set created"

复制副本集已成功启动并正常运行,但当我尝试连接到复制副本集时,它出错:

  1. $ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs"
  2. MongoDB shell version v5.0.2
  3. connecting to: mongodb://localhost:27017,localhost:27018,localhost:27019/?compressors=disabled&gssapiServiceName=mongodb&replicaSet=rs
  4. {"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"}}
  5. Error: Could not find host matching read preference { mode: "nearest" } for set rs, rs/localhost:27017,localhost:27018,localhost:27019 :
  6. connect@src/mongo/shell/mongo.js:372:17
  7. @(connect):2:6
  8. exception: connect failed
  9. exiting with code 1

更多详细日志:

  1. {
  2. "t": {
  3. "$date": "2021-08-05T21:35:54.531Z"
  4. },
  5. "s": "I",
  6. "c": "-",
  7. "id": 4333222,
  8. "ctx": "ReplicaSetMonitor-TaskExecutor",
  9. "msg": "RSM received error response",
  10. "attr": {
  11. "host": "mongo1:27017",
  12. "error": "HostUnreachable: Error connecting to mongo1:27017 :: caused by :: Could not find address for mongo1:27017: SocketException: Host not found (authoritative)",
  13. "replicaSet": "rs",
  14. "response": "{}"
  15. }
  16. }

问题的原因是什么,我如何解决它?

wljmcqd8

wljmcqd81#

关于这个问题,各地都有一些片面的答案,以下是我认为完整的答案。
《The Cause》

  • Mongo客户端使用副本集配置中列出的主机名,而不是种子列表

虽然连接字符串是"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。这个问题的典型解决方案是代理,请参阅Access docker container from host using containers name了解详细信息。

修复

因为这个问题涉及到Docker网络,而Docker网络在Linux和Mac之间各不相同。这两个平台上的修复是不同的。

Linux

代理修复(通过第三方软件或修改/etc/hosts文件)工作正常,但有时不可行,例如在远程CI主机上运行。一个简单的自包含可移植解决方案是更新intiate_replia_set.sh脚本,以使用成员IP而不是主机名启动副本集。

initiate_replia_set.sh

  1. echo "Starting replica set initialization"
  2. until mongo --host mongo1 --eval "print(\"waited for connection\")"
  3. do
  4. sleep 2
  5. done
  6. echo "Connection finished"
  7. echo "Creating replica set"
  8. MONGO1IP=$(getent hosts mongo1 | awk '{ print $1 }')
  9. MONGO2IP=$(getent hosts mongo2 | awk '{ print $1 }')
  10. MONGO3IP=$(getent hosts mongo3 | awk '{ print $1 }')
  11. read -r -d '' CMD <<EOF
  12. rs.initiate(
  13. {
  14. _id : 'rs',
  15. members: [
  16. { _id : 0, host : '${MONGO1IP}:27017' },
  17. { _id : 1, host : '${MONGO2IP}:27017' },
  18. { _id : 2, host : '${MONGO3IP}:27017' }
  19. ]
  20. }
  21. )
  22. EOF
  23. echo $CMD | mongo --host mongo1
  24. 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文件中添加以下Map:

  1. 127.0.0.1 mongo1 mongo2 mongo3
展开查看全部
l2osamch

l2osamch2#

docker compose用户的其他信息。
要捕获上述情况,请使用healthcheck,它利用ping和URL显式定义replicaSet=

  1. healthcheck:
  2. << : *healthcheck_defaults
  3. test: ['CMD-SHELL', 'mongosh --eval "db.adminCommand(\"ping\")" mongodb://${MONGODB_USERNAME}:${MONGODB_PASSWORD}@mongo:27017/${MONGODB_DATABASE}?replicaSet=rs0' ]
h22fl7wq

h22fl7wq3#

我认为将replicaSet的名称“rs”更改为“rs0”就足够了,这是配置中为您的replicaSet指定的名称。
您将拥有:

  1. $ mongo "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0"

相关问题