复制集Mongo Docker Compose

41
我正在尝试使用docker-compose配置mongodb副本集,但是当我停止主容器时,似乎没有将其传递到次要容器。
redis:
 image: redis
 ports:
  - "6379:6379"

mongo3:
 hostname: mongo3
 image: mongo
 entrypoint: [ "/usr/bin/mongod", "--replSet", "rs", "--journal","--dbpath","/data/db","--smallfiles", "--rest" ]
 volumes:
  - ./data/mongo3:/data/db
 ports:
  - "27018:27017"
  - "28018:28017"
 restart: always

mongo2:
 hostname: mongo2
 image: mongo
 entrypoint: [ "/usr/bin/mongod", "--replSet", "rs", "--journal","--dbpath","/data/db","--smallfiles", "--rest" ]
 volumes:
  - ./data/mongo2:/data/db
 ports:
  - "27019:27017"
  - "28019:28017"
 restart: always

mongo1:
 hostname: mongo1
 image: mongo
 entrypoint: [ "/usr/bin/mongod", "--replSet", "rs", "--journal","--dbpath","/data/db","--smallfiles", "--rest" ]
 volumes:
  - ./data/mongo1:/data/db
ports:
  - "27017:27017"
  - "28017:28017"
links:
 - mongo2:mongo2
 - mongo3:mongo3
restart: always

web:
 build: .
 ports:
  - "2000:2000"
 volumes:
  - .:/vip
 links:
  - redis
  - mongo1
  - mongo2
  - mongo3

nginx:
 restart: always
 build: ./nginx/
 ports:
  - "80:80"
 links:
  - web:web

mongosetup:
 image: mongo
 links:
  - mongo1:mongo1
  - mongo2:mongo2
  - mongo3:mongo3
 volumes:
  - ./scripts:/scripts
 entrypoint: [ "/scripts/setup.sh" ]

setup.sh :

#!/bin/bash

MONGODB1=`ping -c 1 mongo1 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`
MONGODB2=`ping -c 1 mongo2 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`
MONGODB3=`ping -c 1 mongo3 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`

echo "**********************************************" ${MONGODB1}
echo "Waiting for startup.."
until curl http://${MONGODB1}:28017/serverStatus\?text\=1 2>&1 | grep uptime | head -1; do
  printf '.'
  sleep 1
done

echo curl http://${MONGODB1}:28017/serverStatus\?text\=1 2>&1 | grep uptime | head -1
echo "Started.."


echo SETUP.sh time now: `date +"%T" `
mongo --host ${MONGODB1}:27017 <<EOF
var cfg = {
    "_id": "rs",
    "version": 1,
    "members": [
        {
            "_id": 0,
            "host": "${MONGODB1}:27017",
            "priority": 2
        },
        {
            "_id": 1,
            "host": "${MONGODB2}:27017",
            "priority": 0
        },
        {
            "_id": 2,
            "host": "${MONGODB3}:27017",
            "priority": 0
        }
    ],settings: {chainingAllowed: true}
};
rs.initiate(cfg, { force: true });
rs.reconfig(cfg, { force: true });
rs.slaveOk();
db.getMongo().setReadPref('nearest');
db.getMongo().setSlaveOk(); 
EOF

请查看此文章:https://smaillns.medium.com/set-up-a-mongodb-replicaset-using-docker-799029493edf。 - Smaillns
8个回答

26

我有一个类似的问题,并通过以下compose文件解决:

version: "3.8"

services:
  mongo1:
    image: mongo:4.2
    container_name: mongo1
    command: ["--replSet", "my-replica-set", "--bind_ip_all", "--port", "30001"]
    volumes:
      - ./data/mongo-1:/data/db
    ports:
      - 30001:30001
    healthcheck:
      test: test $$(echo "rs.initiate({_id:'my-replica-set',members:[{_id:0,host:\"mongo1:30001\"},{_id:1,host:\"mongo2:30002\"},{_id:2,host:\"mongo3:30003\"}]}).ok || rs.status().ok" | mongo --port 30001 --quiet) -eq 1
      interval: 10s
      start_period: 30s

  mongo2:
    image: mongo:4.2
    container_name: mongo2
    command: ["--replSet", "my-replica-set", "--bind_ip_all", "--port", "30002"]
    volumes:
      - ./data/mongo-2:/data/db
    ports:
      - 30002:30002

  mongo3:
    image: mongo:4.2
    container_name: mongo3
    command: ["--replSet", "my-replica-set", "--bind_ip_all", "--port", "30003"]
    volumes:
      - ./data/mongo-3:/data/db
    ports:
      - 30003:30003

