我有一个 NodeJS Express 应用程序,它依赖于 MongoDB 更改流。为了使它们可用,必须将 MongoDB 配置为作为副本集运行(即使该集合中只有一个节点)。
我正在使用 Windows 10 专业版。
我正在尝试对这个应用程序进行 docker 化,将 MongoDB 容器基于官方
mongo:5
图像。
为了实现此目的,我需要一种将数据库初始化为副本集的自动化方法。我发现的教程依赖于
exec
进入容器并从 rs.initiate()
运行 mongosh
(或类似的方法),这是我想避免的手动工作。或者他们使用像wait-for-it.sh
这样的黑客作为这里。
我觉得必须有一个更好的解决方案,以某种方式基于docs中的“初始化一个新实例”段落。
它描述了
当容器第一次启动时,它将执行在
中找到的扩展名为.sh
和.js
的文件。/docker-entrypoint-initdb.d
当恰好在容器生命周期中时,会发生这种情况吗?容器初始化之后?或者数据库准备好之后?因为这似乎是这个初始化逻辑的完美位置,当从容器内手动执行时,它可以完美地运行。
但是,放置
// initReplSet.js
print('Script running');
config={"_id":"rs0", "members":[{"_id":0,"host":"app-db:27017"}]};
print(JSON.stringify(rs.initiate(config)));
print('Script end');
失败并出现错误
{"ok":0,"errmsg":"No host described in new configuration with {version: 1, term: 0} for replica set rs0 maps to this node","code":93,"codeName":"InvalidReplicaSetConfig"}
,但数据库可在其他容器的主机名 app-db
下使用。这让我觉得这段代码在所有其他初始化逻辑(网络)完成之前运行得太早了。
另一种方法是放置一个通过
mongosh
执行代码的 bash 脚本。这是我尝试过的:
#!/bin/bash
mongosh "mongodb://app-db:27017/app_db" "initiateReplSet"
哪里
initiateReplSet
是
config={"_id":"rs0", "members":[{"_id":0,"host":"app-db:27017"}]}
rs.initiate(config)
exit
但这会导致容器崩溃并出现错误
/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/initiateReplSetWrapper.sh
{"t":{"$date":"2022-02-15T11:31:23.353+00:00"},"s":"I", "c":"-", "id":4939300, "ctx":"monitoring-keys-for-HMAC","msg":"Failed to refresh key cache","attr":{"error":"NotYetInitialized: Cannot use non-local read concern until replica set is finished initializing.","nextWakeupMillis":600}}
Warning: Could not access file: EACCES: permission denied, mkdir '/home/mongodb'
Current Mongosh Log ID: 620b8f0b04b7ad69b446768d
Connecting to: mongodb://app-db:27017/app_db?directConnection=true&appName=mongosh+1.1.9
只有第一行和最后三行似乎真正属于bash脚本,第二行不断重复。
我不确定错误是否源于
permission denied
问题,或者数据库是否真的无法访问。但是,指定
RUN mkdir -p /home/mongodb/.mongodb
RUN chown -R 777 /home/mongodb
在
Dockerfile
中并没有改善情况(尽管如此,还是同样的错误)。
您能否解释一下为什么这种方法不起作用,或者如何使其起作用?是否有另一种更好的自动化方法来初始化副本集? docker镜像可以改进以允许这样的初始化逻辑吗?
我刚刚通过一次疯狂的实验让它发挥作用。意味着我只是在 JS 脚本中省略了对
rs.initiate()
的调用中的配置。由于某种原因,脚本成功运行并且更改流可供我的 NodeJS 后端使用。
我将发布运行启用更改流的 MongoDB docker 所需的所有内容:
# Dockerfile
From mongo
WORKDIR .
COPY initiateReplSet.js ./docker-entrypoint-initdb.d/
CMD ["-replSet", "rs0"]
// initiateReplSet.js
rs.initiate()
您能否解释一下为什么这种方法不起作用
因为初始化仅在第一次运行docker镜像时发生(即init脚本找不到数据库文件)。
当发生这种情况时,它会以隐形模式运行 MongoDB,监听器仅绑定到本地主机地址。
要执行集群初始化,运行rs.initiate
的节点需要能够访问所有副本,包括其自身,但这显然不会发生。在
这个 Github 问题中有更详细的讨论。
是否有另一种更好的自动化方法来初始化副本集?是的,在这个
要点中。
我只保留要点链接,因为我不确定我是否有权在此处复制脚本主体。