Okay so I know I can automate my "docker run" instructions like this, say I would do this without compose:
First create the volume
docker volume create --name mongodb-shard-1-node-1
Then the container
docker run --name mongodb-node-1 -d -v mongodb-node-1:/data/db -p 27031:27017 --link mongo-node-2:mongo mongo --replSet rs0 --smallfiles --oplogSize 128
This would be the same as including this in the docker-compose.yml file:
mongodb-node-1:
image: mongo
volumes:
- "mongodb-node-1:/data/db"
ports:
- "27031:27017"
container_name: mongodb-node-1
external_links:
- "mongodb-node-3:mongo"
command: --replSet rs0 --smallfiles --oplogSize 128
But I also have to run commands inside the mongodb shell, to do this I first use exec to enter the shell like this:
docker exec -it mongodb-shard-1-node-1 mongo
afterwards inside the shell I need to run commands such as
rs.initiate()
and others like
rs.addArb("172.17.0.6:27017")
etc...
Can I automate these last steps with docker-compose? Is it possible to automate this in docker at all?
You can't directly automate it like that, sadly.
As a workaround, you could extend the container to add in a shell script which runs, starts Mongo, then runs the specified commands. You could even pass in that IP address in an environment variable if it needs to be modifiable.
Kontena has leveraged docker-compose.yml file format and introduced this kind of functionality by adding post_start hook.
peer:
image: mongo:3.2
stateful: true
command: --replSet kontena --smallfiles
instances: 3
hooks:
post_start:
- cmd: sleep 10
name: sleep
instances: 1
oneshot: true
- cmd: mongo --eval "printjson(rs.initiate());"
name: rs_initiate
instances: 1
oneshot: true
- cmd: mongo --eval "printjson(rs.add('%{project}-peer-2'))"
name: rs_add2
instances: 1
oneshot: true
- cmd: mongo --eval "printjson(rs.add('%{project}-peer-3'))"
name: rs_add3
instances: 1
oneshot: true
https://github.com/kontena/examples/blob/master/mongodb-cluster/kontena.yml
$ kontena app deploy command will deploy all three mongodb peers and add them to replica set.
Related
I want to leverage Github Action's service and use it to create a mongo instance with replica set support. This is what I have right now.
mongo:
image: mongo:5.0.3
volumes:
- /d/test-db.d
ports:
- 27017:27017
options: >-
--entrypoint /usr/bin/docker run --name mongo_container --replSet test --bind_ip_all
--health-cmd "bash -c \"if [ $(mongo mongo_container --quiet --eval 'rs.initiate().ok || rs.status().ok') -eq 1 ]; then exit 1; else exit 0; fi\""
--health-interval 10s
--health-timeout 5s
--health-retries 5
--name test
However, github actions gives me an error 'denied: requested access to the resource is denied' when trying to call docker run as the entrypoint. I suppose it's not permittable for me to directly run docker run.
I also tried setting --replSet test and --bind_ip_all within the options array, but it is not recognized and throws and error.
I see nothing in the documentation regarding replica sets. Are they even supported as a service?
I want to create a MongoDB replica set with a standalone instance running in docker to be able to work with multi-document transactions.
The problem is that the replica set is not initiated automatically, I always have to start it manually, so I can't run it in gitlab runner.
My Dockerfile is as follows:
FROM mongo:4.2
RUN echo "rs.initiate({'_id':'rs0','members':[{'_id':0,'host':'127.0.0.1:27017'}]});" > /docker-entrypoint-initdb.d/replica-init.js
RUN chmod u+x /docker-entrypoint-initdb.d/replica-init.js
RUN cat /docker-entrypoint-initdb.d/replica-init.js
CMD [ "--bind_ip_all", "--replSet", "rs0" ]
And here the docker-compose snippet:
mongo:
build:
context: .
dockerfile: docker/mongo/Dockerfile
command: --replSet rs0
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
ports:
- 27017:27017
I put this script into docker-entrypoint-initdb.d so it should run automatically on first container start but it does not happen.
If I run the same initiate() command manually using mongo shell - it then works fine.
Any ideas on what I do wrong?
Current State
I have a MongoDB instance running on a server without the replication set flag (--replSet)
I have some previously stored information on the Database and wish to retain the information
Aim
I wish to however restart the container with the --replSet "my-set" flag set for the daemon and keep the previous information intact
Implementation
I am trying to follow along a tutorial for setting replica sets in MongoDB with Docker and trying it out on my local machine.
Create a standard MongoDB Docker container w/o the flag replSet set which represents the current state:
docker run -d --name mongo_rs --publish 37017:27017 mongo
Using the MongoDB Compass I connected to the DB and added some dummy information to a Database called test and collection called players
I stop the container:
docker container stop mongo_rs
From here onwards I wish to add the --replSet "my-set" to the mongo_rs container and configuring the Replica set via the mongo Shell as mentioned in the tutorial. What is the possible solution for achieving it?
Here is my .yml file
version: '3.7'
services:
node1:
image: mongo
ports:
- 30001:27017
volumes:
- $HOME/mongoclusterdata/node1:/data/db
networks:
- mongocluster
command: mongod --replSet comments
node2:
image: mongo
ports:
- 30002:27017
volumes:
- $HOME/mongoclusterdata/node2:/data/db
networks:
- mongocluster
command: mongod --replSet comments
depends_on :
- node1
node3:
image: mongo
ports:
- 30003:27017
volumes:
- $HOME/mongoclusterdata/node3:/data/db
networks:
- mongocluster
command: mongod --replSet comments
depends_on :
- node2
networks:
mongocluster:
driver: bridge
The volume section has absolute path which is different from the root.Actually docker file creates a self config file on root , so if u have root as a docker-compose install location change it to else where, and now the config file settings would never delete on docker-install up/down.
Here is a workaround:
1- copy the entrypoint script to your host:
docker cp mongo_rs:/usr/local/bin/docker-entrypoint.sh .
2- edit the script , change the line (last line) exec "$#" to :
mongod --replSet my-mongo-set --port 27017
3- re-copy the script to your container:
docker cp docker-entrypoint.sh mongo_rs:/usr/local/bin/docker-entrypoint.sh
4- start your container:
docker start mongo_rs
I have several docker containers , one of them is Mongodb official image,
Here's part of my docker-compose.yml file
version: '3'
services:
mongo:
image: mongo
container_name: mongo01
# command: ["mongod", "-f", "/etc/mongo/mongod.conf"]
volumes:
- ./data/mongodata:/data/db
# - ./config/mongo:/etc/mongo
restart: always
ports:
- "27017:27017"
I could access to mongo service from the host ( my system) but according to the mongo new security policy there is config for limit access to mongo just form 127.0.0.1,I know it , it's
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
if I could push the mongo image read my custom config I could resolve the problem, but I tried to
to mount a custom config file - ./config/mongo:/etc/mongo and then run mongod with command: ["mongod", "-f", "/etc/mongo/mongod.conf"] but didn't work.
it seems mongod starting in container as process 1 and try to run it with custom command not works, even when I tried to shutdown the mongod in container with mongod --shutdown it shutdown the whole container.( I wanted to stop the mongod and then rerun it with mongod --bind_ip_all )
So the problem is how we can change the mongo image config file ?
The mongo docker image already has an ENTRYPOINT set and it basically is mongod, so in your command (CMD) you can add extra arguments to mongod
simple docker run
docker run -d mongo --bind_ip_all
or with compose
version: '3'
services:
mongo:
image: mongo
command: ["--bind_ip_all"]
ports:
- "27017:27017"
The entrypoint for the official mongo image already contains a step to add --bind_ip_all as long as you don't explicitly bind a specific IP:
# MongoDB 3.6+ defaults to localhost-only binding
haveBindIp=
if _mongod_hack_have_arg --bind_ip "$#" || _mongod_hack_have_arg --bind_ip_all "$#"; then
haveBindIp=1
elif _parse_config "$#" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then
haveBindIp=1
fi
if [ -z "$haveBindIp" ]; then
# so if no "--bind_ip" is specified, let's add "--bind_ip_all"
set -- "$#" --bind_ip_all
fi
I'm trying to deploy a mongodb replicaset using docker.
I managed to do it on a same server by executing this :
docker run -d --expose 27017 --name mongodbmycompany1 dockerfile/mongodb mongod --replSet rsmycompa
docker run -d --expose 27017 --name mongodbmycompany2 dockerfile/mongodb mongod --replSet rsacommeassure
docker run -d --expose 27017 --name mongodbmycompany3 dockerfile/mongodb mongod --replSet rsacommeassure
MONGODB1=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mongodbmycompany1)
MONGODB2=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mongodbmycompany2)
MONGODB3=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mongodbmycompany3)
echo $MONGODB1
echo $MONGODB2
echo $MONGODB3
echo "Mongodb Replicaset init"
docker exec mongodbmycompany1 mongo 127.0.0.1:27017/mycompany --eval 'if(!rs.conf()) { rs.initiate(); cfg = rs.conf(); cfg.members[0].host = "'$MONGODB1':27017"; rs.reconfig(cfg); rs.add("'$MONGODB2':27017"); rs.add("'$MONGODB3':27017"); } rs.status();'
It's working as expected. My replicaset is initialized and my mongodb resultset config contains my 3 servers identified by their internal IP address.
It's not perfect as I'd prefer to use servers names but I didn't manage to do it.
Docker only populate each /etc/hosts file with servers names passed at image launch with --link parameter. If i add a new server while others are running. Those servers won't ping the new server.
Now I have another question. In production, having a lot of Mongodb docker image running on a same physical server is possible but it's not safe :
- if my physical server falls down, i lose all my Mongodb replicas and my service is down
- if my physical server uses internal storage, all my docker images use the same disk... and I'm going to have IO problems.
So my question is : How can I deploy many mongodb replicas on multiple physical servers ? How those mongodb replicas can communicate with each others (primary and secondaries servers can change) while they are on different servers or even on different datacenters ?
Let's assume:
you have 3 different docker hosts (servers), with IPs 10.1.1.101, 10.1.1.102, 10.1.1.103
want to deploy a single replica set called rsacommeassure
Dockerfiles for mongodb expose port 27017
all servers are in a trusted zone and can talk to each other
First let's start mongodb containers on each server (10.1.1.101 ~$ is used for command prompt):
10.1.1.101 ~$ docker run -d -p 27017:27017 --name mongodbmycompany1 dockerfile/mongodb mongod --replSet rsacommeassure
10.1.1.102 ~$ docker run -d -p 27017:27017 --name mongodbmycompany2 dockerfile/mongodb mongod --replSet rsacommeassure
10.1.1.103 ~$ docker run -d -p 27017:27017 --name mongodbmycompany3 dockerfile/mongodb mongod --replSet rsacommeassure
-p 27017:27017 exposes port 27017 on the host IP so mongo is accessible on servers' host IP address.
Then you need to initiate the replica set, so just run this against a mongodb container (I'll pick server1 here):
your_laptop ~$ > mongo --host 10.1.1.101
MongoDB shell version: 2.6.9
connecting to: test
> rs.initiate()
> cfg = rs.conf()
> cfg.members[0].host = "10.1.1.101:27017"
> rs.reconfig(cfg)
> rs.add("10.1.1.102:27017")
> rs.add("10.1.1.103:27017")
> rs.status();
The IPs are local but it works with global as well as long as the servers can talk to each other (VPN, firewall, DMZ, whatever). Btw you should consider security carefully.
I've created a Replica Set on different physical servers using docker-machine and virtual box as a driver:
$ docker-machine create --driver virtualbox server1
$ docker-machine create --driver virtualbox server2
$ docker-machine create --driver virtualbox server3
Open 3 different terminals, in each
$(Terminal1) eval "$(docker-machine env server1)"
$(Terminal2) eval "$(docker-machine env server2)"
$(Terminal3) eval "$(docker-machine env server3)"
In each terminal:
$(Terminal1) docker run -d -p 27017:27017 --name mongoClient1 mongo mongod --replSet r1
$(Terminal2) docker run -d -p 27017:27017 --name mongoClient2 mongo mongod --replSet r1
$(Terminal3) docker run -d -p 27017:27017 --name mongoClient3 mongo mongod --replSet r1
Go in VirtualBox -> on each environment(server1,server2,server3) -> Setting -> Network -> Adapter 1 -> Port Forwarding. Create a new rule Protocol TCP, Host Port - 27017, Guest Port - 27017, leave Host Ip and Guest Ip empty
Now restart all the environments, you can do this from the VirtualBox or from the terminal, from terminal just run:
$(Terminal1) docker-machine restart server1
$(Terminal2) docker-machine restart server2
$(Terminal3) docker-machine restart server3
Restart the containers:
$(Terminal1) docker start mongoClient1
$(Terminal2) docker start mongoClient2
$(Terminal3) docker start mongoClient3
Now the containers should be running, you can check them by running
$ docker ps in each terminal
Get into the first container's(or another) Mongo Shell
$(Terminal1) docker exec -it mongoClient1 mongo
// now we are in the Mongo Shell
$(Mongo Shell) rs.initiate()
$(Mongo Shell) cfg = rs.conf()
$(Mongo Shell) cfg.members[0].host = <server1's Ip Address>
// you should get server1's Ip Address by running $ docker-machine ls, mine was 192.168.99.100
$(Mongo Shell) rs.reconfig(cfg)
$(Mongo Shell Primary) rs.add("<server2's Ip Address>:27017")
// now we added a Secondary
$(Mongo Shell Primary) rs.add("<server3's Ip Address>:27017", true)
// now we added an Arbiter
$(Mongo Shell Primary) use planes
// now we create a new database
$(Mongo Shell Primary) db.tranporters.insert({name:'Boeing'})
// create a new collection
$(Mongo Shell Primary) db.tranporters.find()
// we obtain the inserted plane
To connect to a Secondary, you can either:
$(Terminal2) docker exec -it mongoClient2 mongo planes
// or
$(Mongo Shell Primary) db = connect ("<server2's Ip Address>:27017/planes")
Now we are in the Mongo Shell of a Secondary
$(Mongo Shell Secondary) rs.slaveOk()
// to allow readings from the Shell
$(Mongo Shell Secondary)db.tranporters.find()
// should return inserted plane
You could use "Weave - the Docker network" to resolve your problem easily.
Weave creates an overlay network that joins containers on different hosts, even at different cloud providers. Weave also supplies a DNS service that lets you find containers by name within the Weave network.
#Here stop already running instances
docker stop m1 m2 m3
#Cleanup of the volumes
docker rm -f m1 m2 m3
# Start MongoDB services optimised for faster startup
docker run -dP --name m1 mongo mongod --replSet "r1" --noprealloc --smallfiles --nojournal --syncdelay 0
docker run -dP --name m2 mongo mongod --replSet "r1" --noprealloc --smallfiles --nojournal --syncdelay 0
docker run -dP --name m3 mongo mongod --replSet "r1" --noprealloc --smallfiles --nojournal --syncdelay 0
export M1_ADDRESS=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' m1`
export M2_ADDRESS=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' m2`
export M3_ADDRESS=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' m3`
docker exec m1 mongo --eval "rs.initiate();"
docker exec m1 mongo --eval "cfg = rs.conf(); cfg.members[0].host = '$M1_ADDRESS:27017'; rs.reconfig(cfg);"
docker exec m1 mongo --eval "rs.add('$M2_ADDRESS:27017');rs.add('$M3_ADDRESS:27017')"
# Check if everything is fine.
docker exec m1 mongo --eval "rs.status();"
I scripted a docker-image that sets up a mongodb replicaSet for any number of containers and automates scaling as you add more containers. check out the github github repo or docker registry
Use Docker Compose
setup your docker-compose.yml
version: "2"
services:
<your_service_name>:
image: rollymaduk/mongo-replica:local
environment:
REPLICA_NAME: "<your_replica_name>"
volumes:
- /var/config:/var/config
run in command line
docker-compose up
scale up to more containers
docker-compose scale <your_service_name>=3
Docker Cloud
To deploy mongo-db replicaSet using docker-cloud, set up a stack
file
stack.yml: stack file not requiring shared volumes
<service_name>:
image: rollymaduk/mongo-replica:cloud
deployment_strategy: high_availability
target_num_containers: 3
environment:
REPLICA_NAME: <your_replica_name>
DOCKERCLOUD_AUTH: <your_docker_auth_key>
stack.yml: stack file requiring shared volume
<service_name>:
image: rollymaduk/mongo-replica:local
deployment_strategy: high_availability
target_num_containers: 3
volumes:
- /var/config:/var/config
environment:
REPLICA_NAME: <your_replica_name>
using docker-cloud cli run in command line (If stack file does not exist in cloud)
docker-cloud stack create --name <your_stack_name> -f <your_stack_file>
docker-cloud stack start <your_stack_name>
using docker-cloud cli run in command line (If stack file already exists in cloud)
docker-cloud stack update -f <your_stack_file> <your_stack_name>
docker-cloud stack redeploy <your_stack_name>
Important: you must specify a shared volume and mount it to the container's
config directory [default: /var/config ] the example below mounts a host
directory /var/config to the container's config volume.
You can use any of the docker recommended ways of sharing volumes between
containers i.e mounting a host directory or data volume container),just
you specify the correct path for the config volume (/var/config).
_to change default config directory use the environment variable
--CONFIG_DIR to setup your container. Make sure to update host volume
path to reflect your new --CONFIG_DIR name_