Installing MongoDB with Docker on Ubuntu Linux 14.04

View recent blog entries

This post describes installing MongoDB (and backing it up and restoring from backup) in a Docker container on an Ubuntu Linux 14.04 virtual machine. (Update: 2017-05-24 we've just published an easier way to run MongoDB and Rocket.Chat and Wekan which depend on it)

The motivation for installing MongoDB is that it is the key-value based data container engine used by two compelling web technologies, Rocket.Chat and Wekan, that the OER Foundation has deployed for the OERu as https://chat.oeru.org and https://plan.oeru.org.

Installing Docker

Rather than trying to write a comprehensive guide to installing Docker, I'll instead recommend that you have a look at this one provided by the Docker community. It also covers installing on other versions of Ubuntu which might be helpful for some. Once you've got a working Docker container install going on your server, you can continue to the next step...

Note: the idea with Docker is that nothing important is stored in the container itself. That means that you can blow it away at any time without losing any important data. You can do that to quickly upgrade it, or to change its properties. You simply re-launch the container making sure it points properly to the persistent stuff you want it to use.

Installing MongoDB

Once you've got Docker working on your virtual machine, getting the MongoDB docker container (where it's preinstalled and ready to go) is simple:

docker pull mongo

This will install the latest version of the MongoDB container from docker.io.

Launching MongoDB

I recommend setting up a per-container user to help keep things well segmented. You can create a "mongo" user:

sudo adduser mongo

This creates a directory /home/mongo and that's where we'll get the Mongo container to store the data we want to keep persistent.

We can create "data" and "backups" directories in which it should store its data and save its backups respectively:

sudo mkdir /home/mongo/data
sudo mkdir /home/mongo/backup
s

Then you can launch the container:

docker run --name mongodb --restart unless-stopped \
-v /home/mongo/data:/data/db -v /home/mongo/backups:/backups -d mongo --smallfiles

You should be able to check it's running by typing

docker ps

and looking for the reference to "mongodb" (that's the "name" we've assigned to this instance of the "mongo" container).

You can even log into the container and poke around the filesystem if you like, using the container's "id":

ID=`docker ps | grep mongo | cut --characters=1-12`
docker exec -it $ID bash

which will drop you at the command line in the container. You can exit with "exit" or CTRL-D.

If things go wrong, you can run this:

ID=`docker ps | grep mongo | cut --characters=1-12`
docker inspect $ID | grep log

Which will give you a result like this (your filenames will be different):

"LogPath": "/var/lib/docker/containers/539072b8fb976b5048bd13e90074ea4b43166b81c3d69d1720b4746785f7d917/539072b8fb976b5048bd13e90074ea4b43166b81c3d69d1720b4746785f7d917-json.log",

In which case, you can look at the log file to see what's gone wrong:

less +G /var/lib/docker/containers/539072b8fb976b5048bd13e90074ea4b43166b81c3d69d1720b4746785f7d917/539072b8fb976b5048bd13e90074ea4b43166b81c3d69d1720b4746785f7d917-json.log

You can exit less by hitting "q" or CTRL-C.

Backing up MongoDB

Backing up MongoDB requires a somewhat circuitous process. Here's what I do (after quite a bit of head scratching and web searching - Note: these backups are not directly related to the "backups" directory created above. More on that in the script below.):

On the host VM, I create a directory for backups - I use /home/backup

sudo mkdir /home/backup

and I create a bin directory in /home/mongo

sudo mkdir /home/mongo/bin

and I create a script file with my preferred editor (I use vim, you could use nano instead if vim scares you):

sudo vim /home/mongo/bin/mongo_backup.sh

In it, I put the following:

