diff --git a/bitnami/mongodb/README.md b/bitnami/mongodb/README.md index 66370acfc63a..8240c479aa0c 100644 --- a/bitnami/mongodb/README.md +++ b/bitnami/mongodb/README.md @@ -119,6 +119,76 @@ myapp: Inside `myapp`, use `mongodb` as the hostname for the MongoDB server. +## Setting up a replicaSet + +A [replica set](https://docs.mongodb.com/manual/replication/) can easily be setup with the Bitnami MongoDB Docker Image using the following environment variables: + + - `MONGODB_REPLICASET_MODE`: MongoDB replica set mode. Possible values `primary`,`secondary` or `arbiter`. No defaults. + - `MONGODB_REPLICASET_NAME`: MongoDB replica set name. Defaults to `replicaset`. + - `MONGODB_PRIMARY_HOST`: MongoDB primary host. No defaults. + - `MONGODB_PRIMARY_PORT`: MongoDB primary port. Defaults to `27017` + - `MONGODB_PRIMARY_USER`: MongoDB primary user. Defaults to `root`. + - `MONGODB_PRIMARY_PASSWORD`: MongoDB primary user password. No defaults. + +In a replica set you can have one primary and zero or more secondary nodes. + +### Step 1: Create the primary node + +The first step is to start the MongoDB primary node. + +```bash +docker run --name mongodb-primary \ + -e MONGODB_REPLICASET_MODE=primary \ + bitnami/mongodb:latest +``` + +In the above command the container is configured as the `primary` node using the `MONGODB_REPLICASET_MODE` parameter. + +### Step 2: Create the secondary node + +Next we start a MongoDB secondary node. + +```bash +docker run --name mongodb-secondary \ + --link mongodb-primary:primary \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=primary \ + -e MONGODB_PRIMARY_PORT=27017 \ + bitnami/mongodb:latest +``` + +In the above command the container is configured as a `secondary` using the `MONGODB_REPLICASET_MODE` parameter. The `MONGODB_PRIMARY_HOST` and `MONGODB_PRIMARY_PORT` parameters are used connect to the MongoDB primary. + +You now have a two node MongoDB replica set which can be scaled by adding/removing secondary nodes. + +With Docker Compose the replica set can be setup using: + +```yaml +primary: + image: bitnami/mongodb:latest + environment: + - MONGODB_REPLICASET_MODE=primary + +secondary: + image: bitnami/mongodb:latest + links: + - primary:primary + environment: + - MONGODB_REPLICASET_MODE=secondary + - MONGODB_PRIMARY_HOST=primary + - MONGODB_PRIMARY_PORT=6379 +``` + +Scale the number of secondary nodes using: + +```bash +docker-compose scale primary=1 secondary=3 +``` + +The above command scales up the number of secondary nodes to `3`. You can scale down in the same way. + +> **Note**: You should not scale up/down the number of primary nodes. Always have only one primary node running. + # Configuration ## Setting the root password on first run diff --git a/bitnami/mongodb/rootfs/app-entrypoint.sh b/bitnami/mongodb/rootfs/app-entrypoint.sh index e76997d4c379..e9da9e5f55a1 100755 --- a/bitnami/mongodb/rootfs/app-entrypoint.sh +++ b/bitnami/mongodb/rootfs/app-entrypoint.sh @@ -8,7 +8,13 @@ if [[ "$1" == "harpoon" && "$2" == "start" ]]; then ${MONGODB_ROOT_PASSWORD:+--rootPassword $MONGODB_ROOT_PASSWORD} \ ${MONGODB_USER:+--username $MONGODB_USER} \ ${MONGODB_PASSWORD:+--password $MONGODB_PASSWORD} \ - ${MONGODB_DATABASE:+--database $MONGODB_DATABASE} + ${MONGODB_DATABASE:+--database $MONGODB_DATABASE} \ + ${MONGODB_REPLICASET_MODE:+--replicaSetMode $MONGODB_REPLICASET_MODE} \ + ${MONGODB_REPLICASET_NAME:+--replicaSetName $MONGODB_REPLICASET_NAME} \ + ${MONGODB_PRIMARY_HOST:+--primaryHost $MONGODB_PRIMARY_HOST} \ + ${MONGODB_PRIMARY_PORT:+--primaryPort $MONGODB_PRIMARY_PORT} \ + ${MONGODB_PRIMARY_USER:+--primaryUser $MONGODB_PRIMARY_USER} \ + ${MONGODB_PRIMARY_PASSWORD:+--primaryPassword $MONGODB_PRIMARY_PASSWORD} fi chown $BITNAMI_APP_USER: /bitnami/$BITNAMI_APP_NAME || true fi diff --git a/bitnami/mongodb/test.sh b/bitnami/mongodb/test.sh index 6777bd454f47..573fc03c4154 100755 --- a/bitnami/mongodb/test.sh +++ b/bitnami/mongodb/test.sh @@ -8,7 +8,7 @@ MONGODB_PASSWORD=test_password APP_NAME=mongodb VOL_PREFIX=/bitnami/$APP_NAME VOLUMES=$VOL_PREFIX -SLEEP_TIME=30 +SLEEP_TIME=60 load tests/docker_helper # Link to container and execute mongo client @@ -21,6 +21,7 @@ mongo_client() { # Cleans up all running/stopped containers and host mounted volumes cleanup_environment() { container_remove_full default + container_remove_full secondary } # Teardown called at the end of each test @@ -168,3 +169,123 @@ cleanup_environment run mongo_client default -u $MONGODB_USER -p $MONGODB_PASSWORD $MONGODB_DATABASE --eval "printjson(db.getCollectionNames())" [[ "$output" =~ '"users"' ]] } + +@test "Can't create replicaSet with authentication" { + run container_create default \ + -e MONGODB_REPLICASET_MODE=primary \ + -e MONGODB_PASSWORD=$MONGODB_PASSWORD + [[ "$output" =~ "Not possible to configure replica set scenario using authentication" ]] +} + +@test "Can't create secondary replicaSet node without specifying MONGODB_PRIMARY_HOST" { + run container_create secondary \ + -e MONGODB_REPLICASET_MODE=secondary + [[ "$output" =~ "provide the --primaryHost property" ]] +} + +@test "Can create replicaSet without authentication" { + container_create default -d \ + -e MONGODB_REPLICASET_MODE=primary + + container_create secondary -d \ + $(container_link default $CONTAINER_NAME) \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=$CONTAINER_NAME + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('users'))" + [[ "$output" =~ '"ok" : 1' ]] + + sleep 3 + run mongo_client secondary $MONGODB_DATABASE --eval "rs.slaveOk(); printjson(db.getCollectionNames())" + [[ "$output" =~ '"users"' ]] +} + +@test "Secondary node in replicaSet synchronizes with the primary (delayed start)" { + container_create default -d \ + -e MONGODB_REPLICASET_MODE=primary + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('users'))" + [[ "$output" =~ '"ok" : 1' ]] + + container_create secondary -d \ + $(container_link default $CONTAINER_NAME) \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=$CONTAINER_NAME + + run mongo_client secondary $MONGODB_DATABASE --eval "rs.slaveOk(); printjson(db.getCollectionNames())" + [[ "$output" =~ '"users"' ]] +} + +@test "replicaSet setup and state is preserved after restart" { + container_create default -d \ + -e MONGODB_REPLICASET_MODE=primary + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('users'))" + [[ "$output" =~ '"ok" : 1' ]] + + container_create secondary -d \ + $(container_link default $CONTAINER_NAME) \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=$CONTAINER_NAME + + container_restart secondary + container_restart default + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('networks'))" + [[ "$output" =~ '"ok" : 1' ]] + + sleep 3 + run mongo_client secondary $MONGODB_DATABASE --eval "rs.slaveOk(); printjson(db.getCollectionNames())" + [[ "$output" =~ '"users"' ]] + [[ "$output" =~ '"networks"' ]] +} + +@test "replicaSet setup and state is preserved after deletion" { + container_create_with_host_volumes default -d \ + -e MONGODB_REPLICASET_MODE=primary + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('users'))" + [[ "$output" =~ '"ok" : 1' ]] + + container_create_with_host_volumes secondary -d \ + $(container_link default $CONTAINER_NAME) \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=$CONTAINER_NAME + + container_remove secondary + container_remove default + + container_create_with_host_volumes default -d + container_create_with_host_volumes secondary -d $(container_link default $CONTAINER_NAME) + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('networks'))" + [[ "$output" =~ '"ok" : 1' ]] + + sleep 3 + run mongo_client secondary $MONGODB_DATABASE --eval "rs.slaveOk(); printjson(db.getCollectionNames())" + [[ "$output" =~ '"users"' ]] + [[ "$output" =~ '"networks"' ]] +} + +@test "Slave recovers if master is temporarily offine" { + container_create_with_host_volumes default -d \ + -e MONGODB_REPLICASET_MODE=primary + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('users'))" + [[ "$output" =~ '"ok" : 1' ]] + + container_create_with_host_volumes secondary -d \ + $(container_link default $CONTAINER_NAME) \ + -e MONGODB_REPLICASET_MODE=secondary \ + -e MONGODB_PRIMARY_HOST=$CONTAINER_NAME + + container_restart default + + run mongo_client default $MONGODB_DATABASE --eval "printjson(db.createCollection('networks'))" + [[ "$output" =~ '"ok" : 1' ]] + + sleep 3 + run mongo_client secondary $MONGODB_DATABASE --eval "rs.slaveOk(); printjson(db.getCollectionNames())" + [[ "$output" =~ '"users"' ]] + [[ "$output" =~ '"networks"' ]] +}