The Hyperpessimist

The grandest failure.

Dockerizing Meteor

In case you were wondering, what the deal with CoreOS was: I wasn’t planning on leaving the stuff running as is, the idea is to actually run some containers. After wasting some 3 or 4 hours testing, here’s how to get your Meteor app running in Docker.

The setup

First off, you need a database. Meteor can use any database as long as it is MongoDB. Not my first choice, but after all, this is someone else’s baby that I’m setting up.

Setting up a MongoDB is pretty easy, due to the fact that a pre-made official image is available which works pretty alright. Just do a docker run -d --name db mongodb and wait a few minutes, until the images are loaded and set up.

Now, we want to run Meteor. I don’t have much experience with Meteor, but the idea is that you compile everything to plain JS you can just run it via Node.js. So, I took some inspiration from the rocker-docker Dockerfile. I put this stuff as Dockerfile into my Git repo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM node
MAINTAINER Marek Kubica <marek@xivilization.net>

RUN npm cache clean -f && npm install -g n && n 0.10.30
RUN curl https://install.meteor.com/ | sh
RUN npm install --silent -g forever meteorite

ADD . ./meteorsrc
WORKDIR /meteorsrc

RUN mrt install && meteor bundle --directory /var/www/app
WORKDIR /var/www/app

RUN npm install phantomjs

ENV PORT 8080
ENV ROOT_URL http://127.0.0.1
ENV MONGO_URL mongodb://db:27017/test

EXPOSE 8080

RUN touch .foreverignore
CMD forever -w ./main.js

This starts off with the official Nodejs image, installs a newer Nodejs (since Meteor needs at least 0.10.28 or so, which is newer than the node that is packaged), installs Meteor and some helper packages. Then I am copying all the Git repo contents to /meteorsrc in the image. Not the cleanest location, but I just wanted something quick that works. Then I run mrt install which presumably installs some Meteor stuff. The important part is meteor bundle which creates a compiled copy of all Meteor resources in /var/www/app that can be served by Nodejs.

Then it is mostly environment variables and stuff. What is important is the MONGO_URL, as you notice it points not to localhost but to db. Docker isolates containers from each other, so the communication between containers is kind of like between different hosts.

To create it, run docker build -t yourusername/reponame:tag . which will start the build. This might take a while. Afterwards I pushed it to the Docker registry with docker push yourusername/repo:tag, so I can get it to the server in some more or less convenient way.

On the server, I did docker run -d -p 80:8080 --name web --link db:db yourusername/repo:tag. Let me explain that for a bit. What I do here is to run the container which I just built, name it web. The interesting part is the 80:8080 part which means that whatever the server gets on port 80 will be forwarded to the Meteor app running in the container on port 8080. Next, I am telling Docker to expose the db container (running MongoDB) to the new container using the db alias. So the web container can access db via the hostname db.

Observations

The idea with the containers is actually pretty neat, I like that setting up a MongoDB container requires zero setup and I can set up as many of them as I like without having to care about isolation. What I liked less is that the Nodejs container is outdated enough that I need to install a newer Node anyway, so what is even the point? I hope this will get fixed in the future. What I also didn’t like very much is that creating the image takes so long. Every step in the Dockerfile is another layer in the image and creating those takes way too long.

Next steps

I hope the node image will finally be updated to 0.10.29 or 0.10.30 which saves me from a lengthy Node setup. I will also try to reduce the build times by decreasing the amount of commands in the Dockerfile (PORT is one obvious candidate to be deleted). What I am also less than happy is the fact that I am building the code on my laptop, then uploading the image to the Docker registry (~250 MB, which takes a couple of minutes) and then download it again on my server. Not sure what the best way to go about this will be, maybe setting up a registry on the server, or maybe even building the image directly on the server.

Also, the Docker user guide is rather pretty damn good. Need to read more.