0

I use MongoDB change stream in my code and need to create a MongoDB docker image with change stream enabled. The problem is that mongod should be started first with default settings to allow create users, documents, etc. Then mongod should be stopped. Then the replica set should be added to the mongod.conf to enable change stream:

# mongod.conf
replication:
  replSetName: rs0
  oplogSizeMB: 100

After that mongod should be started again and replica set initialized by MongoDB shell:

rs.slaveOk()
rs.initiate()
rs.initiate({_id:"rs0", members: [{"_id":1, "host":"127.0.0.1:27017"}]})

MogodDB 3.6 base image provides an initialization capabilities. Do you know how to start mongod, initialize DB then stop it and reconfigure?

UPD: I need to initialize database then add replica set. Therefore, I need to run the mongod with the default mongod.conf, create users and collections, then restart the mongod with another mongod,conf in which the replica set is enabled. I can't do that with official MongoDB image. I've installed MongoDB 3.6.12 on Ubuntu image. My MongoDB container is working well after running the setup commands manually in its bash shell, but the same instructions not working from Dockerfile Here is the commands

RUN mongod --fork --config /etc/mongod.conf \
     && mongo < /opt/init_mongodb.js \
     && mongod --shutdown --dbpath /var/lib/mongodb \
     && cp /etc/mongod.conf /etc/mongod.conf.orig \
     && mongod --fork --config /opt/mongod.conf \
     && mongo -u "root" -p "root" --authenticationDatabase "admin" <  /opt/reconfig_mongodb.js \

When run this commands from Dockerfile, the following error appears

> backend@1.0.0 start /usr/src/app
> npm run babelserver

> backend@1.0.0 babelserver /usr/src/app
> babel-node --presets es2015 index.js

(node:41) UnhandledPromiseRejectionWarning: MongoError: not master and slaveOk=false
    at queryCallback (/usr/src/app/node_modules/mongodb-core/lib/cursor.js:248:25)
    at /usr/src/app/node_modules/mongodb-core/lib/connection/pool.js:532:18
    at processTicksAndRejections (internal/process/task_queues.js:82:9)
(node:41) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
  • Not your mentioned way, but my the `/docker-entrypoint-initdb.d`' dir solve your problem. See: https://hub.docker.com/_/mongo – Wie May 11 '19 at 07:01
  • Tried that image, but looks like it doesn't allow to restart mongod with custom mongod.conf – David Osipyan May 14 '19 at 21:04
  • `/docker-entrypoint-initdb.d` will only execute by frist container start (no restart). And from the Docker Hub Readme: "`mongod` does not read a configuration file by default, so the `--config` option with the path to the configuration file needs to be specified" – Wie May 15 '19 at 06:52

3 Answers3

2

I don't think that's a clean way of setting up your replica set. It really should be much simpler.

Instead of running one of the members in background mode, setting up the replicaset, saving the config file, shutting down process, and lastly starting with the saved config file, you can just run an additional "helper" container (also a mongodb image) whose sole purpose is to set up the mongodb replica set without acting as a member.

Something similar to this would do the job:

docker-compose.yaml

version: '3'
services:
  mongodb-rs0-1:
    image: ${your_image}
    ports: [27018:27018]
    environment:
      - PORT=27018
      - MONGO_RS=rs0/mongodb-rs0-1:27018,mongodb-rs0-2:27019,mongodb-rs0-3:27020
    volumes:
    - ./db/rs0-1:/data/db
  mongodb-rs0-2:
    image: ${your_image}
    ports: [27019:27019]
    environment:
      - PORT=27019
      - MONGO_RS=rs0/mongodb-rs0-1:27018,mongodb-rs0-2:27019,mongodb-rs0-3:27020
    volumes:
    - ./db/rs0-2:/data/db
  mongodb-rs0-3:
    image: ${your_image}
    ports: [27020:27020]
    environment:
      - PORT=27020
      - MONGO_RS=rs0/mongodb-rs0-1:27018,mongodb-rs0-2:27019,mongodb-rs0-3:27020
    volumes:
    - ./db/rs0-3:/data/db
  mongodb-replicator:
    image: ${your_image}
    environment:
      - PRIMARY=mongodb-rs0-1:27018
    depends_on:
      - mongodb-rs0-1
      - mongodb-rs0-2
      - mongodb-rs0-3
networks:
  default:
    external:
      name: mongo

and in container_start.sh:

#!/bin/sh

if [ -z "$PRIMARY" ]; then
  # Member container 
  docker-entrypoint.sh --replSet rs0 --smallfiles --oplogSize 128 --port "${PORT-27017}"
else
  # Replicator container
  until mongo "$PRIMARY" /app/replicaset-setup.js; do
      sleep 10
  done

fi
  • How and where is container_start.sh used? This looks as if it only will execute for the first rs0 container. Also is /app/replicaset-setup.js; already in the container image? – dynamiclynk Nov 04 '22 at 12:18
1

Even thought it took me hours to figure out, it's actually pretty easy to setup a dockerized mongodb instance with replication enabled (which allows the use of change streams.)

Put both of these in the same folder:

Dockerfile

FROM mongo:4.4
ADD ./init_replicaset.js /docker-entrypoint-initdb.d/init_replicaset.js
CMD ["mongod", "--replSet", "rs0", "--bind_ip_all"]

init_replicaset.js

rs.initiate({'_id':'rs0', members: [{'_id':1, 'host':'127.0.0.1:27017'}]});

Note: All .js/.sh files in the /docker-entrypoint-initdb.d directory are automatically run when the mongo instance is initialized which is why this works.

From within the same folder, run the following:

docker build . -t mongo_repl

docker run -i -p 27017:27017 mongo_repl

That's it! You should now have a running mongodb instance with replication enable.

88jayto
  • 659
  • 1
  • 5
  • 20
0

Resolved by initializing the replica set on container startup. This script is set as ENTRYPOINT in docker file

#!/bin/sh
# start mongod, initialize replica set
mongod --fork --config /opt/mongod.conf
mongo --quiet < /opt/replica.js
# restart mongod    
mongod --shutdown --dbpath /var/lib/mongodb
mongod --config /opt/mongod.conf

replica.js

rs.slaveOk()
rs.initiate()
rs.initiate({_id:"rs0", members: [{"_id":1, "host":"127.0.0.1:27017"}]})
  • Why are you calling `rs.initiate()` twice? Also shouldn't it be `_id: 0` for a single node replica set? – jshbrntt Dec 17 '20 at 20:35