在我的/etc/hosts文件中包含以下内容:

127.0.0.1       mongo1
127.0.0.1       mongo2
127.0.0.1       mongo3

我在GitHub仓库中记录了它,并在这里写了一篇简短的博客文章:

https://github.com/UpSync-Dev/docker-compose-mongo-replica-set

https://www.upsync.dev/2021/02/02/run-mongo-replica-set.html


4
我试过这个方法,效果很好!我希望这将被认可为最佳答案。 - Zatara7
1
看起来这个程序每10秒运行一次rs.initiate()?为什么要这样做? - D. O.

15

更新:这个方法不可行!你需要运行rs.initiate()

在MongoDB 4.0中,你不需要第四个容器来运行设置脚本。只需轻松启动三个容器的副本集:

version: "3"
services:
  mongo1:
    hostname: mongo1
    container_name: localmongo1
    image: mongo:4.0-xenial
    expose:
      - 27017
    restart: always
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0" ]
  mongo2:
    hostname: mongo2
    container_name: localmongo2
    image: mongo:4.0-xenial
    expose:
      - 27017
    restart: always
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0" ]
  mongo3:
    hostname: mongo3
    container_name: localmongo3
    image: mongo:4.0-xenial
    expose:
      - 27017
    restart: always
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0" ]

更多信息请点击:https://github.com/msound/localmongo/tree/4.0


17
你是否仍然需要手动在其中一个节点上运行rs.initiate(...)和可能的rs.add(...)?如果不需要,那么你发布的docker-compose yaml文件中的哪一部分负责处理这个问题? - Shreyas
https://docs.mongodb.com/manual/tutorial/deploy-replica-set/ 上说你需要这样做。 - Michael Cole
还有,别忘了:rs.slaveOk() - Michael Cole
mongodb2 | 2019-06-28T02:04:15.218+0000 I STORAGE [initandlisten] 在 initAndListen 中发生异常:DBPathInUse:无法锁定锁文件:/data/db/mongod.lock(未知错误)。另一个 mongod 实例已在 /data/db 目录上运行,终止。 - crapthings
2
对于那些想要在docker compose中包含rs.initiate()调用的人,您需要使用healthcheck属性来实现。请参见相关线程上的此答案:https://stackoverflow.com/a/66755872/3493695 - iamarkadyt

8

我正在寻找如何使用一个数据库实例启动MongoDB副本集以进行本地开发,最终来到了这里。我发现这里的答案太过复杂,因此想出了以下解决方案:

docker-compose.yml

version: "3"
services: 
  mongo:
    hostname: mongodb
    container_name: mongodb
    image: mongo:latest
    restart: always
    ports:
      - "27017:27017"
    volumes:
      - ./scripts:/docker-entrypoint-initdb.d/
    command: ["--replSet", "rs0", "--bind_ip_all"]

当前目录下有一个名为“scripts”的文件夹,其中包含一个名为“init.js”的单个文件(名称不重要)。该文件夹作为卷挂载到“/docker-entrypoint-initdb.d/”这个特殊文件夹中。当MongoDB启动时,该目录中的所有文件都将被执行。文件的内容如下:
rs.initiate();

用这种方式,我无法访问我的集合中的数据。我收到以下错误消息:错误:{ "ok" : 0, "errmsg" : "节点不在主状态或恢复状态", "code" : 13436, "codeName" : "NotMasterOrSecondary" } - gustavz
@gustavz 请确保你的文件结构正确。否则,请在Mongo CLI客户端中运行“rs.initiate()”。 - armansimonyan13
在 MongoDB 5.0 中,需要使用卷,并且它可以正常工作。而在 MongoDB 4.x 中则不需要使用卷。 - huagang
那么这只使用一个容器,但对客户端表示副本集吗?需要副本集的代码(如事务)仍然可以工作吗? - m8a
添加“hostname”参数解决了我的问题,该问题在下面的主题中有记录。本质上,容器ID被用作主机名。当容器被替换时,分片无法再连接到配置服务器:https://stackoverflow.com/questions/75747148/upgrading-a-sharded-mongodb-cluster-in-docker-container-id-being-used-to-connec - Leia

