Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MongoDB Replica Set / docker-compose / mongoose transaction with persistent volume

This will guide you through setting up a replica set in a docker environment using.

  • Docker Compose
  • MongoDB Replica Sets
  • Mongoose
  • Mongoose Transactions

Thanks to https://gist.github.com/asoorm for helping with their docker-compose file!

mongo-setup:
container_name: mongo-setup
image: mongo
restart: on-failure
networks:
default:
volumes:
- ./scripts:/scripts
entrypoint: [ "/scripts/setup.sh" ] # Make sure this file exists (see below for the setup.sh)
depends_on:
- mongo1
- mongo2
- mongo3
mongo1:
hostname: mongo1
container_name: localmongo1
image: mongo
expose:
- 27017
ports:
- 27017:27017
restart: always
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--journal", "--dbpath", "/data/db", "--enableMajorityReadConcern", "false" ]
volumes:
- <VOLUME-DIR>/mongo/data1/db:/data/db # This is where your volume will persist. e.g. VOLUME-DIR = ./volumes/mongodb
- <VOLUME-DIR>/mongo/data1/configdb:/data/configdb
mongo2:
hostname: mongo2
container_name: localmongo2
image: mongo
expose:
- 27017
ports:
- 27018:27017
restart: always
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--journal", "--dbpath", "/data/db", "--enableMajorityReadConcern", "false" ]
volumes:
- <VOLUME-DIR>/mongo/data2/db:/data/db # Note the data2, it must be different to the original set.
- <VOLUME-DIR>/mongo/data2/configdb:/data/configdb
mongo3:
hostname: mongo3
container_name: localmongo3
image: mongo
expose:
- 27017
ports:
- 27019:27017
restart: always
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--journal", "--dbpath", "/data/db", "--enableMajorityReadConcern", "false" ]
volumes:
- <VOLUME-DIR>/mongo/data3/db:/data/db
- <VOLUME-DIR>/mongo/data3/configdb:/data/configdb
# NOTE: This is the simplest way of achieving a replicaset in mongodb with Docker.
# However if you would like a more automated approach, please see the setup.sh file and the docker-compose file which includes this startup script.
# run this after setting up the docker-compose This will instantiate the replica set.
# The id and hostname's can be tailored to your liking, however they MUST match the docker-compose file above.
docker-compose up -d
docker exec -it localmongo1 mongo
rs.initiate(
{
_id : 'rs0',
members: [
{ _id : 0, host : "mongo1:27017" },
{ _id : 1, host : "mongo2:27017" },
{ _id : 2, host : "mongo3:27017", arbiterOnly: true }
]
}
)
exit
// If on a linux server, use the hostname provided by the docker compose file
// e.g. HOSTNAME = mongo1, mongo2, mongo3
// If on MacOS add the following to your /etc/hosts file.
// 127.0.0.1 mongo1
// 127.0.0.1 mongo2
// 127.0.0.1 mongo3
// And use localhost as the HOSTNAME
mongoose.connect('mongodb://<HOSTNAME>:27017,<HOSTNAME>:27018,<HOSTNAME>:27019/<DBNAME>', {
useNewUrlParser : true,
useFindAndModify: false, // optional
useCreateIndex : true,
replicaSet : 'rs0', // We use this from the entrypoint in the docker-compose file
})
#!/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`
MONGODB1=mongo1
MONGODB2=mongo2
MONGODB3=mongo3
echo "**********************************************" ${MONGODB1}
echo "Waiting for startup.."
until curl http://${MONGODB1}:27017/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": "rs0",
"protocolVersion": 1,
"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
async function transaction() {
// Start the transaction.
const session = await ModelA.startSession();
session.startTransaction();
try {
const options = { session };
// Try and perform operation on Model.
const a = await ModelA.create([{ ...args }], options);
// If the first operation succeeds this next one will get called.
await ModelB.create([{ ...args }], options);
// If all succeeded with no errors, commit and end the session.
await session.commitTransaction();
session.endSession();
return a;
} catch (e) {
// If any error occured, the whole transaction fails and throws error.
// Undos changes that may have happened.
await session.abortTransaction();
session.endSession();
throw e;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment