At this point in our Docker Tutorial Series, we have learned how to get Docker installed, run some Nginx containers, tested out MongoDB in a container, spun up more than one container, learned about Docker networking, DNS, images, volumes, and much more. You may have noticed that when using the Docker command-line interface, the commands can be somewhat verbose and require a lot of memorization of what commands to run to accomplish all of the prior mentioned tasks. This is where Docker Compose comes to the rescue. By leveraging Docker Compose, you can define multi-container applications that provide a service, all by defining a single YAML File. Once your YAML file is complete, you can run a single command to bring up an application that may have multiple containers, a network, and one or more volumes as needed.
Why Use Docker Compose?
Docker Compose is a combination of a command-line tool, working in concert with a configuration file(the YAML file mentioned above). In order to use Docker Compose, you’ll want to be familiar with building images, running containers, creating networks, and how to use volumes. Software services are not usually in isolation. Containers are a single process, and most useful applications need much more than a single process to provide a useful service. A Node container is going to need other containers like perhaps a Mongo container or a Redis container to do useful work. Docker Compose gives you a way to connect all the pieces of a solution together, along with setting up the networking and volumes, without having to run an excessive amount of commands at the command line. It works using two things, the Docker Compose YAML file, and The Docker Compose CLI.
Docker Compose YAML File
The first part of Docker Compose is the YAML file. YAML stands for Yet Another Markup Language, and it is the format used for Docker Compose configuration files. In the YAML file, you would specify several things.
The YAML File Defines:
- Environment Variables
- Many other possible options
The docker-compose.yml file has several versions, with version 3 being the most current and recommended at the time of this writing. The version statement should always be the very first line in the docker-compose.yml file. The default file name to use is docker-compose.yml, but you can specify a different name if you must by using the -f flag. In this file, you define the application stack and the way components in the stack interact with each other.
Sample docker-compose.yml template
version: '3.8' # Specify the compose format that this file conforms to. services: # Specify the set of containers that your app is composed of. servicename1: # Same as 'docker container run' # Used as the DNS name inside the network. build: # Build an image from a Dockerfile image: # Tag or partial image ID. Can be local or remote, Compose will attempt # to pull if it doesn't exist locally. # Optional if you use 'build:' command command: # Override the default command. # Optional to replace the default CMD specified by the image environment: # Add environment variables. You can use either an array or a dictionary # Environment variables with only a key are resolved to their values on # the machine Compose is running on. Same as '-e' in 'docker run' volumes: # Same as using '-v' with 'docker container run' ports: # Expose ports. Either specify both ports (HOST:CONTAINER) # or just the container port (a random host port will be chosen). servicename2: build: image: command: environment: volumes: ports: volumes: # Optional, same as 'docker volume create' networks: # Optional, same as 'docker network create'
So you can see why you kind of need to be familiar already with images, containers, environment variables, networks, and volumes since these are exactly what you are defining in the compose YAML file. The ‘services’ section is where all of the containers of your app are defined. Inside ‘services’, we see ‘servicename1’ and ‘servicename2’. This would represent two different containers on the same network. Again, the name you choose here is also used as the DNS name on the virtual network, so choose your name carefully. Inside of each ‘servicename’, are the things that comprise each container. These of course are things like the image, command, variables, and volumes in use. We added comments above to explain what each key in the YAML file does, but typically the file will look more compact. Here it is without the comments, where you can see how the indentation sets up the hierarchy.
version: services: servicename1: build: image: command: environment: volumes: ports: servicename2: build: image: command: environment: volumes: ports: volumes: networks:
Docker Compose CLI
The second part of Docker Compose is the
docker-compose cli tool. This tool parses the YAML configuration file to set up local development and test automation in the most common scenarios. Note that Docker Compose is constantly evolving, and may find use in production or other ways as well. Check the spec and the roadmap for more information on this. The CLI of Docker Compose greatly reduces the number of commands you need to memorize and type. You can go a long way with two simple commands.
- docker-compose up
- docker-compose down
Note: Be sure to type docker-compose and not docker compose, or you will get an error of Command “compose up” not available in current context (default).
The full listing of Docker Compose CLI commands is listed here:
PS C:\code\> docker-compose Define and run multi-container applications with Docker. Usage: docker-compose [-f
...] [options] [--] [COMMAND] [ARGS...] docker-compose -h|--help Options: -f, --file FILE Specify an alternate compose file (default: docker-compose.yml) -p, --project-name NAME Specify an alternate project name (default: directory name) -c, --context NAME Specify a context name --verbose Show more output --log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) --no-ansi Do not print ANSI control characters -v, --version Print version and exit -H, --host HOST Daemon socket to connect to --tls Use TLS; implied by --tlsverify --tlscacert CA_PATH Trust certs signed only by this CA --tlscert CLIENT_CERT_PATH Path to TLS certificate file --tlskey TLS_KEY_PATH Path to TLS key file --tlsverify Use TLS and verify the remote --skip-hostname-check Don't check the daemon's hostname against the name specified in the client certificate --project-directory PATH Specify an alternate working directory (default: the path of the Compose file) --compatibility If set, Compose will attempt to convert keys in v3 files to their non-Swarm equivalent (DEPRECATED) --env-file PATH Specify an alternate environment file Commands: build Build or rebuild services config Validate and view the Compose file create Create services down Stop and remove containers, networks, images, and volumes events Receive real time events from containers exec Execute a command in a running container help Get help on a command images List images kill Kill containers logs View output from containers pause Pause services port Print the public port for a port binding ps List containers pull Pull service images push Push service images restart Restart services rm Remove stopped containers run Run a one-off command scale Set number of containers for a service start Start services stop Stop services top Display the running processes unpause Unpause services up Create and start containers version Show version information and quit
Ok let’s put the rubber to the road. How powerful is Docker Compose? Well, consider this. Let’s say you want to try Django. There are a lot of prerequisites to take care of. You have to have the right version of Python installed on your system, you need to install Django using pip, you need to configure all kinds of variables, you might even need to set up virtual environments. You can do it the long way following this tutorial. With Docker Compose it’s as easy as a few commands. A great place to get started with using Docker Compose is with the Awesome Compose github repository. This repository is a curated list of Docker Compose examples that provide a starting point for how to integrate different services using a Compose file and to manage their deployment with Docker Compose. Some of the environments included are angular, apache-php, django, flask, Minecraft, nginx, golang, react, node, express, vuejs, traefik, java, WordPress, and many more.
Let’s try a few examples for ourselves!
PS C:\code> git clone https://github.com/docker/awesome-compose.git PS C:\code> cd awesome-compose/django PS C:\code\awesome-compose\django> docker-compose up Creating network "django_default" with the default driver Building web Step 1/8 : FROM python:3.7-alpine 3.7-alpine: Pulling from library/python 188c0c94c7c5: Pull complete 55578f60cda7: Pull complete 3d0ed0f04a02: Pull complete 98762f4040a9: Pull complete 2eeb36df0351: Pull complete Digest: sha256:61582ae7cccd4c8e64c79fd559e70ef1dbb5a5bddc8fab51cabfad592e7d5bde Status: Downloaded newer image for python:3.7-alpine ---> 4d91c1ce4cc8 Step 2/8 : EXPOSE 8000 ---> Running in 5c5ddb2d943e Removing intermediate container 5c5ddb2d943e ---> 915a9a087216 Step 3/8 : WORKDIR /app ---> Running in d9fea7cfae35 Removing intermediate container d9fea7cfae35 ---> d6ec540549fe Step 4/8 : COPY requirements.txt /app ---> 7f0a3dfca478 Step 5/8 : RUN pip3 install -r requirements.txt ---> Running in d38b7eee00eb Collecting Django==3.0.7 Downloading Django-3.0.7-py3-none-any.whl (7.5 MB) Collecting environs==7.3.1 Downloading environs-7.3.1-py2.py3-none-any.whl (11 kB) Collecting pytz Downloading pytz-2020.1-py2.py3-none-any.whl (510 kB) Collecting sqlparse>=0.2.2 Downloading sqlparse-0.4.1-py3-none-any.whl (42 kB) Collecting asgiref~=3.2 Downloading asgiref-3.3.0-py3-none-any.whl (19 kB) Collecting python-dotenv Downloading python_dotenv-0.15.0-py2.py3-none-any.whl (18 kB) Collecting marshmallow>=2.7.0 Downloading marshmallow-3.8.0-py2.py3-none-any.whl (46 kB) Installing collected packages: pytz, sqlparse, asgiref, Django, python-dotenv, marshmallow, environs Successfully installed Django-3.0.7 asgiref-3.3.0 environs-7.3.1 marshmallow-3.8.0 python-dotenv-0.15.0 pytz-2020.1 sqlparse-0.4.1 Removing intermediate container d38b7eee00eb ---> 658522092c7f Step 6/8 : COPY . /app ---> 7b9155a033ae Step 7/8 : ENTRYPOINT ["python3"] ---> Running in 5c4690dcbf9d Removing intermediate container 5c4690dcbf9d ---> b23991bc8043 Step 8/8 : CMD ["manage.py", "runserver", "0.0.0.0:8000"] ---> Running in 82d8e32ead64 Removing intermediate container 82d8e32ead64 ---> 026aa876dacf Successfully built 026aa876dacf Successfully tagged django_web:latest WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating django_web_1 ... done Attaching to django_web_1 web_1 | Watching for file changes with StatReloader
Everything is built and ready to go. Visit http://localhost:8000/ in the browser.
Whoa! If you have done this before manually, you can appreciate how much was just accomplished so easily. As we visit the page, the container logs out the visits to the console seen below.
web_1 | [28/Oct/2020 18:13:48] "GET / HTTP/1.1" 200 16351 web_1 | [28/Oct/2020 18:13:48] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423 web_1 | [28/Oct/2020 18:13:48] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 86184 web_1 | [28/Oct/2020 18:13:48] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 85692 web_1 | [28/Oct/2020 18:13:48] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 85876 web_1 | Not Found: /favicon.ico web_1 | [28/Oct/2020 18:13:48] "GET /favicon.ico HTTP/1.1" 404 1974
Let’s try Angular.
PS C:\code\awesome-compose> cd angular PS C:\code\awesome-compose\angular> docker-compose up
What about Vue?
PS C:\code\awesome-compose> cd vuejs PS C:\code\awesome-compose\vuejs> docker-compose up
I’ve been meaning to try Flask…
PS C:\code\awesome-compose> cd flask PS C:\code\awesome-compose\vuejs> docker-compose up
In the span of just a few minutes, we were able to spin up several different back end and front end frameworks with a small amount of effort. This is the power of Docker Compose.
Further Reading for Docker Compose
- Docs For Docker Compose (docs.docker.com)
- Docker Compose Getting Started (docs.docker.com)
- How To Use Docker Compose (linode.com)
- Containers With Docker Compose (code.visualstudio.com)
- Docker Compose On Github (github.com)
- Docker Tutorials Use Docker Compose (docs.microsoft.com)
- How To Use Docker Compose Tutorial With Examples (buildvirtual.net)
- How To Use The Docker Compose Command (techrepublic.com)
- How To Install And Use Docker Compose On Ubuntu 20 04 (digitalocean.com)
- How To Understand Building Images With Docker Compose (medium.com)
- Install And Use Docker Compose On Centos 7 (linuxize.com)
- Docker Compose Tutorial (educative.io)
- Docker Compose Tips (baeldung.com)
- Docker Compose Tutorial Beginners By Example (takacsmark.com)
- Volumes In Docker Compose (devopsheaven.com)
Docker Compose Tutorial Summary
- Used to configure relationships between containers
- Saves our
docker container runsettings in a single file
- Creates simple
docker-compose upenvironment startups
- Consists of two parts
- 1. A YAML file that defines:
- 2. The
docker-composeCLI used to process those YAML files
- YAML file is used with
docker-composecommand for local builds
docker-compose --helpcan give you good tips
- docker-compose.yml is the default filename but can be customized via
- Docker Compose CLI comes with Docker for Windows and Mac
- Perfect for local development and testing
- Two most common commands
docker-compose upsets up volumes, networks and starts all containers
docker-compose downstops all containers and removes containers, volumes, and networks
- Test some of the most popular software available in three easy commands
- git clone https://github.com/docker/awesome-compose.git
- cd software-to-run (django, angular, flask, vuejs, etc..)
- docker-compose up
- Compose can also build your custom images
- Will build them with
docker-compose upif not found in cache
- Great for complex builds that have lots of vars or build args