Docker Compose is a handy tool for solving one of the biggest inherent challenges posed by container-based infrastructure. That challenge is this: While Docker containers provide a very easy and convenient way to make apps portable, they also abstract your apps from the host system — since that is the whole point of containers. As a result, connecting one container-based app to another — and to resources like data storage and networking — is tricky.
If you’re running a simple container environment, this isn’t a problem. A containerized web server that doesn’t require multiple containers can exist happily enough on its own, for example.
But if life were always simple, you wouldn’t need containers in the first place. To do anything serious in your cloud, you will probably want your containers to be able to interact with one another and to access system resources.
That’s where Docker Compose comes in. Compose lets you define the containers and services that need to work together to power your application. Compose allows you to configure everything in plain text files, then use a simple command-line utility to control all of the moving parts that make your app run.
Another way to think of Compose is as an orchestrator for a single app. Just as Swarm and Kubernetes automate management of all of the hundreds or thousands of containers that span your data center, Compose automates a single app that relies on multiple containers and services.
Using Docker Compose
Setting up a Docker environment using Compose entails multiple steps. But if you have any familiarity with basic cloud configuration — or just text-based interfaces on Unix-like operating systems — Compose is pretty simple to use.
Deploying the tool involves three main steps. First, you create a Dockerfile to define your app. Second, you create a Compose configuration file that defines app services. Lastly, you fire up a command-line tool to start and control the app.
I’ll walk through each of these steps below.
Step 1. Make a Dockerfile
This step is pretty straightforward if you are already familiar with creating Docker images. Using any text editor, open up a blank file and define the basic parameters for your app.
The Dockerfile contents will vary depending on your situation, but the format should basically look like this:
FROM [ name of the base Docker image you're using ]
ADD . [ /path/to/workdir ]
WORKDIR [ directory where your code lives ]
RUN [ command(s) to run to set up app dependencies ]
CMD [ command you'll use to call the app ]
Save your Dockerfile. Then build the image by calling docker build -t [ image name ]
Step 2. Define Services
If you can build a Dockerfile, you can also define app services. Like the first step, this one is all about filling in fields in a text file.
You’ll want to name the file docker-compose.yml and save it in the workdir that you defined in your Dockerfile. The contents of docker-compose.yml should look something like this:
[ name of a service ]:
build: [ code directory ]
- "[ tcp and udp ports ] "
- .: [ /path/to/code directory ]
- [ name of dependency image ]
[ name of another service ]:
image: [ image name ]
You can define as many services, images and dependencies as you need. For a complete overview of the values you can include in your Compose config file, check out Docker’s documentation.
Don’t forget that another cool thing you can do with Compose is configure log collection using Powerstrip and the Sumo Logic collector container.
Step 3. Run the app
Now comes the really easy part. With your container image built and the app services defined, you just need to turn the key and get things running.
You do that with a command-line utility called (simply enough) docker-compose.
The syntax is pretty simple, too. To start your app, call docker-compose up from within your project directory.
You don’t need any arguments (although you can supply some if desired; see below for more on that). As long as your Dockerfile and Compose configuration file are in the working directory, Compose will find and parse them for you.
Even sweeter, Compose is smart enough to build dependencies automatically, too.
After being called, docker-compose will respond with some basic output telling you what it is doing.
To get the full list of arguments for docker-compose, call it with the help flag:
When you’re all done, just run (you guessed it!) docker-compose down to turn off the app.
Some Docker Compose Tips
If you’re just getting started with Compose, knowing about a few of the tool’s quirks ahead of time can save you from confusion.
One is that there are multiple ways to start an app with Compose. I covered docker-compose up above. Another option is docker-compose run.
Both of these commands do the same general thing — start your app — but run is designed for starting a one-time instance, which can be handy if you’re just testing out your build. up is the command you want for production scenarios.
There’s also a third option: docker-compose start. This call only restarts containers that already exist. Unlike up, it doesn’t build the containers for you.
Another quirk: You may find that Compose seems to hang or freeze when you tell it to shut down an app using docker-compose stop. Panic not! Compose is not hanging. It’s just waiting patiently for the container to shut down in response to a SIGTERM system call.
If the container doesn’t shut down within ten seconds, Compose will hit it with SIGKILL, which should definitely shut it down. (If your containers aren’t responding to standard SIGTERM requests, by the way, you may want to read more about how Docker processes signals to figure out why.)
That’s Compose in a nutshell — or about a thousand words, at least. For all of the nitty-gritty details, you can refer to Docker’s Compose reference guide.
Setting Up a Docker Environment Using Docker Compose is published by the Sumo Logic DevOps Community. If you’d like to learn more or contribute, visit devops.sumologic.com. Also, be sure to check out Sumo Logic Developers for free tools and code that will enable you to monitor and troubleshoot applications from code to production.