docker 由于ReplicaSetGhost,单个映像中的MongoDb副本集在GitLab CI/CD中失败

btxsgosb  于 2023-06-05  发布在  Docker
关注(0)|答案(1)|浏览(536)

bounty 8小时后到期。此问题的答案有资格获得+500声望奖励。diegosasw希望吸引更多注意力这个问题:赏金赢家将是使用我创建的自定义副本集docker镜像的人,进行必要的更改(如果有的话),并成功地将其用作GitLab CI/CD服务,解释如何

我正在努力使用MongoDb 6副本集和CI/CD中的自动化测试。
我创建了一个带有副本集的docker镜像,用于开发和测试目的。
当在本地运行docker容器并对它运行测试时,这很好用。查看存储库https://gitlab.com/sunnyatticsoftware/sandbox/mongo-rs并查看公共存储库www.example.com中的docker镜像registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
它有一个dockerfile

FROM mongo:6.0.5-jammy as base

COPY ./init-mongodbs.sh ./init-replica.sh ./entry-point.sh /

RUN chmod +x /init-mongodbs.sh && \
    chmod +x /init-replica.sh && \
    chmod +x /entry-point.sh

# Data directory
ARG DB1_DATA_DIR=/var/lib/mongo1
ARG DB2_DATA_DIR=/var/lib/mongo2
ARG DB3_DATA_DIR=/var/lib/mongo3

# Log directory
ARG DB1_LOG_DIR=/var/log/mongodb1
ARG DB2_LOG_DIR=/var/log/mongodb2
ARG DB3_LOG_DIR=/var/log/mongodb3

# DB Ports
ARG DB1_PORT=27017
ARG DB1_PORT=27018
ARG DB1_PORT=27019

RUN mkdir -p ${DB1_DATA_DIR} && \
    mkdir -p ${DB1_LOG_DIR} && \
    mkdir -p ${DB2_DATA_DIR} && \
    mkdir -p ${DB2_LOG_DIR} && \
    mkdir -p ${DB3_DATA_DIR} && \
    mkdir -p ${DB3_LOG_DIR} && \
    chown `whoami` ${DB1_DATA_DIR} && \
    chown `whoami` ${DB1_LOG_DIR} && \
    chown `whoami` ${DB2_DATA_DIR} && \
    chown `whoami` ${DB2_LOG_DIR} && \
    chown `whoami` ${DB3_DATA_DIR} && \
    chown `whoami` ${DB3_LOG_DIR}

EXPOSE ${DB1_PORT}
EXPOSE ${DB2_PORT}
EXPOSE ${DB3_PORT}

ENTRYPOINT [ "bash", "entry-point.sh" ]

它复制了一些在执行容器时运行的脚本
entry-point.sh

#!/bin/bash

/bin/bash ./init-replica.sh &
/bin/bash ./init-mongodbs.sh

init-mongodbs.sh

#!/bin/bash

# Data directory
DB1_DATA_DIR="/var/lib/mongo1"
DB2_DATA_DIR="/var/lib/mongo2"
DB3_DATA_DIR="/var/lib/mongo3"

# Log directory
DB1_LOG_DIR="/var/log/mongodb1"
DB2_LOG_DIR="/var/log/mongodb2"
DB3_LOG_DIR="/var/log/mongodb3"

REPLICA_SET="${REPLICA_SET_NAME:-rs0}"

mongod --dbpath ${DB1_DATA_DIR} --logpath ${DB1_LOG_DIR}/mongod.log --fork --port 27017 --bind_ip_all --replSet $REPLICA_SET
mongod --dbpath ${DB2_DATA_DIR} --logpath ${DB2_LOG_DIR}/mongod.log --fork --port 27018 --bind_ip_all --replSet $REPLICA_SET
mongod --dbpath ${DB3_DATA_DIR} --logpath ${DB3_LOG_DIR}/mongod.log --port 27019 --bind_ip_all --replSet $REPLICA_SET

init-replica.sh

#!/bin/bash

DB1_PORT=27017
DB2_PORT=27018
DB3_PORT=27019

LOCAL_HOST="${HOST:-localhost}"
REPLICA_SET="${REPLICA_SET_NAME:-rs0}"
SLEEP_INITIATE="${DELAY_INITIATE:-30}"

