Basic Usecase with Docker: Simple PHP Application

Basic Usecase with Docker: Simple PHP Application

In last post with introduction about Docker, we had an overview about what is Docker and how it works.

Today we will start with a very basic usecase with Docker, try to wrap an existing simple PHP application into a container, publish it to public repository and pull to use in any client.

Install Docker

Docker can be installed within OS X, Linux or Windows. We will use Ubuntu 14.04 as basic environment. To start, check and install wget if needed:

$ sudo apt-get update $ sudo apt-get install wget

Now install Docker:

$ wget -qO- https://get.docker.com/ | sh

After Docker installation has been completed, try to run Hello World:

$ docker run hello-world

Now you can see a message from Docker:

Hello from Docker. This message shows that your installation appears to be working correctly.

Create PHP Application

Our application will do simple thing: connect to a MySQL server, then echo all data inside user table.

Create directory and then index.php:

$ mkdir TestDocker $ cd TestDocker $ vi index.php

Content of index.php:

<?php
  $servername = "localhost:3306";
  $username = "root";
  $password = "";
  $database = "docker_test";
  // Create connection
  $conn = new mysqli($servername, $username, $password, $database);
  // Check connection
  if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
  }
  echo "Connected successfully<br>";
  $sql = "SELECT * FROM user";
  $result = $conn->query($sql);
  if ($result ==! NULL && $result->num_rows > 0) {
    // output data of each row
    while($row = $result->fetch_assoc()) {
      echo "id: " . $row["id"]. " - Name: " . $row["name"]. "<br>";
    }
  } else {
    echo "0 results";
  }
  $conn->close();
?>

Note that our server name is still localhost:3306 which should not work in case we use this application inside a Docker container, but we will fix this later.

Create Dockerfile

We create a Dockerfile to build our container:

$ vi Dockerfile

Enter the following content:

FROM ubuntu:12.04
# Install dependencies
RUN apt-get update -y
RUN apt-get install -y git curl apache2 php5 libapache2-mod-php5 php5-mcrypt php5-mysql
# Install app
RUN rm -rf /var/www/*
ADD index.php /var/www
# Configure apache
RUN a2enmod rewrite
RUN chown -R www-data:www-data /var/www
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
EXPOSE 80
CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]

Above Dockerfile script has a few commands to:

  • Install dependencies, including git, curl, apache web server, php, etc.
  • Remove all contents inside root Apache directory, then add our index.php inside that directory
  • Setup some environments variable for Apache to work correctly
  • Expose port 80 (default http port) for host
  • Set default run command to active Apache in foreground

Then we are ready to go, try to build this container. Note that the command has a dot at the end.

$ docker build -t <enter-your-docker-username>/<enter-your-repo-name> .

After build process has been completed, verify images existence by entering docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<your-docker-username>/<your-docker-repo-name> latest 43c52559a0a1 12 minutes ago 258.1 MB
ubuntu 12.04 78cef618c77e 3 weeks ago 133.7 MB

Run it:

$ docker run -p 80:80 <your-docker-username>/<your-docker-repo-name>

This command will map port 80 of host, forward it with port 80 which has been exposed by container. In another way, Apache inside our container, now will listen for request from port 80 of host, which is our Linux machine. You can point to its public IP Address (if you are using a VPS) or localhost (if you are on local machine) to see our result for now:

[Docker_InvalidMySQLSDocker_InvalidMySQLServer

This error is intended because within container, the localhost:3306 should be a MySQL Server is running inside container. But our MySQL Server is running within host machine, so we need to findout a way for our Apache inside container, to connect with MySQL Server inside host machine.

Docker Network Bridge

The easiest way to get our application work, is to identify IP Address of host machine which Docker container can see.

$ sudo ip addr show docker0 4: docker0:
<BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff inet 172.17.42.1/16 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::5484:7aff:fefe:9799/64 scope link valid_lft forever preferred_lft forever

You can see our host machine has IP Address of 172.17.42.1 on docker0 network interface. We try to start a new Docker container to see its IP Address:

$ docker run --rm -it ubuntu:trusty bash -il
root@e77f6a1b3740:/# ip addr show eth0
863: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ff inet 172.17.1.192/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::6432:13ff:fef0:f1e3/64 scope link valid_lft forever preferred_lft forever

Check route table of running container:

root@e77f6a1b3740:/# route
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.42.1 0.0.0.0 UG 0 0 0 eth0 172.17.0.0 * 255.255.0.0 U 0 0 0 eth0

So the IP Address of the docker host 172.17.42.1 is set as the default route and is accessible from your container. We exit this container, and back to our index.php, change localhost:3306 to 172.17.42.1:3306. Then rebuild our container:

$ docker build -t <enter-your-docker-username>/<enter-your-repo-name> .

And run it again:

$ docker run -p 80:80 <your-docker-username>/<your-docker-repo-name>

Refresh your browser and your application should work right now:

[Docker_ValidMySQLSerDocker_ValidMySQLServer

Now you know about how a Docker container can directly connect to an external service through network interface. In next post, we will discuss about how can we connect multiple Docker container for migration and development.