DocumentDB 把 BSON 类型、文档 CRUD API 和 MongoDB Wire Protocol 网关带进了 PostgreSQL。对应用侧而言,mongosh、PyMongo 和 Node.js MongoDB Driver 仍然是在连接 MongoDB;对基础设施侧而言,底层实际运行的是 PostgreSQL。这套能力也是 Azure DocumentDB 背后的同一引擎,并已包含在 pglayers-azure 配置镜像中。
这很适合正在使用 PostgreSQL、但某些业务又需要 MongoDB 文档访问模型的团队:不必为本地开发、测试或特定工作负载单独维护一套 MongoDB 服务。不过,真正跑通链路时有两个关键点:网关端口是 10260,而且必须创建一个新的 MongoDB 登录角色,不能直接复用 postgres。
一个容器启动 PostgreSQL 和 DocumentDB 网关
pglayers-azure 镜像会在启动时完成 DocumentDB 所需配置:设置包含 pg_documentdb_gw_host 的 shared_preload_libraries、追加相关 GUC,并在首次初始化时创建 documentdb 扩展。
下面的命令同时暴露 PostgreSQL 的 5432 端口和 MongoDB Wire Protocol 网关监听的 10260 端口:
docker run -d --name pglayers-docdb \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-p 10260:10260 \
ghcr.io/pglayers/pglayers-azure:18
启动一两秒后,可通过日志确认网关已开始监听:
docker logs pglayers-docdb
预期能看到与 10260 端口 TCP listener 已绑定相关的日志。这里不需要手工执行 CREATE EXTENSION documentdb,因为 profile 镜像会自动处理。
版本选择不能忽略:DocumentDB 当前仅构建于 PostgreSQL 17 和 18,因此应使用 pglayers-azure:17 或 pglayers-azure:18,不要使用 pglayers-azure:19。如果明确不希望镜像自动创建扩展,可设置环境变量 PGLAYERS_CREATE_EXTENSIONS=none,但此时需要自行确保网关依赖的 documentdb 扩展存在。
为什么 postgres 用户连不上
DocumentDB 网关使用 PostgreSQL 原生的 SCRAM 认证。它的配置会阻止若干角色名前缀,例如 documentdb、citus、pg 和 internal_role。因此,默认的 postgres 超级用户不适合作为 MongoDB 客户端身份,应该创建一个独立角色。
一个容易踩中的坑是直接调用 DocumentDB 的 documentdb_api.create_user()。在默认镜像中,服务器设置为:
SHOW password_encryption;
-- scram-sha-256
此时 create_user() 会预先处理密码,而 DocumentDB 安装的 check_password hook 期待明文密码,以便自行构造网关需要的 SCRAM verifier。两者冲突后,常见报错类似:
ERROR: password type is not a plain text
可行路径是直接执行带明文密码的 CREATE ROLE,并授予 DocumentDB 内置角色。密码会由 hook 按网关预期方式处理:
docker exec pglayers-docdb psql -U postgres -c \
"CREATE ROLE mongoadmin WITH LOGIN INHERIT PASSWORD 'Secret_123' \
IN ROLE documentdb_admin_role, documentdb_readonly_role;"
示例中的 mongoadmin 和 Secret_123 仅用于本地验证。实际环境应替换为专用服务账号和由密钥管理系统提供的高强度密码。
用 mongosh 完成一次文档读写
网关默认使用自动生成的自签名 TLS 证书,因此客户端连接时需要开启 TLS,并暂时放宽证书校验。以下命令从临时容器发起连接,插入两条文档、执行条件查询,并计算平均价格:
docker run --rm --network host mongodb/mongodb-community-server:latest \
mongosh --quiet \
"mongodb://mongoadmin:Secret_123@localhost:10260/?tls=true&tlsAllowInvalidCertificates=true&authMechanism=SCRAM-SHA-256" \
--eval 'const db = db.getSiblingDB("shop"); db.products.insertMany([{name:"Widget",price:9.99},{name:"Gadget",price:19.5}]); printjson(db.products.find({price:{$gte:10}}).toArray()); printjson(db.products.aggregate([{$group:{_id:null,avg:{$avg:"$price"}}}]).toArray());'
这段命令会返回价格不低于 10 的 Gadget,并得到平均值 14.745。从调用形式看,这就是普通 MongoDB 操作:数据库、集合、过滤条件和聚合管道都沿用 MongoDB 客户端语义。
在 Linux 主机上,--network host 可以直接访问宿主机的 localhost:10260。在 Docker Desktop 等环境中,可以这样实践:把连接地址中的 localhost 改为 Docker 提供的宿主机地址,例如 host.docker.internal。
从 PyMongo 接入,并保留 SQL 查询能力
现有 Python 服务可以使用相同 URI 接入。运行前安装依赖:
python -m pip install pymongo
然后执行以下代码:
from pymongo import MongoClient
uri = (
"mongodb://mongoadmin:Secret_123@localhost:10260/"
"?tls=true&tlsAllowInvalidCertificates=true"
"&authMechanism=SCRAM-SHA-256"
)
client = MongoClient(uri)
db = client["shop"]
db.products.insert_one({"name": "Sprocket", "price": 4.5})
print(db.products.find_one({"name": "Sprocket"}))
client.close()
这类架构的实际价值不只在于兼容 MongoDB Driver。DocumentDB 数据仍在 PostgreSQL 中,因此同一份文档数据还可以从 SQL 侧访问。例如,可通过 documentdb_api.count_query('shop', ...) 一类 API 将文档查询结果纳入 PostgreSQL 内部的数据处理流程。采用前应先确认目标 MongoDB API 是否在 DocumentDB 的支持范围内,尤其是复杂聚合、索引行为、事务语义和特定驱动功能。
上生产前检查四件事
- 替换自签名证书。
tlsAllowInvalidCertificates=true只适用于镜像附带的自签名证书。生产环境应挂载自己的证书,并通过自定义/etc/documentdb/gateway_config.json配置 TLS、端口和受限角色名前缀。 - 固定兼容版本。 当前选择 PostgreSQL 17 或 18,并将镜像 tag 固定在经过验证的版本,而不是随意追踪不兼容的 PostgreSQL 19 镜像。
- 创建独立账号。 不要把 PostgreSQL 超级用户暴露给 MongoDB 客户端;使用最小权限角色,并对密码和连接串进行密钥管理。
- 验证 API 边界。 DocumentDB 的目标是 MongoDB 兼容,而不是自动等同于每个 MongoDB 版本和功能。上线前应使用真实查询、聚合和驱动配置完成兼容性测试。
对于已经以 PostgreSQL 为核心数据平台的团队,DocumentDB 加上 pglayers-azure 提供了一条很直接的验证路径:一个容器启动服务,一个 CREATE ROLE 创建客户端身份,然后用现成 MongoDB 客户端跑通读写。在决定承载生产流量前,再把 TLS、账号权限、版本固定和 API 兼容测试补齐。