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 generic-php-apache 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 generic-php-apache
docker start generic-php-apache
docker restart generic-php-apache

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://registry.hub.docker.com/u/lysender/genericphpapache/

Simple run it like this:

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

You can view the full sample files on Github.

Part 2 coming soon. The database part.

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

Enjoy and share.

Posted in Linux, php | Tagged , | Leave a comment

PHP – Instantiate a class from a string with namespace

I know this is old but I just learned it today. Instantiating a class from a string requires a full namespace of the class, otherwise it won’t work. You cannot use a namespace to make shortcuts. See example below. Shortcut? … Continue reading

Posted in php, Programming | Tagged , | 2 Comments

CentOS 7 + SELinux + PHP + Apache – cannot write/access file no matter what

I’ve spent 2-3 hours pulling my hair trying to setup a supposed to be simple PHP/MySQL web application on an Amazon EC2 instance running on CentOS 7. Apache logs keep saying that it can’t write to file due to permission … Continue reading

Posted in Linux | Tagged , , , , | Leave a comment

jQuery DataTables – sort by date using timestamp data attribute

DataTables is an awesome “do-it-all” table plugin for jQuery. I was trying to display a locally formatted date but should still be sorted correctly. Current plugins may not work since it the date format uses long date format like “20 … Continue reading

Posted in JavaScript, Web Development | Tagged , , | Leave a comment

Pag-IBIG contributions – paying through payment centers?

Few days ago, I was thinking of paying my Pag-IBIG monthly contributions via third party payment centers. However, I’m hesitant to try. Today, I made up my mind and went to Pag-IBIG office to pay and at the same time, … Continue reading

Posted in Personal Blog | Tagged , , | Leave a comment

Pag-IBIG – Homebased – Change status from employed to individual payor

It turn’s out that it is not too hard to change your Pag-IBIG membership status from being employed to voluntary paying member. It only took me less than an hour to file the change of my status. Here is the … Continue reading

Posted in Personal Blog | Tagged , | Leave a comment

PHPStorm SFTP to a Linux machine

I tried setting up PHPStorm on Windows 8.1 with remote files stored on a Linux VM via VirtualBox bridged connection. However,upon trying to setup SFTP, I got this error: “Algorithm negotiation fail”. It seems related to SSH rather than specific … Continue reading

Posted in Linux, SSH | Leave a comment

Installing KeePass on Slackware Linux

Due to the recent data breach incident at LastPass, I immediately look for alternative offline password managers. KeePass seems to be a good alternative. With Slackware 14.1_64, I installed it with some minor issues that are resolved with just few … Continue reading

Posted in Personal Blog | Leave a comment

Fast internet connection but very slow hostname resolution

I’m using PLDT’s HomeBro Ultera basic plan and that’s 3 megabits per second. That is fast enough for most of us. However, this last 2 or 3 months, I’m experiencing very poor browsing experience. Sites are loading show, but once … Continue reading

Posted in Networking | Leave a comment

Homebased – PhilHealth – change status from employed to voluntary

For homebased worker like me, we usually pay our government obligations ourselves. In my case, I have just continued my PhilHealth contributions by paying voluntarily under the category of “Informal Economy/Informal Sector”. Sounds weird but that is how they qualify … Continue reading

Posted in Personal Blog | 3 Comments