Linux, php

A sample application running on Docker container – part 1

I’ve been using Docker for several months on my development and production environments. With Docker, I’m able to run multiple PHP applications, Django apps and RabbitMQ messaging containers simulteanously without having to deal with software package conflicts. I’d like to share my generic PHP/Apache Dockerfile that can be used to run simple PHP applications.

Docker Basics

According to Docker’s website:

Docker allows you to package an application with all of its dependencies into a standardized unit for software development.

And more:

Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.

In short, it is like running your entire web application in an isolated container as if it is a separate server.

Requirements

Docker needs to run on a Linux environment (you can run it on a VM if you are on Windows or Mac). It is much better if you have a fast connection as well (or run it on a VPS). My sample Dockerfile is based on Docker 1.6.2.

See the getting started guide on how install docker.

Sample Application

In our example, we are trying to build a simple PHP page, showing phpinfo() content. Nothing fancy. We will be using CentOS 7 as base image and Supervisord to run our Apache server. Let’s prepare the following files.

Building a Docker image

In order to run a Docker container, we need to create a Docker image first, or use one of the existing images on Docker hub. In our case, let’s build it ourselves.

Let’s organize a bit so we put all our files in one directoty.

mkdir  /path/to/docker-build
cd /path/to/docker-build

Prepare the files. Based on the list above, we create files like the sample php file, a bash script to start the supervisord application and some apache and supervisord configuration.

File: index.php

Or you could put hello world.

<?php

phpinfo();

File: vhost.conf

Our virtual host configuration for Apache.

<VirtualHost *:80>
    ServerAdmin admin@example.com
    DocumentRoot /var/www/html
    ErrorLog /var/log/httpd/default-error.log
    CustomLog /var/log/httpd/default-access.log combined
    <Directory "/var/www/html">
        AllowOverride all
        Require all granted 
    </Directory>
    php_value "date.timezone" "UTC"
</VirtualHost>

File: supervisor-httpd.ini

Supervisord configuration to run Apache on start.

[program:httpd]
priority = 100
command = /bin/bash -c "/bin/sleep 2 && /usr/sbin/httpd -c \"ErrorLog /dev/stdout\" -DFOREGROUND"
redirect_stderr = true
stdout_logfile = /var/log/httpd/error_log
stdout_events_enabled = true
startsecs = 5
autorestart = true

File: start.sh

Entry script where Docker executes upon running the container.

#!/bin/bash

__run_supervisor() {
    echo "Running the run_supervisor function."
    supervisord -n
}

# Call all functions
__run_supervisor

File: Dockerfile

The most important file is the Dockerifle where it contains instructions on how to build the image. Below is my sample Dockerfile.

It simply instructs the packages to install, copy our prepared files into specific directories, define volumes which make it possible for the files to persist even if we delete the container. Then it defines a port to expose, which is 80 in our example.

Last part defines the entry point where it points to a script that runs supervisord that makes it possible to run Apache on the background.

You can find more information on Docker documentation on how to create a Dockerfile.

FROM centos:centos7
MAINTAINER Leonel Baer <leonel@lysender.com>

# Install packages
RUN yum -y update &&  yum clean all
RUN yum -y install epel-release && yum clean all

# Install MariaDB, Apache, PHP and misc tools
RUN yum -y install mariadb-server \
    mariadb-devel \
    supervisor \
    git \
    tree \
    httpd \
    php \
    php-bcmath \
    php-common \
    php-pear \
    php-mysql \
    php-cli \
    php-devel \
    php-gd \
    php-fpm \
    php-pdo \
    php-mbstring \
    php-mcrypt \
    php-soap \
    php-xml \
    php-xmlrpc \
    bind-utils \
    pwgen \
    psmisc \
    net-tools \
    hostname \
    curl \
    curl-devel \
    sqlite \
    sendmail \
    cronie && yum clean all

# Add config files and scripts
ADD ./vhost.conf /etc/httpd/conf.d/default-vhost.conf
ADD ./index.php /var/www/html/index.php

# Configure servicies
ADD ./start.sh /start.sh
ADD ./supervisor-httpd.ini /etc/supervisord.d/httpd.ini

RUN chmod 755 /start.sh

VOLUME ["/var/www/html", "/var/log/httpd"]

EXPOSE 80

CMD ["/bin/bash", "/start.sh"]

Build it

Let’s build the docker image. I prefer to do docker stuff (building image/running containers) using a regular user. You may use root as well.

cd /path/to/docker-build
docker build --rm -t generic-php-apache .

The command builds an image. The parameter --rm tells docker to delete the intermediate containers while running the commands. Since docker commands needs a container for it to run, building an image will create multiple containers that we won’t need, thus we pass the --rm parameter.

The -t parameter let you specify the tag name of the image. I used generic-php-apache in this case. You can specify any custom name.

It should successfuly create an image. Run docker images to list the available images on your local machine.

Running the container

We can now run our first container. In our example, we will run it at http://127.0.0.1:8080.

docker run --name=sample-php -d -p 8080:80 generic-php-apache

The command above creates a container called sample-php, run it on the background via the -d parameter so that it would continously run, then assign the local port 8080 to map to the containers port 80. Lastly, we run our container off the generic-php-apache image.

We should be able to view the page http://127.0.0.1:8080 or if you are using a VPS, it would be like this http://your-vps-ip:8080.

Explore

To check the currently running docker containers, simply run:

docker ps

Curious what the container looks like? You can login to it. Run this command:

docker exec -it sample-php bash

You should get a shell inside the docker container. You can inspect the files like our sample index.php file or even modify it. It is just like any other server machine although it is abstracted as if it is a real box. To logout of the shell, press CTRL+D.

You can also stop, start and restart containers. Just remember its name.

docker stop sample-php
docker start sample-php
docker restart sample-php

You can also create another container using the same image, but runs on different port.

docker run --name=sample-php2 -d -p 8081:80 generic-php-apache

And access it on port 8081.

Use an existing image from Docker hub

To save you from having the trouble of building the image, you can re-use this existing image. I have pushed this example to the Docker hub.

Docker hub image: https://hub.docker.com/r/lysender/php-apache/

Simply run it like this:

docker run --name=sample-php -d -p 8082:80 lysender/php-apache

You can view the full sample files on Github.

Part 2 coming soon. The database part.

Note: This blog is using the lysender/php-apache image (the PHP/Apache part).

Enjoy and share.

1 thought on “A sample application running on Docker container – part 1”

Leave a reply

Your email address will not be published. Required fields are marked *