为了在本地环境中以编程方式将 python 连接到我们的 AWS DocumentDB(AWS 版本的 MongoDB)实例,我遇到了一些问题。我们尝试了以下步骤:
为了开始我们的实验,我们使用推荐的 AWS 教程来创建 python 连接:https://docs.aws.amazon.com/documentdb/latest/developerguide/connect_programmatically.html.
由于我们希望最初在本地进行设置,因此我们使用以下命令与集群建立 SSH 连接: ssh -i "example.pem" -L 27019:docdb.cluster-XXXXXXX.region.docdb.amazonaws.com:27017 [电子邮件受保护] -N -v 该命令运行良好,我们能够创建隧道并将本地端口 27019 连接到我们的集群。关于这一点的一个重要注意事项是,为了到达集群,我们必须激活 VPN 网络。
我们调整了代码以连接到本地端口:
from pymongo import MongoClient,ReadPreference
import urllib
import ssl
username=urllib.parse.quote_plus("username")
password=urllib.parse.quote_plus("password")
port=27019
host="localhost"
dbName="general"
dbUri = f'mongodb://{username}:{password}@{host}:{port}/?tls=true&tlsCAFile=./rds-combined-ca-bundle.pem&retryWrites=false'
print(dbUri)
client = MongoClient(dbUri)
print(client.list_database_names())
运行上面的示例,我们得到错误:
pymongo.errors.ServerSelectionTimeoutError: hostname '127.0.0.1' doesn't match either of 'docdb.XXXXXXX.region.docdb.amazonaws.com', 'docdb.cluster-XXXXXXX.region.docdb.amazonaws.com', 'docdb.cluster-ro-XXXXXXX.region.docdb.amazonaws.com', Timeout: 30s, Topology Description: <TopologyDescription id: 61e7f8c811a815088e97e5a7, topology_type: Unknown, servers: [<ServerDescription ('127.0.0.1', 27019) server_type: Unknown, rtt: None, error=CertificateError("hostname '127.0.0.1' doesn't match either of 'docdb.XXXXXXX.region.docdb.amazonaws.com', 'docdb.cluster-XXXXXXX.region.docdb.amazonaws.com', 'docdb.cluster-ro-XXXXXXX.region.docdb.amazonaws.com'",)>]>
2022-01-19 11:41:27,120| ERROR | Could not establish connection from local ('127.0.0.1', 27019) to remote ('ai-docdb-dev.cluster-cq8mixmxuhvt.eu-west-1.docdb.amazonaws.com', 27017) side of the tunnel: open new channel error:
这表明两件事:首先,我们的代码正确使用 .pem 证书,并且能够获取我们的集群名称。其次,代码无法理解 localhost 是一个地址并检查本地端口 27019 是否到达集群。
为了尝试解决这个问题,我们还尝试将代码变量主机替换为 127.0.01,但结果是相同的。我们还尝试调整 pymongo 函数 MongoClient https://pymongo.readthedocs.io/en/stable/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient 的几个参数,但结果仍然相同。
谢谢
经过多次不成功的尝试,我们终于找到了解决方案!
使用 pymongo 3.X.X,我们能够通过将参数
tlsInsecure=true
添加到原始 URI(传递给我们的 MongoClient
函数)来连接到我们的 documentDb 实例。
对于 pymongo 4.X.X,我们必须添加一个额外的参数
directConnection=true
才能正常工作。
host
变量可以设置为localhost
或127.0.0.1
,username
和password
可以用urlib或不加密。
看起来证书检查正在验证您正在使用的主机名,因此我们可以通过使用实际的主机名并将其在主机文件中映射到
127.0.0.1
来欺骗它。
将每个
docdb.XXXXXXX.region.docdb.amazonaws.com
添加到您的呼叫系统的 hosts 文件,例如
127.0.0.1 docdb.XXXXXXX-1.region.docdb.amazonaws.com
127.0.0.1 docdb.XXXXXXX-2.region.docdb.amazonaws.com
127.0.0.1 docdb.XXXXXXX-3.region.docdb.amazonaws.com
然后更改宿主变量:
host=f"docdb.XXXXXXX-1.region.docdb.amazonaws.com:{port},docdb.XXXXXXX-2.region.docdb.amazonaws.com:{port},docdb.XXXXXXX-3.region.docdb.amazonaws.com:{port}"
然后更改 URI 以删除端口,因为它现在位于主机变量中:
dbUri = f'mongodb://{username}:{password}@{host}/?tls=true&tlsCAFile=./rds-combined-ca-bundle.pem&retryWrites=false'
将 &tlsAllowInvalidHostnames=true 添加到 URI 以跳过主机名检查。
这个连接字符串对我有用:
mongodb://mongodbuser:{username}:{password}@{host}:27017/?tls=true&tlsCAFile=./rds-combined-ca-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false&tlsAllowInvalidHostnames=true&directConnection=true
还有 directConnection - 对于 aws 必须为 false,对于本地为 true(例如使用隧道时)