Dockerizing Apache2 in Ubuntu

Challenge Statement:

  • Use Ubuntu as base image for Docker
  • Install Apache2
  • Redirect the logs to stdout so we can use 'docker logs'

Motivation:

  • Avoid pre-built images
  • Exercise some conceps

Introduction

Ok, if you reached here with the sole purpose of quickly run Apache2 in a Docker container, then it's quite simple once you have Docker installed:

# Make sure you have an Internet connection
# It will automatically fetch the image from Docker Hub
docker run -d -P -p 80:80 httpd

Check if the instance is running since we sent it to the background with '-d':

docker ps

Run curl command afterwards connect to your container and the container returns the web page.

This Docker image will automatically redirect all the logs to stdout, so you can issue a Docker logs command to see what's going on in the containerized Apache2:

docker logs container_name

To clean everything up , just stop and remove all the containers:

docker stop $(docker ps -qa)
docker rm $(docker ps -qa)

So, why the hell would I want to do from scratch something that already exists?

Well, that's how it works in real life, right. Some non-functional requirement, some system dependency, company culture... So, let's learn it, because it's not straightforward.


The Dockerfile

This is the gold. Let's just rush into it and explain things later.

Create an empty directory and put a file named Dockerfile into it, paste these contents there:

FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends apache2 && \
  apt-get clean
RUN ln -sf /proc/self/fd/1 /var/log/apache2/access.log && \
    ln -sf /proc/self/fd/1 /var/log/apache2/error.log
COPY start_apache.sh /
RUN chmod 755 start_apache.sh
EXPOSE 80
ENTRYPOINT ["/start_apache.sh"]

The FROM entry sources the latest Ubuntu as base image.

The 1st RUN updates the distro install, installs Apache2 and cleans up the apt-get cache so the image gets trimmed out of downloaded packages.

The 2nd RUN is a hack from here, an ingenuous way to output all the Apache logs to stdout. Kudos to Matt!

The COPY places a file from the host file system into the image file system. Grab the file's contents from Github and rename it accordingly. It is necessary so we don't have to setup a whole bunch of Apache environment variables in the Dockerfile, thus, cluttering it and making the maintenance of the infrastructure mixed with application layer. Ideally, this file would be in a repository of its own, so we could use the ADD command. But it's part of a larger repository. Downloading just one file from a repo is not easy from Dockerfile, if even possible.

The 3rd RUN makes sure the script has execution mode.

The EXPOSE will make sure port 80 is mapped from the running container into the host so it's accessible.

ENTRYPOINT will execute the Apache execution script right after the container kicks in.

Building the Image from the Dockerfile

All it takes now is to build the image and run it:

docker build -t ub-apache2-custom .

A lot of downloads will happen...

And eventually get to this, so you can run it with:

docker run -d -p 80:80 ub-apache2-custom

And run a curl to certify that it worked:

curl 127.0.0.1:80

Conclusion

Now we have a base image with Apache2 where logs go to STDOUT, so they can be streamed with docker logs command:

docker logs image_name

See below what happens once I trigger a request from a browser.

Notice how larger the Apache2 installation in Ubuntu more than doubled the image size. Always remember to take into account the size of your Docker images.

The provided official httpd image from Docker Hub is a few megabytes smaller. But if you need an Ubuntu customized image, that's not that bad.