Most modern developers don’t work with single applications anymore. Single applications use multiple services like MySQL, Redis, and Elasticsearch with Ruby on Rails or Spring Framework or Django. A single application then becomes many application that all use the same services and as developers, we need to work with all of them.

Tools like Docker or Kubernetes help us organize these apps in our live servers but we don’t need a lot of the complex orchestration on our development box, do we? Well, I think Docker Compose could be very useful.

Containers

The important concept to understand is the idea of containers.

A container … 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.

And there are plenty of these already prepackaged for us to use through Docker Hub, which is an app registry that allows anyone to link to already existing software and code. Containers are our pieces that we’ll use to put the development environment together and we’ll use existing apps to run MySQL and Redis as services.

Have you installed docker yet?

Docker Download

Once you’ve completed the install onto your machine and it’s running, you can go into your terminal or command prompt:

$ docker ps
CONTAINER ID        IMAGE                 COMMAND               ...

Docker Compose

We could download all our software packages from their website or from package managers like Homebrew or Chocolatey to install them locally but we could just as easily write a file called docker-compose.yml and have it pull from Docker Hub. The docker-compose.yml file tells Docker Compose how to build your services and settings your services use like volume on your machine.

# Filename: docker-compose.yml
version: '3'
services:
  mysqldb:
    image: mysql/mysql-server:5.6
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=true
      - MYSQL_LOG_CONSOLE=true
    ports:
      - "3306:3306"
    volumes:
      - mysqldata:/var/lib/mysql
  redis:
    image: redis:3.0
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data
volumes:
  mysqldata:
    driver: local
  redisdata:
    driver: local

In the sample above, I am doing the following:

  • Specifying compose to expect version: 3 syntax.
  • Specifying services for mysqldb and redis.
  • Specifying volumes so my services can persist their data. Using volumes is important because when you shutdown your services, any changes made on your server will be lost.

Under the MySQL service:

  • Specifying the image install 5.6 of MySQL with mysql/mysql-server:5.6 from DockerHub
  • Specifying the environment variables that the image uses, in this case it’s my development environment. I want to login to the MySQL server as root using an empty password and I want it to output stderrs so I can see them when I ask Docker to look at the logs.
  • Specifying the ports in the way I did makes local port 3306 bind to the service’s 3306 so we can access it locallying on our machine at 127.0.0.1:3306.
  • Specifying the volume for MySQL to persist all our development work with it.

Under the Redis service:

  • Specifying the image install version 3.0 of Redis with redis:3.0
  • Specifying the ports to bind locally to 6379 to the services 6379.
  • Specifying the volume to again keep our work that we used with Redis

Now what?

We wrote the file but how does it work? When you had installed Docker, it should have also installed a command line utility called docker-compose. In your terminal, if you go to the directory containing your docker-compose.yml file, running

(~/wombat)$ docker-compose up -d
...
  a bunch of docker data pulling your images
...
Creating network "wombat_default" with the default driver
Creating wombat_redis_1   ... done
Creating wombat_mysqldb_1 ... done

You’ll notice that docker-compose uses your folder that contained your docker-compose.yml file to create your services name in Docker. In my case, my folder was called wombat and if I look at Docker’s running processes, I will see something like the following:

(~/wombat)$ docker ps
... IMAGE                    ...  PORTS                   NAMES
... redis:3.0                ...  0.0.0.0:6379->6379/tcp  wombat_redis_1
... mysql/mysql-server:5.6   ...  0.0.0.0:3306->3306/tcp  wombat_mysqldb_1

You should now be able to access your services with the following credentials on your local machine

# mysql
username: root
password: <empty>
hostname: 127.0.0.1
port: 3306

#redis
username: <empty>
password: <empty>
hostname: 127.0.0.1
port: 6379

Shut down your services

Okay, you’re done developing for the day, how would you shut down your services? You could quit Docker, which shuts down your services gracefully, but you can do the same thing in your terminal or command prompt:

(~/wombat)$ docker-compose down
Stopping wombat_redis_1   ... done
Stopping wombat_mysqldb_1 ... done
Removing wombat_redis_1   ... done
Removing wombat_mysqldb_1 ... done
Removing network wombat_default

Conclusion

There are so many other things that you can do to maintain your Docker development environment. What if you wanted to use your container for development? Maybe that will be the topic for my next blog post…


References