├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── docker-compose.yml └── scripts ├── entrypoint.sh ├── install_deps.sh ├── mongo_setup_repset.sh ├── mongo_setup_users.sh └── run.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | *.md 3 | *.yml 4 | Dockerfile 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | MAINTAINER Jeremy Shimko 3 | 4 | ENV MONGO_MAJOR 3.4 5 | ENV MONGO_VERSION 3.4.0 6 | ENV MONGO_PACKAGE mongodb-org 7 | ENV GOSU_VERSION 1.10 8 | ENV MONGO_SCRIPTS_DIR /opt/mongo/scripts 9 | ENV MONGO_KEYFILE /opt/mongo/keyfile 10 | 11 | # create user/group 12 | RUN groupadd -r mongodb && useradd -r -g mongodb mongodb 13 | 14 | # install dependencies 15 | COPY scripts/install_deps.sh $MONGO_SCRIPTS_DIR/install_deps.sh 16 | RUN $MONGO_SCRIPTS_DIR/install_deps.sh 17 | 18 | # mongod config 19 | ENV MONGO_AUTH true 20 | ENV MONGO_STORAGE_ENGINE wiredTiger 21 | ENV MONGO_DB_PATH /data/db 22 | ENV MONGO_JOURNALING true 23 | ENV MONGO_REP_SET rs0 24 | ENV MONGO_SECONDARY mongo2:27017 25 | ENV MONGO_ARBITER mongo3:27017 26 | 27 | # mongo root user (change me!) 28 | ENV MONGO_ROOT_USER root 29 | ENV MONGO_ROOT_PASSWORD root123 30 | 31 | # mongo app user + database (change me!) 32 | ENV MONGO_APP_USER myAppUser 33 | ENV MONGO_APP_PASSWORD myAppPassword 34 | ENV MONGO_APP_DATABASE myAppDatabase 35 | 36 | COPY scripts $MONGO_SCRIPTS_DIR 37 | 38 | VOLUME /data/db /data/configdb 39 | 40 | EXPOSE 27017 28017 41 | 42 | WORKDIR $MONGO_SCRIPTS_DIR 43 | 44 | ENTRYPOINT ["./entrypoint.sh"] 45 | 46 | CMD ["./run.sh"] 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dockerized MongoDB Replica Set 2 | 3 | This MongoDB Docker container is intended to be used to set up a 3 node replica set. 4 | 5 | Mongo version: **3.4.0** 6 | 7 | ## About 8 | 9 | A MongoDB [replica set](https://docs.mongodb.org/v3.4/replication/) consists of at least 3 Mongo instances. In this case, they will be a primary, secondary, and an arbiter. To use this project as a replica set, you simply launch three instances of this container across three separate host servers and the primary will configure your users and replica set. Also note that each server must be able to access the others (discovery must work in both directions). 10 | 11 | ## Setup 12 | 13 | You will need to create your own custom build of this image to generate a unique [keyfile](https://docs.mongodb.com/v3.4/tutorial/enforce-keyfile-access-control-in-existing-replica-set/) that Mongo uses for [internal authentication](https://docs.mongodb.org/v3.4/tutorial/enable-internal-authentication/) between replica set members. Each of your replica set members needs to have the same key, so be sure to use the same image in each location. Once using your build in production do NOT publish your Docker image to a public repo because it will container your private key. 14 | 15 | #### Build 16 | 17 | ```sh 18 | docker build -t yourname/mongo-rep-set:latest . 19 | ``` 20 | 21 | ## Launch 22 | 23 | Now you're ready to start launching containers. You need to launch the secondary and arbiter first so they're ready for the primary to configure them when it starts. 24 | 25 | #### Secondary 26 | 27 | ```sh 28 | docker run -d -p 27017:27017 yourname/mongo-rep-set:latest 29 | ``` 30 | 31 | #### Arbiter 32 | 33 | The only difference here is you can turn off journaling. From the [official docs](https://docs.mongodb.org/v3.4/tutorial/add-replica-set-arbiter/#considerations): 34 | > An arbiter does not store data, but until the arbiter’s mongod process is added to the replica set, the arbiter will act like any other mongod process and start up with a set of data files and with a full-sized journal. To minimize the default creation of data, you can disable journaling. 35 | 36 | ```sh 37 | docker run -d -p 27017:27017 -e JOURNLING=false yourname/mongo-rep-set:latest 38 | ``` 39 | 40 | #### Primary 41 | 42 | The primary is responsible for setting up users and configuring the replica set, so this is where all of the configuration happens. Once your secondary and arbiter are up and running, launch your primary with: 43 | 44 | ```sh 45 | docker run -d 46 | -p 27017:27017 \ 47 | -e MONGO_ROLE="primary" \ 48 | -e MONGO_SECONDARY="hostname or IP of secondary" \ 49 | -e MONGO_ARBITER="hostname or IP of arbiter" \ 50 | -e MONGO_ROOT_USER="myRootUser" \ 51 | -e MONGO_ROOT_PASSWORD="myRootUserPassword" \ 52 | -e MONGO_APP_USER="myAppUser" \ 53 | -e MONGO_APP_PASSWORD="myAppUserPassword" \ 54 | -e MONGO_APP_DATABASE="myAppDatabase" \ 55 | yourname/mongo-rep-set:latest 56 | ``` 57 | 58 | The primary will start up, configure your root and app users, shut down, and then start up once more and configure the replica set. Assuming the secondary and arbiter are reachable, the server will now be ready for authenticated connections. You can use the standard two server Mongo URL to connect to the primary/secondary like this: 59 | 60 | #### Connect 61 | 62 | Note that the following connection url is using default env var values (more info on those below), so it should work if you haven't overwritten any of the variables yourself. 63 | 64 | ```sh 65 | 66 | mongodb://myAppUser:myAppPassword@mongo1:27017,mongo2:27017/myAppDatabase?replicaSet=rs0 67 | ``` 68 | 69 | ## Environment Variables 70 | 71 | Here are all of the available environment variables and their defaults. Note that if you set `MONGO_REP_SET`, you must set it to the same value on all 3 containers. 72 | 73 | ```sh 74 | # mongod config 75 | MONGO_STORAGE_ENGINE wiredTiger 76 | MONGO_JOURNALING true 77 | MONGO_REP_SET rs0 78 | MONGO_AUTH true 79 | MONGO_OPLOG_SIZE # not set, but you can override the default 80 | MONGO_SECONDARY mongo2:27017 81 | MONGO_ARBITER mongo3:27017 82 | MONGO_DB_PATH /data/db 83 | 84 | # mongo root user 85 | MONGO_ROOT_USER root 86 | MONGO_ROOT_PASSWORD root123 87 | 88 | # mongo app user + database (user is given oplog access) 89 | MONGO_APP_USER myAppUser 90 | MONGO_APP_PASSWORD myAppPassword 91 | MONGO_APP_DATABASE myAppDatabase 92 | ``` 93 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | mongo1: 2 | restart: always 3 | environment: 4 | MONGO_ROLE: primary 5 | tty: true 6 | hostname: mongo1 7 | image: ongoworks/mongo-rep-set:latest 8 | links: 9 | - mongo2:mongo2 10 | - mongo3:mongo3 11 | stdin_open: true 12 | 13 | mongo2: 14 | restart: always 15 | tty: true 16 | hostname: mongo2 17 | image: ongoworks/mongo-rep-set:latest 18 | links: 19 | - mongo1:mongo1 20 | - mongo3:mongo3 21 | stdin_open: true 22 | 23 | mongo3: 24 | restart: always 25 | environment: 26 | JOURNALING: 'no' 27 | tty: true 28 | hostname: mongo3 29 | image: ongoworks/mongo-rep-set:latest 30 | links: 31 | - mongo1:mongo1 32 | - mongo2:mongo2 33 | stdin_open: true 34 | -------------------------------------------------------------------------------- /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ "${1:0:1}" = '-' ]; then 5 | set -- mongod "$@" 6 | fi 7 | 8 | # allow the container to be started with `--user` 9 | if [ "$1" = 'mongod' -a "$(id -u)" = '0' ]; then 10 | chown -R mongodb /data/configdb /data/db 11 | exec gosu mongodb "$BASH_SOURCE" "$@" 12 | fi 13 | 14 | if [ "$1" = 'mongod' ]; then 15 | numa='numactl --interleave=all' 16 | if $numa true &> /dev/null; then 17 | set -- $numa "$@" 18 | fi 19 | fi 20 | 21 | exec "$@" 22 | -------------------------------------------------------------------------------- /scripts/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | printf "\n[-] Installing base OS dependencies...\n\n" 6 | 7 | # base 8 | apt-get update 9 | apt-get install -y --no-install-recommends ca-certificates openssl numactl wget 10 | 11 | 12 | # Gosu 13 | # https://github.com/tianon/gosu 14 | dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" 15 | wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" 16 | wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" 17 | export GNUPGHOME="$(mktemp -d)" 18 | gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 19 | gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu 20 | rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc 21 | chmod +x /usr/local/bin/gosu 22 | gosu nobody true 23 | 24 | 25 | # generate a key file for the replica set 26 | # https://docs.mongodb.com/v3.4/tutorial/enforce-keyfile-access-control-in-existing-replica-set 27 | printf "\n[-] Generating a replica set keyfile...\n\n" 28 | openssl rand -base64 741 > $MONGO_KEYFILE 29 | chown mongodb:mongodb $MONGO_KEYFILE 30 | chmod 400 $MONGO_KEYFILE 31 | 32 | 33 | # install Mongo 34 | printf "\n[-] Installing MongoDB ${MONGO_VERSION}...\n\n" 35 | 36 | apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 0C49F3730359A14518585931BC711F9BA15703C6 37 | 38 | echo "deb http://repo.mongodb.org/apt/debian jessie/mongodb-org/$MONGO_MAJOR main" > /etc/apt/sources.list.d/mongodb-org.list 39 | 40 | apt-get update 41 | 42 | apt-get install -y \ 43 | ${MONGO_PACKAGE}=$MONGO_VERSION \ 44 | ${MONGO_PACKAGE}-server=$MONGO_VERSION \ 45 | ${MONGO_PACKAGE}-shell=$MONGO_VERSION \ 46 | ${MONGO_PACKAGE}-mongos=$MONGO_VERSION \ 47 | ${MONGO_PACKAGE}-tools=$MONGO_VERSION 48 | 49 | 50 | # cleanup 51 | printf "\n[-] Cleaning up...\n\n" 52 | 53 | rm -rf /var/lib/apt/lists/* 54 | rm -rf /var/lib/mongodb 55 | mv /etc/mongod.conf /etc/mongod.conf.orig 56 | 57 | apt-get purge -y --auto-remove openssl wget 58 | -------------------------------------------------------------------------------- /scripts/mongo_setup_repset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "************************************************************" 4 | echo "Setting up replica set" 5 | echo "************************************************************" 6 | 7 | gosu mongodb mongo admin --eval "help" > /dev/null 2>&1 8 | RET=$? 9 | 10 | while [[ RET -ne 0 ]]; do 11 | echo "Waiting for MongoDB to start..." 12 | gosu mongodb mongo admin --eval "help" > /dev/null 2>&1 13 | RET=$? 14 | sleep 1 15 | 16 | if [[ -f /data/db/mongod.lock ]]; then 17 | echo "Removing Mongo lock file" 18 | rm /data/db/mongod.lock 19 | fi 20 | done 21 | 22 | # Login as root and configure replica set 23 | gosu mongodb mongo admin -u $MONGO_ROOT_USER -p $MONGO_ROOT_PASSWORD --eval "rs.initiate();" 24 | gosu mongodb mongo admin -u $MONGO_ROOT_USER -p $MONGO_ROOT_PASSWORD --eval "rs.add('$MONGO_SECONDARY');" 25 | gosu mongodb mongo admin -u $MONGO_ROOT_USER -p $MONGO_ROOT_PASSWORD --eval "rs.addArb('$MONGO_ARBITER');" 26 | -------------------------------------------------------------------------------- /scripts/mongo_setup_users.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mongodb_setup_cmd="gosu mongodb mongod --storageEngine $MONGO_STORAGE_ENGINE" 4 | 5 | if [ ! -d "$MONGO_DB_PATH" ]; then 6 | mkdir -p $MONGO_DB_PATH 7 | fi 8 | 9 | chown -R mongodb:mongodb $MONGO_DB_PATH 10 | 11 | mongodb_setup_cmd="$mongodb_setup_cmd --dbpath $MONGO_DB_PATH" 12 | 13 | $mongodb_setup_cmd & 14 | 15 | fg 16 | 17 | 18 | gosu mongodb mongo admin --eval "help" > /dev/null 2>&1 19 | RET=$? 20 | 21 | while [[ RET -ne 0 ]]; do 22 | echo "Waiting for MongoDB to start..." 23 | gosu mongodb mongo admin --eval "help" > /dev/null 2>&1 24 | RET=$? 25 | sleep 1 26 | done 27 | 28 | echo "************************************************************" 29 | echo "Setting up users..." 30 | echo "************************************************************" 31 | 32 | # create root user 33 | gosu mongodb mongo admin --eval "db.createUser({user: '$MONGO_ROOT_USER', pwd: '$MONGO_ROOT_PASSWORD', roles:[{ role: 'root', db: 'admin' }]});" 34 | 35 | # create app user/database 36 | gosu mongodb mongo $MONGO_APP_DATABASE --eval "db.createUser({ user: '$MONGO_APP_USER', pwd: '$MONGO_APP_PASSWORD', roles: [{ role: 'readWrite', db: '$MONGO_APP_DATABASE' }, { role: 'read', db: 'local' }]});" 37 | 38 | 39 | echo "************************************************************" 40 | echo "Shutting down" 41 | echo "************************************************************" 42 | gosu mongodb mongo admin --eval "db.shutdownServer();" 43 | 44 | sleep 3 45 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -m 3 | 4 | if [ "$MONGO_ROLE" == "primary" ]; then 5 | $MONGO_SCRIPTS_DIR/mongo_setup_users.sh 6 | fi 7 | 8 | mongodb_cmd="gosu mongodb mongod --storageEngine $MONGO_STORAGE_ENGINE --keyFile $MONGO_KEYFILE" 9 | cmd="$mongodb_cmd --httpinterface --rest --replSet $MONGO_REP_SET" 10 | 11 | if [ "$MONGO_AUTH" == true ]; then 12 | cmd="$cmd --auth" 13 | fi 14 | 15 | if [ "$MONGO_JOURNALING" == false ]; then 16 | cmd="$cmd --nojournal" 17 | fi 18 | 19 | if [[ "$MONGO_OPLOG_SIZE" ]]; then 20 | cmd="$cmd --oplogSize $OPLOG_SIZE" 21 | fi 22 | 23 | if [ ! -d "$MONGO_DB_PATH" ]; then 24 | mkdir -p $MONGO_DB_PATH 25 | fi 26 | 27 | chown -R mongodb:mongodb $MONGO_DB_PATH 28 | 29 | $cmd --dbpath $MONGO_DB_PATH & 30 | 31 | if [ "$MONGO_ROLE" == "primary" ]; then 32 | $MONGO_SCRIPTS_DIR/mongo_setup_repset.sh 33 | fi 34 | 35 | fg 36 | --------------------------------------------------------------------------------