Friday, May 15, 2015

Series: How to create your own website based on Docker (Part 7 - Creating the mongodb Docker container)


Creating our mongodb database image

This is part 7 of the series: How to create your own website based on Docker.

It's about time to create our first image/container that is part of the real application stack. This container acts as persistence layer for the REST API (which we will create in the next part of this series). So the only component that talks to the database is the ioJS REST API container. In this part of the series, we'll have a look into how you can create your own mongodb container based on the official mongodb image.

Source code

All files mentioned in this series are available on Github, so you can play around with it! :)

Let's get started

Let's create a new directory called /opt/docker/mongodb/ and within this new directory we'll create two folders and one file:
# mkdir -p /opt/docker/mongodb/config/
# mkdir /opt/docker/mongodb/db/
# > /opt/docker/mongodb/Dockerfile
Since I don't want to re-invent the wheel, I'll have a look at the official mongodb Docker image and we're basically using the same mongodb 3.0 Dockerfile for our design. Since we want to run this mongodb database on our own Ubuntu Base Container, we need to make some changes to official mongodb Docker image.

The official mongodb Dockerfile uses Debian wheezy as base image, which is not what we want:
FROM debian:wheezy
We are going to use our own Ubuntu Base Image for the mongodb image and since we use Docker Compose, we must specify the correct base image name, which is a concatenation of "docker_" and the image name that we have specified in our docker-compose.yml - so in our case that would be "docker_ubuntubase". So we're changing the aforementioned line to use our base image:
# Pull base image.
FROM docker_ubuntubase 
Since the original Dockerfile only allows us to mount /data/db as volume, so we're extending it to also allow the mongodb log directory:

Replace the following line:
VOLUME /data/db
With this line:
VOLUME ["/data/db","/var/log/mongodb/"]
I'd like to have my configurations in a subfolder called "config", so we need to adjust another line:

Replace the following lines:
COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
With these lines:
COPY ./config/docker-entrypoint.sh /tmp/entrypoint.sh
RUN ["chmod", "+x", "/tmp/entrypoint.sh"]
ENTRYPOINT ["/tmp/entrypoint.sh"]
These lines copy a script called ./config/docker-entrypoint.sh to the /tmp/ folder in the container, make it executable and run it once, once the container has started. You can find the docker-entrypoint.sh file in the official mongodb docker repository on GitHub. Just copy that file into the config directory, which you have to create if you haven't done so already.

Let's create our own mongodb configuration file to set some parameters.

To do so, create a file called /opt/docker/mongodb/config/mongodb.conf and add the following lines (important: YAML does not accept tabs; use spaces instead!):
systemLog:
   destination: file
   path: "/var/log/mongodb/mongodb-projectwebdev.log"
   logAppend: true
storage:
   journal:
      enabled: true
net:
   port: 3333
   http:
       enabled: false
       JSONPEnabled: false
       RESTInterfaceEnabled: false
Now add the following lines to your Dockerfile to copy our new custom config file to our image:
RUN mkdir -p /var/log/mongodb && chown -R mongodb:mongodb /var/log/mongodb
COPY ./config/mongodb.conf /etc/mongod.conf
Since we want to load our custom config now, we'll need to changed the way we start mongodb, so we change the following line from
CMD ["mongod"]
to
CMD ["mongod", "-f", "/etc/mongod.conf"]
Our folder structure must look like this now:
+-- /opt/docker/mongodb
¦   +-- config
¦   ¦   +-- docker-entrypoint.sh
¦   ¦   +-- mongodb.conf
¦   +-- db
¦   +-- Dockerfile
Another thing we can remove is the EXPOSE instruction, since we already specified that in our docker-compose.yml.

So the complete Dockerfile will look like this now:
# Pull base image.
FROM docker_ubuntubase

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mongodb && useradd -r -g mongodb mongodb

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates curl \
numactl \
&& rm -rf /var/lib/apt/lists/*

# grab gosu for easy step-down from root
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4
RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/1.2/gosu-$(dpkg --print-architecture)" \
&& curl -o /usr/local/bin/gosu.asc -SL "https://github.com/tianon/gosu/releases/download/1.2/gosu-$(dpkg --print-architecture).asc" \
&& gpg --verify /usr/local/bin/gosu.asc \
&& rm /usr/local/bin/gosu.asc \
&& chmod +x /usr/local/bin/gosu

# gpg: key 7F0CEB10: public key "Richard Kreuter <richard@10gen.com>" imported
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 492EAFE8CD016A07919F1D2B9ECBEC467F0CEB10

ENV MONGO_MAJOR 3.0
ENV MONGO_VERSION 3.0.3

RUN echo "deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/$MONGO_MAJOR main" > /etc/apt/sources.list.d/mongodb-org.list

RUN set -x \
&& apt-get update \
&& apt-get install -y mongodb-org=$MONGO_VERSION \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/lib/mongodb \
&& mv /etc/mongod.conf /etc/mongod.conf.orig

RUN mkdir -p /data/db && chown -R mongodb:mongodb /data/db
RUN mkdir -p /var/log/mongodb && chown -R mongodb:mongodb /var/log/mongodb

VOLUME ["/data/db","/var/log/mongodb/"]

COPY ./config/docker-entrypoint.sh /tmp/entrypoint.sh
COPY ./config/mongodb.conf /etc/mongod.conf
RUN ["chmod", "+x", "/tmp/entrypoint.sh"]

ENTRYPOINT ["/tmp/entrypoint.sh"]

CMD ["mongod", "-f", "/etc/mongod.conf"]
Source: https://github.com/mastix/project-webdev-docker-demo/blob/master/mongodb/Dockerfile

This is pretty much it. That's all we need to create our mongodb database container, that will run on the default port 3333 - but will only be accessible from the REST API, since we linked that container to the ioJS REST API container only, see our docker-compose.yml file again:
projectwebdevapi:
    build: ./projectwebdev-api
    expose:
        - "3000"
    links:
        - mongodb:db

    volumes:
        - ./logs/:/var/log/supervisor/
        - ./projectwebdev-api/app:/var/www/html
In the next chapter it's getting more interesting: Let's create our REST API container, that talks to the mongodb container!

6 comments:

  1. Hello Sascha

    I am trying to follow this and my mongodb service dies every time. Looking at the logs I see a message
    `F CONTROL Failed global initialization: FileNotOpen Failed to open "/var/log/mongodb/mongodb-projectwebdev.log"`.

    Did you encounter this? My docker-compose version is 1.3.3

    Thanks
    -Venu

    ReplyDelete
    Replies
    1. Hey Venu, this pretty much looks like a permission issue on your Docker Host. Please check your permissions on /var/log/mongodb...

      Delete
  2. Nobody ever knew creating a website has so many processes and having read some of them, this is quite impressive considering you write like a pro yet you try to keep it simple for every layman to understand. I also like the fact that you have categorized and separated the process into parts which makes it easier for those learning to follow through.

    Lavonne @ Optimal RoI

    ReplyDelete
  3. Interesting blog - wouldn't normally use mongodb - I've used MySQL before but have been considering switching to mongodb lately as it's more lightweight and supposed to be simpler. If I get around to it I'll definitely try and follow the directions here as they seem to be pretty clearly laid out and easy to follow . Famous last words, perhaps!

    Edwin @ Clicks In Motion

    ReplyDelete
  4. Ever since the internet took over the world, it has become more and more important for people to know how to do things like build their own website. These days, it seems like everyone has a website for themselves, their business, or both. This is a great post explaining how to go about building a website in a simple and easy way.

    Caroline @ Strategic Info Tech

    ReplyDelete