So let’s talk a little bit about development environments, shall we?
We developers are always trying to find the best solution when it comes to creating the best development environment, but that’s not always easy. Things are changing very fast nowadays and if you work on more than one project, things can get a little confusing.
About three years ago I thought I had the best solution for this problem using VirtualBox and Vagrant. I could start up a linux box in minutes and changes made in a project’s box would not affect other project’s environment. That was a nearly perfect solution when it worked. Most of the times things got out of hand when the projects got to big and complex and sometimes it was nearly impossible to recreate exacly the production environment using only one VM, and that’s when this solution failed.
Well, that was the time I first heard about Docker.
At first, the whole concept of containers in Docker kind of scared me, and I was reluctantly to start using this amazing tool. A year passed and I kept banging my head on development environment issues until I finally gave Docker a chance, and I can say that it changed my life.
What is a container
A container image is a lightweight, stand-alone, executable package of a piece of software that includes everything needed to run it: code, runtime, system tools, system libraries, settings. Available for both Linux and Windows based apps, containerized software will always run the same, regardless of the environment. Containers isolate software from its surroundings, for example differences between development and staging environments and help reduce conflicts between teams running different software on the same infrastructure.
Docker containers running on a single machine share that machine’s operating system kernel; they start instantly and use less compute and RAM. Images are constructed from filesystem layers and share common files. This minimizes disk usage and image downloads are much faster.
If you don’t have Docker installed on your local machine, please refer to the Docker website for instructions on how to install the software depending on your platform.
Docker is available in two editions: Community Edition (CE) and Enterprise Edition (EE).
Docker Community Edition (CE) is ideal for developers and small teams looking to get started with Docker and experimenting with container-based apps and that’s the one we are going to use on development.
With Docker installed, we’re now able to start building our setup.
For this example project, I’m using a Node.js app. For development purposes I want the app to run directly on my machine, so I won’t put the app inside a container too. I’m only loading containers for the services I need to run for support and that’s the database server, a mail server (I use mailhog) and a PhpMyAdmin container so I can better access the database.
An excelent tool to automate this kind of building task on Docker is Docker compose, so let’s talk a little bit about it.
Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.
sudo curl -L https://github.com/docker/compose/releases/download/1.20.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
On Windows and Mac, compose is already installed.
So, we’re all set, let’s go.
Go inside your project’s folder, create a file called docker-compose.yml.
Now we’ll add the compose version used on this document and create a separate network to add the containers. All the containers from this example are going to be enclosed in this bridged network.
version: '3' networks: dev: driver: bridge ipam: config: - subnet: 22.214.171.124/16
Now let’s create a services block to add the containers and add the database container.
For this example we are going to use a MySQL image called ‘database’. We’ll set a volume so we can run .sql scripts at database startup and set a database name and root password. Since we are going to access the database from outside of the docker network, we have to open the database port and we also have to attach this container to the network we created earlier.
After all this changes, the file will look like this:
version: '3' services: database: # service name image: mysql:latest # docker image container_name: database # container name volumes: - ./data:/docker-entrypoint-initdb.d # binds local 'data' folder to container 'docker-entrypoint-initdb.d' folder environment: MYSQL_DATABASE: contability # create default database with this name MYSQL_ROOT_PASSWORD: asdfasdf # set password for root user networks: - dev # bind this service to a network ports: - 3306:3306 # opens this port to the outside world - [outside port]:[container port]
Now let’s add other containers to our compose file, starting with a PhpMyAdmin container.
pma: image: phpmyadmin/phpmyadmin container_name: pma environment: PMA_ARBITRARY: 1 PMA_HOST: database PMA_USER: root PMA_PASSWORD: asdfasdf PHP_UPLOAD_MAX_FILESIZE: 1G PHP_MAX_INPUT_VARS: 1G networks: - dev ports: - 8080:80 # Forwards port 8080 on the outside to port 80 inside the container
Now a mailhog container
mailhog: image: mailhog/mailhog # docker image container_name: mailhog # container name networks: - dev # bind this service to a network ports: - 1025:1025 # Web interface port - 8025:8025 # SMTP port
The final docker-compose.yml file we’ll look like this:
version: '3' services: database: image: mysql:latest container_name: database volumes: - ./data:/docker-entrypoint-initdb.d environment: MYSQL_DATABASE: contability MYSQL_ROOT_PASSWORD: asdfasdf networks: - dev ports: - 3306:3306 pma: image: phpmyadmin/phpmyadmin container_name: pma environment: PMA_ARBITRARY: 1 PMA_HOST: database PMA_USER: root PMA_PASSWORD: asdfasdf PHP_UPLOAD_MAX_FILESIZE: 1G PHP_MAX_INPUT_VARS: 1G networks: - dev ports: - 8080:80 mailhog: image: mailhog/mailhog container_name: mailhog networks: - dev ports: - 1025:1025 - 8025:8025 networks: dev: driver: bridge ipam: config: - subnet: 126.96.36.199/16
When all of this is set, just run docker-compose up to bring your development environment up. The database will be accessible on localhost:3306, PMA will be on localhost:8080 and mailhog will be on localhost:1025.