Category Archives: Docker

Adventures with Containerization: Fedora, Docker and httpd

I have finally got around to experimenting with Docker this week.   Thanks to the hard work of many wonderful hackers, since late last year we’ve had Docker packages working in Fedora (under the package name “docker-io”).  Matt Miller has also been pushing Fedora images to the Docker index.

How can we use Apache httpd with Docker in Fedora?    One simple use-case might be serving a mostly static web site which you want to isolate inside a Docker container.  This is what you might aim for if you have a server hosting a bunch of different web sites for unrelated customers, but you want isolation between those servers.  If one httpd gets compromised, you don’t want them all to go down.

Most of the hard work is already done here.  We need two things:

  1. A Docker image which can launch httpd, serving our web site.
  2. A way to control that Docker container from the host side.

A Dockerfile is a script which describes how to create a docker image.  Here’s my Dockerfile:

# Clone from the Fedora 20 image
FROM fedora:20
# Install httpd
RUN yum install -y httpd
# Change the default docroot
RUN sed -i 's|^DocumentRoot.*|DocumentRoot "/srv"|' /etc/httpd/conf/httpd.conf
# Add in our custom httpd configuration.
ADD extra.conf /etc/httpd/conf.d/root.conf
# Start up httpd.
ENTRYPOINT /usr/sbin/httpd -DFOREGROUND

The first line means this image begins as a clone of the standard Fedora 20 installation.  We only make a few modifications: we install the httpd package, change the default docroot to /srv, and add in a custom configuration file.  The “ENTRYPOINT” line means that httpd is the process invoked by default when containers are created from this image.  The custom configuration file, “extra.conf” I use is this, which relaxes httpd’s access control for the /srv directory:

<Directory /srv>
   Require all granted
</Directory>

Combining that and the change to the DocumentRoot, this image is set up to serve whatever is mounted at /srv.  Placing the two files described above in an empty directory I create a new Docker image as follows – note the argument passed tags the image with the name “httpd-test1”:

# docker build -t httpd-test1 .
Uploading context 10.24 kB
Uploading context 
Step 1 : FROM fedora:20
 ---> 6572f78e5fa5
Step 2 : RUN yum install -y httpd
 ---> Using cache
 ---> 44b5498e707a
Step 3 : RUN echo > /etc/httpd/conf.d/welcome.conf
 ---> Running in c91f82b99bce
 ---> c5a629660e53
Step 4 : RUN sed -i 's|^DocumentRoot.*|DocumentRoot "/srv"|' /etc/httpd/conf/httpd.conf
 ---> Running in 67fe1b96bf51
 ---> 26ad2ac0ebf8
Step 5 : ADD extra.conf /etc/httpd/conf.d/root.conf
 ---> cfe43ce7417a
Step 6 : ENTRYPOINT /usr/sbin/httpd -DFOREGROUND
 ---> Running in 05a4a1154fb7
 ---> f7d8712877b8
Successfully built f7d8712877b8

The remaining piece of the puzzle is integration on the host side.   We could rely on standard Docker tools for this, but let’s do it properly.  I’m going to place my example site, imaginatively named “example.com”, in /srv/example.com, as follows:

# mkdir /srv/example.com
# echo '<h1>Hello,  World</h1>' > /srv/example.com/index.html

Now I can set up a systemd service file which launches a Docker container using my image, and launch the container:

# cat > /etc/systemd/system/example.com.service <<EOF
[Unit]
Description=example.com Container
After=docker.service

[Service]
Type=simple
ExecStart=/usr/bin/docker run -v /srv/example.com:/srv httpd-test1
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
# systemctl daemon-reload 
#

And that’s it!  The “docker run” command will create a container using that “httpd-test1” image built earlier, and  mounts the host’s /srv/example.com as /srv in the container.  I can control this new service like any other systemd service:

# systemctl start example.com
# docker ps
    CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS                  PORTS               NAMES
91e770b4d61f        httpd-test1:latest   /bin/sh -c /usr/sbin   1 seconds ago       Up Less than a second                       kickass_franklin6   
# docker inspect 91e770b4d61f | grep IPAddr
        "IPAddress": "172.17.0.25",
# curl http://172.17.0.25/
<h1>Hello,  World</h1>
#

Neat stuff.

Advertisements