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
FROM perl:latest MAINTAINER Dave Cross email@example.com 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
Let's go through it in a some detail.
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 firstname.lastname@example.org
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
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
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
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
And here, we change into the newly-cloned subdirectory and use
carton to install all of the Perl modules that we need.
This line tells Docker that port 1701 on the container should be made available to the outside world.
WORKDIR sets the working directory that we use when the container starts up.
CMD carton exec starman --port 1701 Succession/bin/app.psgi
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 .
-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
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
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
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
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.