Setting up a dev environment using Docker

Ridwan Shariffdeen
Docker Captain
Published in
7 min readAug 14, 2016

--

It has been a pain for the developers when it comes to setting up a dev environment on a new PC or a formatted PC. Back in those days I just wished if we could just run one command and boom! everything I want is up and running. Well, now that is possible with Docker. Using docker we can now create multiple dev environment which was not possible at all unless we use a VM. If you are developing web application on PHP 5.5 and want to migrate to Ubuntu 16.04 but you can’t because Ubuntu 16.04 supports PHP 7 and you are not sure downgrading to PHP 5.5 would solve the problems, this is a solution you can try since Docker isolates the host environment and provide a containerized environment to deploy your applications. Which means, you can run PHP 5.5 applications using docker on Ubuntu 16.04 without downgrading the existing PHP 7.

developer need to setup multiple languages and multiple tools

Let’s setup a dev environment for web application development to see how it works. The art of dockerization is identifying decomposable components and designing a architecture that is easily pluggable and easy to maintain. So if we take a simple web application we need a web server, database server, programming environment and optionally a load balancer. For this exercise lets create a simple setup which includes a Apache Server, MySQL Server and PHP 5.5 environment. First lets setup a MySQL server using Docker with the following command

docker run -d --name Database_Server --restart always -e MYSQL_ROOT_PASSWORD=root mysql:5.5

This command will pull mysql image with the tag 5.5 and instantiate a container named “Database_Server”. If you noticed we have used some additional parameters (which are simple to understand) with Docker run command. Docker run accepts many flags and parameters which gives us the flexibility to modify the container behavior as we wish. You can find the full list of parameters for Docker run command here, I will explain the ones I used above.

  • d or - -detach is a flag used to indicate the running mode of the container. There are two modes you can choose the container to run from which are the default “foreground” mode or the detached “background” mode. Simply put in foreground method the container would exit once the command it run exit(or once you exit the interactive shell). Background mode can be used to keep the container alive until we explicitly stop the container. More details on that can be found here. By design, containers started in detached mode exit when the root process used to run the container exits.
  • - -name followed by a string (a name) would set the container name to that specified string. Each container has a unique name and a container ID to identify the container within the host.
  • - -restart flag is used to set the restart policy of the container. Docker daemon can restart your container (if stopped by failure) according to the policy we set. If MySQL server process exit due to some exception, the container would automatically stop since that is the root process used to run the container. Hence, we need some sort of restart mechanism to start our containers, thankfully Docker provides that. You have several restart policies given by docker. Possible options are : no, on-failure[:max-retry], always, unless-stopped.
  • e or - -env followed by a key=value combination would set the value as a environment variable in the docker container. These environment variables are specific to the container we are running. In this case we pass the ROOT_PASSWORD for the MySQL database. You can find more environment variables you can set for the MySQL container here.

Once you enter this command, you will get a hash string which is the ID of the container. You can use docker ps to verify that a container named Database_Server is up and running. The MySQL image with tag 5.5 has the bash installed on it (usually it is not installed) so we can use docker exec -it Database_Server bash to open a bash inside the container. Then, we can use any MySQL command we use in a normal bash to execute inside the container. You can check the logs of the container by using docker logs Database_Server.

Now that we have a MySQL server up and running what we need is a PHP environment and a Apache server to serve our web pages. You can either use two different containers one for PHP and Apache and link these together or use one container that has both. I prefer to use one container which has Apache installed with PHP 5.5. So let’s go ahead and instantiate a container with both Apache and PHP. First you need to create your own directory which will be used as the web root directory of the container. It doesn’t have to be in your host /var/www/html instead you can define your own folder.

docker run -d --name Web_Server --restart always -p 80:80 -v "$PWD":/var/www/html  --link Database_Server:dbserver php:5.5-apache

There are few additional parameters I have used here to instantiate my Apache container.

  • -p or --published followed by a mapping (ABC:DEF) would publish a containers port to one of host port. The order is HostPort:ContainerPort. In our use case we publish 80 port with host 80 port. If we don’t specify a mapping and instead just a port then docker would automatically map the given port of the container to a random port in the host.
  • -v or --volume followed by a mapping (A/B/C:D/E/F) would mount a volume in the host. Additionally we can specify modes of mounting (read only/read-write etc). Here we mount the ‘/var/www/html’ directory of the container to the current directory of the host.
  • --link followed by a mapping (A:B) will link containers. We can refer containers within containers by using this setup. For our web applications we need to communicate with the database server. Hence we need to know the Docker IP address of the container, but with this option we can easily use a link that can be used as the host name and docker will automatically map the correct IP address to that. So we do not need to query the IP address of the database server manually and set it up.

You can verify that your web server is working by including a index.html file in your current directory and accessing it using your web browser with http://localhost

Now, everything works fine! We have a working web server linked with a database server. But, this only solves the problem partially. We can’t remember each and every parameter for the run command when we setup a dev environment for a new machine. The above example we might be able to remember the exact command but when it comes to different containers remembering this would be not practical. On the other hand what we desire was one simple command to get this up and running for us. This is where docker-compose gives you the added advantage. Compose is a tool for defining and running multi-container Docker applications. First, lets check if you have docker-compose installed in your machine using the command “docker-compose --version” and if it doesn’t recognize your command you should install docker-compose. Click here to see instructions.

Now the idea is simple, we need to create a docker-compose.yml file which captures all the commands we ran earlier. This way we can use docker-compose to run the same commands to instantiate all the containers we require in one command. That’s pretty cool rite? let’s try this out.

First we need to define the containers using the syntax of docker-compose. You can find more details on that here. For the above setup the docker-compose file is as follows

Web_Server:
image:
php:5.5-apache
restart: always
container_name: Web_Server
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./logs/apache2:/var/log/apache2
- ./html:/var/www/html
- /etc/localtime:/etc/localtime
environment:
- VIRTUAL_HOST=example_host
links:
- Database_Server:dbserver
ports:
- 80:80

Database_Server:
container_name:
Database_Server
image: mysql:5.5
restart: always
volumes:
- /etc/localtime:/etc/localtime
environment:
- MYSQL_ROOT_PASSWORD=root

I have additionally linked the host clock and container clock so the clocks are synced (which is useful when debugging), and I have mounted the logs directory for Apache in one of the folders. Using this docker-compose.yml file we can use the following command to instantiate the dev environment

docker-compose up

The above command should be initiated within the directory the docker-compose.yml file is or we can specify the file using the -f parameter. The above command will instantiate all defined containers. If we have multiple definitions and only need certain containers to be started we can use the same command followed by the container names. There are more controls we can use to configure the way our containers would start (start up order, network config, volume containers etc.) using docker-compose. But, for this exercise this would suffice.

In order to check the status of docker containers we usually use docker ps command which would list the status of all the containers running in our machine. If we want to know the status of the containers defined in our docker-compose file we can use the command docker-compose ps which is similar to previous command but filters the containers we require.

I didn’t cover all the scenarios of setting up a dev environment, but I hope this would help you to get an idea on how we can utilize docker to setup our own environment. There are multiple ways you can create your dev environment, this is just one simple way to elaborate the usage of docker-compose. If you have any query do comment below I will help to the best of my knowledge.

--

--