• Ian Stuart's picture

    Building a web server in a Docker container

    Ian Stuart / April 24, 2015
  • The basic premise is to create a Docker container such that it can be spun up relatively quickly, and relatively easily, and used as a production server.

    The basis for this work starts with a blog post by Robert Norris - wherein he creates a Dancer-based web service in a container.

    In my case, I'm taking an existing Apache web server based system, and simply (ha!) moving it to a container. This is NOT as simple as moving it to a virtual machine, however it afforded me the opportunity to spend a few weeks getting to grips with Docker & containers.

    The first thing I did was realise that the build process was actually in two parts:

    1. There's a generic Apache Mod-Perl base which could be common to a number of different services, and
    2. There's the service-specific code 

    Apache Mod-Perl Docker image

    The first thing to do, therefore, is make the base Apache Mod-Perl image. The Dockerfile is given below:

    FROM ubuntu:latest
    
    MAINTAINER Ian Stuart <Ian.Stuart@ed.ac.uk>
    
    ## Make sure Ubuntu is up-to-date
    RUN apt-get update
    RUN apt-get -y upgrade
    
    # Install Apache, mod-perl, and a bunch of perl libraries
    RUN DEBIAN_FRONTEND=noninteractive apt-get -y install apache2 libapache2-mod-perl2 make curl git gcc 
    # Enable apache mods.
    RUN a2enmod perl
    RUN a2enmod rewrite
    
    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libxml-libxml-perl libxml-twig-perl libxml-simple-perl libwww-mechanize-perl libfile-slurp-perl libgetopt-long-descriptive-perl liblwp-useragent-determined-perl libdata-printer-perl libtest-strict-perl cpanminus
    

    This is fairly simple:

    Start by taking an Ubuntu system:

    FROM ubuntu:latest

    Make sure it's up-to-date (as of build-time):

    RUN apt-get update
    RUN apt-get -y upgrade

    Install Apache & Mod-Perl:

    RUN DEBIAN_FRONTEND=noninteractive apt-get -y install apache2 libapache2-mod-perl2 make curl git gcc 
    # Enable apache mods.
    RUN a2enmod perl
    RUN a2enmod rewrite
    

    Install a set of perl modules I consider to be "core" (XML manipulation; web User Agents; etc... and CPAN-Minus); 
    From here, we use the standard perl "Bundle" system to pull in Perl packages on a per-service basis

    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libxml-libxml-perl libxml-twig-perl libxml-simple-perl libwww-mechanize-perl libfile-slurp-perl libgetopt-long-descriptive-perl liblwp-useragent-determined-perl libdata-printer-perl libtest-strict-perl cpanminus
    

    Build the image

    docker build -no-cache --force-rm=true -t edina/apache-mod-perl:2.1 .

    ... and make it the latest version:

    docker tag $$build-code$$ edina/apache-mod-perl:latest

    Service image

    This is a more complex build.... but follows the same basic process.

    There are some complications that threw in some challenges

    • The Perl PostgreSQL connector requires some devel packages
    • The RUN command seems to do weird things with what it remembers (see all those apt-get updates?)

    • Because the container has volatile disk space, various database & log files need to be stored outside the container

    The Docker file

    The Dockerfile breaks down thus:

    FROM edina/apache-mod-perl
    VOLUME /var/log/apache2

    Takes the Apache Mod-Perl image we created above, and creates a flag that allows us to mount external (persistent) disk-space for the log files

     

    RUN groupadd -r postgres && useradd -r -g postgres postgres && mkdir -p /var/run/postgresql && chown -R postgres /var/run/postgresql

    Add the user & group, and directories needed by PostgreSQL

     

    RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
    	&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
    ENV LANG en_US.utf8
    

    Make sure we're using utf8 (we want our PostgreSQL connections to be uft8 enabled

     

    ENV PG_MAJOR 9.3
    
    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
    		postgresql-$PG_MAJOR \
    		postgresql-contrib-$PG_MAJOR \
    		postgresql-common \
        postgresql-server-dev-$PG_MAJOR 
    
    ENV PATH /usr/lib/postgresql/$PG_MAJOR/bin:$PATH
    ENV PGDATA /var/lib/postgresql/data
    
    

    Install PostgreSQL.
    Note that there's no volume export here: we don't need it as we're not actually going to RUN postgres

     

    ADD Ori.pm /usr/lib/perl5/Bundle/
    RUN perl -MCPAN -e 'install Bundle::Ori'

    Install the Perl Packages we want
    (Ori.pm is a CPAN Bundle file [see http://perl.bristolbath.org/blog/lyle/2009/01/writing-a-cpan-bundle.htmlhttp://search.cpan.org/dist/CPAN/lib/CPAN.pm#Bundles]; and it's copied into the perl library path)

    Add our version of the cgi-bin configuration file (it's got some additional scriptAlias lines) & enable cgi processing

    ADD serve-cgi-bin.conf /etc/apache2/conf-available/
    RUN ln -s /etc/apache2/mods-available/cgi.load /etc/apache2/mods-enabled/cgi.load
    

    Add our content to the default locations of the system web-server:

    COPY public_html/ /var/www/html/
    RUN chown -R www-data:www-data /var/www/html/*
    ADD cgi-bin/ /usr/lib/cgi-bin/
    ADD site_perl /usr/local/lib/site_perl/

    Finally add our test suite & run it:

    RUN mkdir /testing
    ADD testing /testing/
    RUN prove testing/*.t