RS_MEMBER_1="{ \"_id\": 0, \"host\": \"${LOCAL_HOST}:${DB1_PORT}\", \"priority\": 2 }"
RS_MEMBER_2="{ \"_id\": 1, \"host\": \"${LOCAL_HOST}:${DB2_PORT}\", \"priority\": 0 }"
RS_MEMBER_3="{ \"_id\": 2, \"host\": \"${LOCAL_HOST}:${DB3_PORT}\", \"priority\": 0 }"

echo "Waiting ${SLEEP_INITIATE} seconds before initiating replica set"
sleep ${SLEEP_INITIATE} 
mongosh --eval "rs.initiate({ \"_id\": \"${REPLICA_SET}\", \"members\": [${RS_MEMBER_1}, ${RS_MEMBER_2}, ${RS_MEMBER_3}] });"

echo "Replica set initiated"
echo "$(mongosh --eval "rs.status()")"

以便它可以作为

version: '3.8'
services:
  mongors:
    image: registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
    container_name: mongors
    environment:
      HOST: mongors
      DELAY_INITIATE: 40
    ports:
      - 27017:27017
      - 27018:27018
      - 27019:27019

注意,它接受一个环境变量HOST,我可以用它来给予一个别名mongors,这样在GitLab CI/CD中我就可以引用这个主机名。
我的应用程序连接字符串可能是这样的

mongodb://mongors:27017,mongors:27018,mongors:27019/?replicaSet=rs0&readPreference=primary&ssl=false

正如我所说的,当在本地将副本集作为docker容器运行时,这很好用(请参阅docker compose)。

**当它在GitLab CI/CD中运行时会超时。**我使用来自www.example.com的共享运行器GitLab.com,内置的。

运行gitlab-runner 16.1.0~beta.5.gf131a6a2(f131 a6 a2)
然而,连接似乎工作正常