4
我建议你查看khezen/mongo
你可以使用以下方法在3个节点的Docker Swarm中部署Mongo副本集:
version: '3'

services:

  replica1:
    image: khezen/mongo:slim
    deploy:
      mode: replicated
      replicas: 1
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
      palcement:
        node.hostname: node-1
    environment:
      RS_NAME: shard1
      SHARD_SVR: 'y'
      AUTH: 'y'
    volumes:
      - /data/mongo/replica1:/data/db
    networks:
      - mongo_cluster

  replica2:
    image: khezen/mongo:slim
    deploy:
      mode: replicated
      replicas: 1
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
      palcement:
        node.hostname: node-2
    environment:
      RS_NAME: shard1
      SHARD_SVR: 'y'
      AUTH: 'y'
    volumes:
      - /data/mongo/replica2:/data/db
    networks:
      - mongo_cluster

  replica3:
    image: khezen/mongo:slim
    deploy:
      mode: replicated
      replicas: 1
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
      palcement:
        node.hostname: node-3
    environment:
      RS_NAME: shard1
      SHARD_SVR: 'y'
      MASTER: replica3
      SLAVES: replica1 replica2
      AUTH: 'y'
    volumes:
      - /data/mongo/replica3:/data/db
    networks:
      - mongo_cluster

networks:
  mongo_cluster:
    driver: overlay

免责声明:我是这个镜像的维护者。


嘿,你怎么连接这些?我目前正在尝试通过我的docker-compose自动化整个部署,但是我看到你没有将任何端口暴露给全世界? - Evan Burbidge
@EvanBurbidge 您需要通过某个服务来定位副本集的目标,并针对适当的 pod/container 进行定位 - 仅仅打开端口或通过 L4 负载均衡器进行连接是不够的,因为您不能保证连接到 RS Leader。 - Junkiebev

1

1

我在一个带有认证的独立 mongodb 服务中设置 副本集 时遇到了类似的问题,以下是我的解决方法。

docker-compose.yml:

version: '3.7'

services:
  
  ...

  db:
    image: mongo
    restart: always
    expose:
      - 27017
    environment:
      MONGO_INITDB_DATABASE: ${DATABASE_NAME}
      MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USER}
      MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
      MONGO_REPLICA_SET_NAME: ${MONGO_REPLICA_SET_NAME}
    command: ["--replSet", "${MONGO_REPLICA_SET_NAME}", "--bind_ip_all"]
    healthcheck:
      test: test $$(echo "rs.status().ok" | mongo -u $${MONGO_INITDB_ROOT_USERNAME} -p $${MONGO_INITDB_ROOT_PASSWORD} --quiet) -eq 1
      interval: 10s
      start_period: 30s
    volumes:
      - ./db:/data/db
      - ./scripts/set-credentials.sh:/docker-entrypoint-initdb.d/set-credentials.sh

  replica-setup:
    image: mongo
    restart: on-failure
    networks:
      default:
    volumes:
      - ./scripts/setup-replica.sh:/scripts/setup-replica.sh
    entrypoint: [ "bash", "/scripts/setup-replica.sh" ]
    depends_on:
      - db
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USER}
      MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD}

./scripts/setup-replica.sh:

#!/bin/bash

MONGODB1=db

echo "Waiting for MongoDB startup..."
until curl http://${MONGODB1}:27017/serverStatus\?text\=1 2>&1 | grep uptime | head -1; do
  printf '.'
  sleep 1
done

# check if replica set is already initiated
RS_STATUS=$( mongo --quiet --host ${MONGODB1}:27017 -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --eval "rs.status().ok" )
if [[ $RS_STATUS != 1 ]]
then
  echo "[INFO] Replication set config invalid. Reconfiguring now."
  RS_CONFIG_STATUS=$( mongo --quiet --host ${MONGODB1}:27017 -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --eval "rs.status().codeName" )
  if [[ $RS_CONFIG_STATUS == 'InvalidReplicaSetConfig' ]]
  then
    mongo --quiet --host ${MONGODB1}:27017 -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD <<EOF
config = rs.config()
config.members[0].host = db # Here is important to set the host name of the db instance
rs.reconfig(config, {force: true})
EOF
  else
    echo "[INFO] MongoDB setup finished. Initiating replicata set."
    mongo --quiet --host ${MONGODB1}:27017 -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --eval "rs.initiate()" > /dev/null
  fi
