DEV Community

Cover image for Docker and Dancer
Dave Cross
Dave Cross

Posted on

Docker and Dancer

I have made a start on the project I mentioned in my last post. I decided that no matter what approach I'm going to take in order to move this web site into the cloud, I'm certainly going to want to run the main application in a Docker container - so that's what I have been concentrating on this week.

It turns out that I've tried this before. There was an existing Github repo containing a Dockerfile that was supposed to build an image for the application. It looks like I copied it from this blog post. I can't remember why I stopped working on that, but it's been pretty easy to get it up and running.

Here's the current version of the Dockerfile:

FROM        perl:latest
MAINTAINER  Dave Cross dave@perlhacks.org

RUN apt-get install openssl libssl-dev libz-dev libexpat1-dev
RUN curl -L http://cpanmin.us | perl - App::cpanminus
RUN cpanm Carton Starman

RUN git clone http://github.com/davorg/succession.git
RUN cd succession && carton install --deployment

EXPOSE 1701

WORKDIR succession
CMD carton exec starman --port 1701 Succession/bin/app.psgi
Enter fullscreen mode Exit fullscreen mode

Let's go through it in a some detail.

FROM        perl:latest
Enter fullscreen mode Exit fullscreen mode

Docker images tend to be built on top of other images. I've chosen an image which contains an up to date version of Perl.

MAINTAINER  Dave Cross dave@perlhacks.org
Enter fullscreen mode Exit fullscreen mode

When I'm happy with this image, I'll upload it to a public Docker hub. This line will allow people to get in touch with me about it.

RUN apt-get install openssl libssl-dev libz-dev libexpat1-dev
Enter fullscreen mode Exit fullscreen mode

RUN commands are run as the image is being built. Here we're loading some fairly standard libraries that underlie some of the Perl modules I'll be loading later. Actually, I'm not sure that these are necessary; they might well be included in the underlying image.

RUN curl -L http://cpanmin.us | perl - App::cpanminus
RUN cpanm Carton Starman
Enter fullscreen mode Exit fullscreen mode

Here, I'm installing cpanm and then using it to install Carton (a mechanism for installing known versions of Perl modules) and Starman (the software that we'll use to run our web service).

RUN git clone https://github.com/davorg/succession.git
Enter fullscreen mode Exit fullscreen mode

On this line we clone the latest version of the Github repo that contains the actual code for the web service.

RUN cd succession && carton install --deployment
Enter fullscreen mode Exit fullscreen mode

And here, we change into the newly-cloned subdirectory and use carton to install all of the Perl modules that we need.

EXPOSE 1701
Enter fullscreen mode Exit fullscreen mode

This line tells Docker that port 1701 on the container should be made available to the outside world.

WORKDIR succession
Enter fullscreen mode Exit fullscreen mode

WORKDIR sets the working directory that we use when the container starts up.

CMD carton exec starman --port 1701 Succession/bin/app.psgi
Enter fullscreen mode Exit fullscreen mode

And finally, CMD defines the command that is run when you start up a container using your new image. Here we're using a pretty standard command line that will start our Dancer application running under Starman.

Having created our Dockerfile, we can turn it into a Docker image by running the docker build command:

$ docker build -t succession .
Enter fullscreen mode Exit fullscreen mode

The -t succession gives the image a tag and the . means "use the Dockerfile in the current directory.

Once the command has finished, you'll have a new image. You can confirm that using the command docker image ls.

$ docker image ls
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
succession                latest              6a6fbfcbebbc        2 hours ago         1.17GB
perl                      latest              9a41aacaefa5        11 days ago         891MB
Enter fullscreen mode Exit fullscreen mode

But having a Docker image is only half the battle. We now need to create and run a container. And we can do this using docker run. The naive approach would be something like this:

$ docker run succession
Enter fullscreen mode Exit fullscreen mode

And that works - but it doesn't do anything useful. For many reasons.

Firstly, although we've asked Docker to expose port 1701 on the container (with the EXPOSE command in the Dockerfile) we haven't done anything useful with that port. We need to use the -p option to docker run in order to make that port visible on the host system. So our command becomes:

$ docker run -p 1701:1701 succession
Enter fullscreen mode Exit fullscreen mode

This makes port 1701 on the container available as port 1701 on the host.

And at this point, I was actually able to connect to my Dancer application (on http://localhost:1701/) running on the container. It was an error message, sure, but at least the communication was working.

The error message was interesting. As we've seen, the Dockerfile uses a Perl tool called Carton to ensure that all the correct libraries are installed. And that uses a file called cpanfile to work out what libraries are needed. All that was happening was that my cpanfile didn't have all of the required listed. I admit it took a few iterations to get that right!

There was one final problem. The application needs a database and nothing in the Dockerfile sets up that database. That's ok as I don't want the database running in the same container. But the application needs to know where its database is. It works that out by looking at some environment variables and those variables weren't being set in the container. So I had to use the -e option (multiple times) to pass the host's environment variables to the container. By this time, the docker run command was looking a bit complicated, so I wrote a bash script to make my life easier.

docker run --name succession \
  -e SUCC_DB_HOST \
  -e SUCC_DB_NAME \
  -e SUCC_DB_USER \
  -e SUCC_DB_PASS \
  -p 1701:1701 succession
Enter fullscreen mode Exit fullscreen mode

You'll see I've also added --name to give the container a name.

That looked a lot better. But I hadn't quite got to the bottom of all of the networking problems. The database host variable (SUCC_DB_HOST) was set to localhost. And on the container, that wasn't right. A quick Google and I discovered that the option --network="host" would allow the container to see the network from the host's perspective, so I could just change the setting to 127.0.0.1 and, suddenly, it all started working.

And that's where I currently am. After just a few hours work over the last week or so, I now have a working containerised version of my web application. This makes me happy. I feel that this could well work.

So, over to any Docker experts that might be reading this. Is what I'm doing sensible or have I missed some obvious improvements? All advice is welcome.

Update: I've already had two people giving me some really useful updates to this. It seems that the Dockerfile I borrowed heavily from wasn't really very good. I'll try and improve it over the next couple of days - in the meantime, please don't copy this version.

Top comments (4)

Collapse
 
domm profile image
domm

You can also pack all your ENV vars into an env file and then do docker --env-file path/to/file.env ...

Collapse
 
davorg profile image
Dave Cross

Yeah, I saw that, thanks. But the syntax I'm using passes existing environment variables to the container. As I understand it, the --env-file version requires me to hard-code the values in the file. And I'm not sure I want that.

Collapse
 
chrborup profile image
Christian Borup

I personally prefer using a database url. Thus only needing one environment variable. Obviously I need to parse it but you need that on services like heroku anyway...

Collapse
 
pplu profile image
Jose Luis Martinez

I like to keep build and runtime dependencies separate. Here I have an example of how to do it: github.com/pplu/performance-throug...