#!/bin/bash
#
# backup all the mongo DBs hosted on this server, in a docker container
#
# useful reference: https://docs.mongodb.org/manual/core/backups/
#
# in event of a disaster, this could be useful:
# https://docs.mongodb.org/manual/tutorial/recover-data-following-unexpec…
#
# script copyright dave@davelane.nz - available under terms of GPL v2 or later,
# see https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
#
# Note: I've commented out some "echo" statements below that might be useful if you're
# debugging this script...
DATE=`date '+%Y%m%d-%H%M%S'`
# this is the local backup directory - gets cleared out after every backup...
DIR=/home/mongo/backups
# the archive dir, with a past archive as well.
ARCHDIR=/home/backup/mongodb
PREV=$ARCHDIR/previous
LATEST=$ARCHDIR/latest
#
# this is the backup directory on the Docker host
DDIR=/backups

# 1. get mongo container (this assumes only one running on the machine, with "mongo" in is name!)
ID=`docker ps | grep "mongo" | cut -c 1-12`
##echo "working with ID = $ID"
#
# 2. back up databases
# 2.1 backup to the local $DDIR on the Docker host
MSG=`docker exec -i $ID mongodump --quiet --out $DDIR`
##echo $MSG
# go into local backup dir
CWD=`pwd`
cd $DIR
# 2.2. get list of backup databases
DBS=`find $DIR -mindepth 1 -maxdepth 1 -type d -exec basename {} \;`
for DB in $DBS
do
    ARCH="${DB}.tgz"
    if [ -f $PREV/$ARCH ] ; then
        ##echo "explicitly jettison old backup of $PREV/$ARCH"
        rm $PREV/$ARCH
    else
        echo "***no previous version of $ARCH in $PREV on $DATE"
    fi
    if [ -f $LATEST/$ARCH ] ; then
        ##echo "move previous $LATEST/$ARCH into $PREV (overwritting old)"
    mv $LATEST/$ARCH $PREV/$ARCH
    else
        echo "***no latest backup $ARCH in $LATEST on $DATE"
    fi
    ##echo "archiving contents of $DB into $LATEST/$ARCH"
    tar cvfz $LATEST/$ARCH $DB
    #echo "removing $DB *.json and *.bson and the dir"
    rm -f $DB/*.bson $DB/*.json
    rmdir $DB
done
# return to where we started
cd $CWD
# exit with a happy 0, which shows we were successful!
exit 0

Save that file, and then run this to fix the ownership of the file and make the script executable:

sudo chown -R mongo:mongo /home/mongo/*
sudo chmod a+x /home/mongo/bin/mongo_backup.sh

You can try running the script to make sure it creates a backup of your mongo database (such as it is! There might not be much there yet, if you haven't got any apps installed that use MongoDB - you can wait until after that before testing. If you run it twice, you should see both a "latest" and "previous" backup directory containing suitably dated directories - this provides extra data security):

sudo /home/mongo/bin/mongo_backup.sh

Once you're happy that's working, set up a cron job to make it run daily... Again, edit a file with your preferred text editor:

sudo vim /etc/cron.d/mongodb-backup

and fill it with the following (replacing [an email that will get to you!] appropriately):

#
# cron.d/mongodb-backup -- backups up the Mongo DBs running in a docker container
#
#
MAILTO=[and email that will get to you!]

# run daily at 12:17pm as the root user
17 12 * * *  root  /home/mongo/bin/mongo_backup.sh

Recovering a MongoDB database

In the event that you ever have to recover or transfer a MongoDB database, aka container, you can do so like this (sort of the reverse of the backup script):

work out the name of the container you want to restore and copy the backup of the database directory (e.g. rocketchat or wekan) into /home/mongo/backups - to be clear: for MongoDB, a "database" is represented as a directory of files holding all the relevant data.

Then log into Docker instance:

ID=`docker ps | grep mongo | cut --characters=1-12`
docker exec -it $ID bash

and on the Docker container's command line run this, replacing the [names] appropriately below. Note, you might want to change the database name from the backup's original to a new name, so they don't need to be the same (but they can be):

mongorestore --db [recovered DB Name] --drop /backups/[backed up DB Name]/

Upgrading MongoDB

Note, if you have other apps in other containers that depend on MongoDB, it's usually most convenient to upgrade them all at the same time. Look at our instructions for, for example, running Wekan, to see how to upgrade MongoDB.