$ nc -zv mongors 27017
Connection to mongors (172.17.0.4) 27017 port [tcp/*] succeeded!
$ nc -zv mongors 27018
Connection to mongors (172.17.0.4) 27018 port [tcp/*] succeeded!
$ nc -zv mongors 27019
Connection to mongors (172.17.0.4) 27019 port [tcp/*] succeeded!

所以连接是可以的,但我从我的应用程序中得到了这样的例外

System.TimeoutException: A timeout occurred after 30000ms selecting a server using CompositeServerSelector{ Selectors = MongoDB.Driver.MongoClient+AreSessionsSupportedServerSelector, LatencyLimitingServerSelector{ AllowedLatencyRange = 00:00:00.0150000 }, OperationsCountServerSelector }. Client view of cluster state is { ClusterId : "1", ConnectionMode : "ReplicaSet", Type : "ReplicaSet", State : "Connected", Servers : [{ ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/mongors:27017" }", EndPoint: "Unspecified/mongors:27017", ReasonChanged: "Heartbeat", State: "Connected", ServerVersion: 6.0.0, TopologyVersion: { "processId" : ObjectId("646f9b54fb0fbb72cb9a3b70"), "counter" : NumberLong(0) }, Type: "ReplicaSetGhost", WireVersionRange: "[0, 17]", LastHeartbeatTimestamp: "2023-05-25T17:36:03.9519134Z", LastUpdateTimestamp: "2023-05-25T17:36:03.9519144Z" }] }.

提示副本集存在ReplicaSetGhost或其他错误。
你知道为什么在GitLab CI/CD中,我不能让我的自动化集成测试连接到使用与我的HOST env变量匹配的服务别名的连接字符串吗?
在某个时候我在痕迹中看到了这个

MongoServerError: No host described in new configuration with {version: 1, term: 0} for replica set rs0 maps to this node

因此,可能副本集内部不理解主机名mongors

**更新1(5月25日):**我创建了一个示例仓库,其中包含一个示例管道,该管道使用独立的mongoDB单示例和我创建的镜像mongo-rs来测试连接性。

https://gitlab.com/sunnyatticsoftware/sandbox/mongo-rs-tester

image: ubuntu:22.04

stages:
  - test

test_mongors:
  stage: test
  services:
    - name: registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
      alias: mongors
  variables:
    # MongoDB
    HOST: "mongors"
    DELAY_INITIATE: "50"
  before_script:
    - apt-get update
    - apt-get install -y curl
    - apt-get install -y gnupg
    - curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | tee /etc/apt/trusted.gpg.d/mongodb.asc > /dev/null
    - echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
    - apt-get update
    - apt-get install -y mongodb-mongosh
    - sleep 30
  script: |
    output=$(mongosh --host mongors:27017 --eval "db.stats()" 2>&1); if [ $? -eq 0 ]; then echo "OK"; else echo "ERROR: $output"; fi

test_mongo:
  stage: test
  services:
    - name: mongo:6.0.5-jammy
      alias: mongostandalone
  variables:
    MONGO_INITDB_ROOT_USERNAME: root
    MONGO_INITDB_ROOT_PASSWORD: dummy

  before_script:
    - apt-get update
    - apt-get install -y curl
    - apt-get install -y gnupg
    - curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | tee /etc/apt/trusted.gpg.d/mongodb.asc > /dev/null
    - echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
    - apt-get update
    - apt-get install -y mongodb-mongosh
  script: |
    output=$(mongosh --host mongostandalone:27017 --username root --password dummy --eval "db.stats()" 2>&1); if [ $? -eq 0 ]; then echo "OK"; else echo "ERROR: $output"; fi

**更新2(5月29日):**将IP添加到/etc/hosts没有任何区别。

image: ubuntu:22.04

stages:
  - test

test_mongors:
  stage: test
  services:
    - name: registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
      alias: mongors
  variables:
    # MongoDB
    HOST: "mongors"
    DELAY_INITIATE: "50"
  before_script:
    - apt-get update
    - apt-get install -y curl
    - apt-get install -y gnupg
    - apt-get install -y dnsutils
    - MONGORS_ALIAS="mongors"
    - MONGORS_IP_ADDRESS=$(getent hosts $MONGORS_ALIAS | awk '{ print $1 }')
    - echo "$MONGORS_IP_ADDRESS  $MONGORS_ALIAS" >> /etc/hosts
    - echo "MongoDB IP $MONGORS_IP_ADDRESS added"
    - cat /etc/hosts
    - curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | tee /etc/apt/trusted.gpg.d/mongodb.asc > /dev/null
    - echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
    - apt-get update
    - apt-get install -y mongodb-mongosh
    - sleep 30
  script: |
    output=$(mongosh --host mongors:27017 --eval "db.stats()" 2>&1); if [ $? -eq 0 ]; then echo "OK"; else echo "ERROR: $output"; fi

test_mongo:
  stage: test
  services:
    - name: mongo:6.0.5-jammy
      alias: mongostandalone
  variables:
    MONGO_INITDB_ROOT_USERNAME: root
    MONGO_INITDB_ROOT_PASSWORD: dummy

  before_script:
    - apt-get update
    - apt-get install -y curl
    - apt-get install -y gnupg
    - curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | tee /etc/apt/trusted.gpg.d/mongodb.asc > /dev/null
    - echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
    - apt-get update
    - apt-get install -y mongodb-mongosh
  script: |
    output=$(mongosh --host mongostandalone:27017 --username root --password dummy --eval "db.stats()" 2>&1); if [ $? -eq 0 ]; then echo "OK"; else echo "ERROR: $output"; fi

更新3:30五月

为了简化,我创建了另一个存储库来测试我的MongoDB副本集映像
https://gitlab.com/sunnyatticsoftware/sandbox/mongo-rs-tester-dotnet
基本上,它是一个带有自动化测试的小型.NET应用程序,可以使用

dotnet test

我添加了一个docker-compose.yml,其中包含一个用于复制集MongoDB映像的容器

version: '3.8'
services:
  mongors:
    image: registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
    container_name: mongors
    environment:
      HOST: mongors
      DELAY_INITIATE: 40
    ports:
      - 27017:27017
      - 27018:27018
      - 27019:27019

.gitlab-ci.yml的等价物,失败(我不知道为什么)

image: mcr.microsoft.com/dotnet/sdk:7.0

stages:
  - test

test:
  stage: test
  services:
    - name: registry.gitlab.com/sunnyatticsoftware/sandbox/mongo-rs
      alias: mongors
  variables:
    HOST: "mongors"
    DELAY_INITIATE: "40"
  before_script:
    - sleep 30
  script: 
    - dotnet test
  allow_failure: false

步骤如下:
要求

  • .NET 7 SDK或运行时
  • Docker和docker-compose

如何在本地运行

1.克隆存储库
1.在/etc/hosts(或在Windows C:\Windows\System32\drivers\etc\hosts中)中添加此条目,并使用localhost的别名

127.0.0.1   mongors

1.执行docker-compose up以启动mongors容器
1.使用dotnet test运行测试
验证测试是否成功,并连接到Mongo副本集数据库
请注意,连接字符串是mongodb://mongors:27017,mongors:27018,mongors:27019/?replicaSet=rs0&readPreference=primary&ssl=false,它使用别名mongors。

如何在GitLab CI/CD中运行

现在让我们在GitLab CI/CD中尝试等效的
1.运行CI/CD管道,这将启动别名为mongors的容器服务,并将dotnet test命令作为脚本运行
1.如果测试正确连接到Mongo副本集数据库,请验证测试是否成功。
如果测试不成功并且管道失败,这是因为Mongo RS容器和测试在GitLab CI/CD中运行时的行为不同。为什么
它失败了

System.TimeoutException : A timeout occurred after 30000ms selecting a server using CompositeServerSelector{ Selectors = MongoDB.Driver.MongoClient+AreSessionsSupportedServerSelector, LatencyLimitingServerSelector{ AllowedLatencyRange = 00:00:00.0150000 }, OperationsCountServerSelector }. Client view of cluster state is { ClusterId : "1", ConnectionMode : "ReplicaSet", Type : "ReplicaSet", State : "Connected", Servers : [{ ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/mongors:27017" }", EndPoint: "Unspecified/mongors:27017", ReasonChanged: "Heartbeat", State: "Connected", ServerVersion: 6.0.0, TopologyVersion: { "processId" : ObjectId("6475c89d5538f6657eda5c74"), "counter" : NumberLong(0) }, Type: "ReplicaSetGhost", WireVersionRange: "[0, 17]", LastHeartbeatTimestamp: "2023-05-30T09:59:11.3576006Z", LastUpdateTimestamp: "2023-05-30T09:59:11.3576012Z" }, { ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/mongors:27018" }", EndPoint: "Unspecified/mongors:27018", ReasonChanged: "Heartbeat", State: "Connected", ServerVersion: 6.0.0, TopologyVersion: { "processId" : ObjectId("6475c89ea07e16061b09e4ec"), "counter" : NumberLong(0) }, Type: "ReplicaSetGhost", WireVersionRange: "[0, 17]", LastHeartbeatTimestamp: "2023-05-30T09:59:11.3575679Z", LastUpdateTimestamp: "2023-05-30T09:59:11.3576205Z" }, { ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/mongors:27019" }", EndPoint: "Unspecified/mongors:27019", ReasonChanged: "Heartbeat", State: "Connected", ServerVersion: 6.0.0, TopologyVersion: { "processId" : ObjectId("6475c89e4cf3656cf898c899"), "counter" : NumberLong(0) }, Type: "ReplicaSetGhost", WireVersionRange: "[0, 17]", LastHeartbeatTimestamp: "2023-05-30T09:59:11.3496821Z", LastUpdateTimestamp: "2023-05-30T09:59:11.3496847Z" }] }

更新4:31五月

我已经禁用了共享的runner,并创建和注册了我自己的docker类型的Linux runner。
有了这个转轮,测试成功了。
https://gitlab.com/sunnyatticsoftware/sandbox/mongo-rs-tester-dotnet/-/jobs/4385389688
我希望服务和CI/CD对跑步者是不可知的。
有没有什么解释为什么它不适用于共享跑步者?

wn9m85ua

wn9m85ua1#

如果它在本地运行良好,这应该意味着设置本身是好的,但是GitLab CI/CD处理网络的方式和/或MongoDB副本集解析主机名的方式是有问题的。
错误消息MongoServerError: No host described in new configuration with {version: 1, term: 0} for replica set rs0 maps to this node表明主机名mongors在MongoDB副本集中无法识别。
默认情况下,MongoDB副本集成员使用指定为during the rs.initiate() command的主机名相互通信。当您在单独的机器上或使用localhost在单个机器上运行MongoDB示例时,这通常不是问题,但当您在Docker容器内运行MongoDB时,它可能会导致问题,其中容器内的主机名可能与容器外的主机名不同。
在本例中,您使用HOST环境变量(设置为mongors)初始化MongoDB副本集,因此MongoDB节点应该使用mongors别名进行节点间通信。但是,您看到的错误消息表明,在MongoDB节点中无法识别mongors别名。
当您启动MongoDB副本集时,您使用的主机名(或别名)存储在MongoDB的副本集配置中。MongoDB使用这些主机名(或别名)进行副本集内的节点间通信。
如果你使用了一个别名,比如mongors,并且这个别名在Docker容器中是正确的defined in the /etc/hosts file(如果你在GitLab CI/CD中使用alias功能,应该是这种情况),那么MongoDB应该能够使用这个别名进行节点间通信。
(Note:对Kubernetes executor was introduced in GitLab Runner 12.8的别名支持,仅适用于Kubernetes版本1.7或更高版本。
然而,重要的是要注意,这取决于在Docker容器内正确定义的别名,以及MongoDB能够将此别名解析为IP地址。如果别名解析存在任何问题,这可能会导致您看到的问题。
此外,如果您的MongoDB节点在单独的Docker容器中运行(在副本集中通常是这种情况),则需要确保在每个容器的/etc/hosts文件中定义别名,并且每个容器可以将此别名解析为正确的IP地址。如果你使用的是Docker的默认桥接网络,这应该会自动发生。但是如果您使用的是自定义网络,则可能需要使用Docker的--add-host选项手动添加别名。
查看您的.gitlab-ci.yml
在GitLab CI/CD中,同一阶段的作业并行执行,但不同阶段的作业按顺序执行。
在你的管道中,test_mongo和test_mongors这两个作业都在同一个阶段,名为test。因此,默认情况下,它们将并行执行。
但是一旦test_mongo运行,它的mongodb独立示例就不再存在了。
GitLab CI/CD管道中的每个作业都在自己的隔离环境中运行。这意味着在作业中定义的服务,例如您的MongoDB示例,仅在作业运行时存在。作业完成后,其环境(包括已启动的任何服务)将被拆除。
因此,在管道中,当test_mongors作业启动时,在test_mongo作业中启动的MongoDB示例将不再存在。
如果test_mongors作业需要与test_mongo作业中的MongoDB示例进行交互,它将无法进行交互,因为该示例将被关闭。
我尝试在test_mongo仍在运行时为test_mongors添加必要的睡眠来测试访问,但it fails
使用您的第二个gitlab-ci.yml,我尝试添加一个mongosh --host $HOST --port 27017 --eval "db.stats()"
我得到了:

Accessing MongoDB instance at port 27017
Current Mongosh Log ID: 6476008e02ae6412b3aafec3
Connecting to:      mongodb://mongors:27017/?directConnection=true&appName=mongosh+1.9.1
Using MongoDB:      6.0.5
Using Mongosh:      1.9.1
For mongosh info see: https://docs.mongodb.com/mongodb-shell/
To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.
------
   The server generated these startup warnings when booting
   2023-05-30T13:54:57.976+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
   2023-05-30T13:54:57.976+00:00: You are running this process as the root user, which is not recommended
   2023-05-30T13:54:57.976+00:00: vm.max_map_count is too low
------
MongoServerError: node is not in primary or recovering state

在这个阶段,我将更新Docker镜像以启用HTTP接口:您需要在配置文件中使用--httpinterface选项或net.http.enabled: true选项启动MongoDB。
要启用RESTful接口,您需要在配置文件中使用--rest选项或net.http.RESTInterfaceEnabled: true选项启动MongoDB。
然后,您可以使用curl或wget向MongoDB发出HTTP请求。

curl http://mongors:28017/serverStatus

我会在120秒内检查状态如何演变(每10秒),以尝试了解数据库集群状态。
来自评论:
当使用GitLab runner“on sasw-linux”执行时,它可以工作。但是当在green-1.shared.runners-manager.gitlab.com/default上使用GitLab Runner执行时失败了。

  • 使用一组GitLab runner,它有一个带有dotnet core SDK默认docker镜像的docker executor
  • 使用“shared.runners-manager.gitlab.com/default”将失败

最近共享runner出现了一些问题,比如# 30944: unix socket errors when NETWORK_PER_BUILD is enabled on the Shared Runners。虽然它可能与手头的问题无关,但它说明了特定type of runner上缺乏稳定性。
使用自己的跑步者手动注册似乎更安全。

相关问题