else
  echo "[INFO] Replication set already initiated."
fi

./scripts/set-credentials.sh:

#!/bin/bash
set -e

mongo -- "$MONGO_INITDB_DATABASE" <<EOF
    var rootUser = '$MONGO_INITDB_ROOT_USERNAME';
    var rootPassword = '$MONGO_INITDB_ROOT_PASSWORD';
    var admin = db.getSiblingDB('admin');
    admin.auth(rootUser, rootPassword);

    var user = '$MONGO_INITDB_ROOT_USERNAME';
    var password = '$MONGO_INITDB_ROOT_PASSWORD';
    db.createUser({user: user, pwd: password, roles: ["readWrite"]});
EOF

我所实现的内容包括:
  • 为默认集合设置带有用户名/密码认证的mongodb服务
  • 在首次运行服务时初始化副本集
  • 当有先前的数据库数据时,重新配置副本集成员
  • 通过检查副本集状态来进行mongodb服务的健康检查

当启动mongod时,您不需要传递--auth标志吗? - java-addict301

1
如果您只需要使用docker-compose.yml创建MongoDB的单节点副本集,可以简单地使用以下命令:
mongodb:
  image: mongo:5
  restart: always
  command: ["--replSet", "rs0", "--bind_ip_all"]
  ports:
    - 27018:27017
  healthcheck:
    test: mongo --eval "rs.initiate()"
    start_period: 5s

0

这个设置对我来说很有效。我还在https://github.com/nguyenduyhust/docker-mongodb-replica-set中设置了所有内容。

如果这正是你想要的,那就太好了。

Dockerfile

FROM mongo
RUN mkdir /config
WORKDIR /config
COPY wait-for-it.sh .
COPY mongo-setup.js .
COPY mongo-setup.sh .
RUN chmod +x /config/wait-for-it.sh
RUN chmod +x /config/mongo-setup.sh
CMD [ "bash", "-c", "/config/wait-for-it.sh mongodb1:27011 -- /config/mongo-setup.sh"]

docker-compose.yml

version: "3"

services:
  mongodb1:
    container_name: mongo1
    image: mongo
    restart: always
    volumes:
      - ./volumes/mongodb1:/data/db
    ports:
      - "27011:27011"
    expose:
      - "27011"
    entrypoint:
      [
        "/usr/bin/mongod",
        "--port", "27011",
        "--replSet", "rs0",
        "--bind_ip_all",
      ]

  mongodb2:
    container_name: mongo2
    image: mongo
    restart: always
    volumes:
      - ./volumes/mongodb2:/data/db
    ports:
      - "27012:27012"
    expose:
      - "27012"
    entrypoint:
      [
        "/usr/bin/mongod",
        "--port", "27012",
        "--replSet", "rs0",
        "--bind_ip_all",
      ]

  mongodb3:
    container_name: mongo3
    image: mongo
    restart: always
    volumes:
      - ./volumes/mongodb3:/data/db
    ports:
      - "27013:27013"
    expose:
      - "27013"
    entrypoint:
      [
        "/usr/bin/mongod",
        "--port", "27013",
        "--replSet", "rs0",
        "--bind_ip_all",
      ]

  mongosetup:
    container_name: mongosetup
    image: "mongo-setup"
    build: "./mongo-setup"
    depends_on:
      - mongodb1

  mongo-express:
    container_name: mongo-express
    image: mongo-express
    environment:
      ME_CONFIG_MONGODB_URL: mongodb://mongodb1:27011,mongodb2:27012,mongodb3:27013/?replicaSet=rs0
    ports:
      - 8081:8081
    restart: always
    depends_on:
      - mongodb1
      - mongosetup

mongo-setup.js

rsconf = {
  _id : "rs0",
  members: [
      {
          "_id": 0,
          "host": "mongodb1:27011",
          "priority": 3
      },
      {
          "_id": 1,
          "host": "mongodb2:27012",
          "priority": 2
      },
      {
          "_id": 2,
          "host": "mongodb3:27013",
          "priority": 1
      }
  ]
}

rs.initiate(rsconf);

mongo-setup.sh

#!/usr/bin/env bash

if [ ! -f /data/mongo-init.flag ]; then
    echo "Init replicaset"
    mongo mongodb://mongodb1:27011 mongo-setup.js
    touch /data/mongo-init.flag
else
    echo "Replicaset already initialized"
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接