; Date: Mon Mar 08 2021
docker run CLI command is powerful, Docker Compose files let us succinctly describe a whole system of Docker containers, virtual networks, and file systems, in one easy to read file. This is a powerful tool with which you can easily describe deployment of a full application stack. Compose files support not only deployment to the local Docker host, but to Docker Swarm clusters, and now to AWS ECS and Azure ACI cloud platforms.
For a long time, Docker Compose has been a command added to the Docker environment but not well integrated. It was originally developed outside the Docker team, but for several years has been distributed alongside the core Docker products, and has been bundled with the Docker Desktop products.
Its core feature is the Compose file which gave us a concise format to describe Docker deployments. In one file we can describe the deployment parameters of multiple Docker Services (a.k.a. Containers) as well as other resources such as virtual networks, volumes, and secrets. It means we can describe a whole system using one easy-to-understand file, and we can bring that whole system up using one command.
This file format started out life in the
docker-compose command. With that command, the Compose file can deploy services to the local Docker host, or to a remote Docker host if you're using the Docker Context feature. This file is also supported with Docker Swarm by using the
docker stack deploy command. More recently, the Docker Team integrated a new command,
docker compose (note - no
-) which lets us use the Compose file with cloud platforms like AWS ECS and Azure ACI.
In short, the Docker team is positioning the Compose file as a universal format for describing Docker service deployment.
A recent change is the development of a formal specification for the Compose file. The Docker team has described a vision that, because of the Specification, other tool makers will now be able to implement Compose functionality.
To give a little taste:
version: '3.8' services: whoami: image: containous/whoami ports: - "80:80"
This is a simple Compose file that can be used, unchanged, for deployment to any of the platforms just named. It uses the YAML format giving a lot of flexibility. The
services tag contains the description of one or more Services, as the name implies. Each service is built using a Docker Image, and various parameters. The service description can also include environment variables, deployment parameters, and more. Remember that a Container is a Docker Image that is instantiated with specific configuration attributes. Hence, a Container and a Service is largely the same thing.
containous/whoami container is popular as an example since it is a web service that prints a lot of useful information. This particular example exposes the
whoami service on port 80.
That Compose file is equivalent to:
$ docker run --name whoami -p "80:80" containous/whoami
We can use either
docker run or Compose files to launch Docker services. Both expose the same Docker capabilities, meaning that there is much similarity between the two.
whoami container using Compose
To familiarize ourselves with
docker-compose let's run the Compose file shown above. Create a new directory, and create a file named
docker-compose.yml to contain the Compose file.
$ mkdir compose $ cd compose $ vi docker-compose.yml ... enter text $ docker-compose up Creating network "compose_default" with the default driver Pulling whoami (containous/whoami:)... latest: Pulling from containous/whoami 29015087d73b: Pull complete 0109a00d13bc: Pull complete d3caffff64d8: Pull complete Digest: sha256:7d6a3c8f91470a23ef380320609ee6e69ac68d20bc804f3a1c6065fb56cfa34e Status: Downloaded newer image for containous/whoami:latest Creating compose_whoami_1 ... done Attaching to compose_whoami_1 whoami_1 | Starting up on port 80
whoami image did not exist locally, it was downloaded from Docker Hub. Because we ran the
up command, it will attempt to launch the service(s) described in the Compose file, and in this case it is successful. The Attaching to message means that the standard output from the service(s) will be shown on the terminal. The last line is a message from the
whoami container saying it started on port 80.
To view the application visit
http://localhost in your browser.
To view the status run this command in another window:
$ docker-compose ps Name Command State Ports ------------------------------------------------------- compose_whoami_1 /whoami Up 0.0.0.0:80->80/tcp
This is similar to the
docker ps command, but a little bit different.
To bring the service down, run this command:
$ docker-compose down Stopping compose_whoami_1 ... done Removing compose_whoami_1 ... done Removing network compose_default
Over in the original command window, the
docker-compose command will exit.
Deploying MySQL using Docker Compose
That was a useful introduction to Compose files, but not very practical. To do something practical let's launch a MySQL service using a Compose file. Specifically, let's replicate the configuration used earlier when deploying Wordpress and PHPMyAdmin - Set up PHPMyAdmin and Wordpress Docker containers using existing MySQL
Remember in that case we used this command:
$ docker run --name mysql \ --env MYSQL_RANDOM_ROOT_PASSWORD=true \ --env MYSQL_USER=wpuser \ --env MYSQL_PASSWORD=w0rdw0rd \ --env MYSQL_DATABASE=wpdb \ --volume `pwd`/mysql-data:/var/lib/mysql \ --network wpnet \ mysql/mysql-server:5.7
With that database instance we went on to deploy PHPMyAdmin and Wordpress.
Rename the existing Compose file to
docker-compose-whoami.yml and create a new
docker-compose.yml containing this:
version: '3.8' services: mysql: image: "mysql/mysql-server:8.0.21" container_name: mysql command: [ "mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci", "--bind-address=0.0.0.0", "--default_authentication_plugin=mysql_native_password" ] # ports: # - "3306:3306" networks: - wpnet volumes: - type: bind source: ./database target: /var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: "r00tr00t" # MYSQL_ROOT_HOST: "%" MYSQL_USER: wpuser MYSQL_PASSWORD: w0rdw0rd MYSQL_DATABASE: wpdb networks: wpnet: driver: bridge
Notice how all the elements of the
docker run command are present in this Compose file. It's expressed a little differently, and we've moved to a later MySQL version, but everything is there.
The service name is
mysql as is the
container_name parameter. This will ensure a known container name, which we can use for accessing the database from other containers.
Using Wordpress with MySQL version 8 required specific server configuration parameters. Those parameters are specified in the
command attribute. This attribute lets us override the CMD instruction in the Dockerfile, changing the command used to launch the MySQL service. While we could have supplied a custom
my.cnf configuration file, the MySQL server lets us configure any setting using command-line options as shown here.
networks attribute lets us attach the service to one or more Docker networks. In this case we've defined
wpnet which will serve to connect services required to run Wordpress. At the bottom there is a top-level
networks tag with which we define this network.
The next attribute,
volumes, is the same as the
--volumes option on the
docker run command. The syntax here is a little different, and gives us more flexibility in defining the parameters. In this case we're simply mounting a host directory into the container as
/var/lib/mysql in order to persist the data directory outside the container.
restart attribute defines the policy to use if the container crashes. We're asking Docker to always restart the container.
environment, is where we define configuration settings. In this case we're defining the database name, user name, and password.
Launching and exploring MySQL using Docker Compose
Earlier to launch services in a Compose file we used
docker-compose up, so let's to it a little different.
$ mkdir database $ docker-compose up -d Creating mysql ... done
-d flag is short for
--detach which means the server will be brought up in the background.
mkdir command is required to avoid this error: ERROR: for mysql Cannot create container for service mysql: invalid mount config for type "bind": bind source path does not exist
To see the logging output, type this:
$ docker-compose logs -f Attaching to mysql mysql | [Entrypoint] MySQL Docker Image 8.0.21-1.1.17 mysql | [Entrypoint] Initializing database ...
As it says, this attaches to the containers, and lets you see their output. This will be useful in case of errors.
As before, we can see the status as so:
$ docker-compose ps Name Command State Ports --------------------------------------------------------------------------- mysql /entrypoint.sh mysqld --ch ... Up (healthy) 3306/tcp, 33060/tcp
We have a functioning database. Because the Compose file does not have a
ports tag, the MySQL port is not exposed to the public. We already know this means exploring the database requires executing commands inside the container.
$ docker-compose exec mysql bash bash-4.2#
While we could use the
docker exec command, this also works.
bash-4.2# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. ... mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | wpdb | +--------------------+ 5 rows in set (0.02 sec) mysql> ^DBye bash-4.2# mysql -u wpuser -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. ... mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | wpdb | +--------------------+ 2 rows in set (0.00 sec) mysql>
And as expected the
wpuser accounts are set up, and have different rights.
Here's the MySQL data directory:
$ ls database/ client-cert.pem ibtmp1 private_key.pem undo_002 binlog.000001 client-key.pem mysql public_key.pem wpdb binlog.000002 ib_buffer_pool mysql.ibd server-cert.pemauto.cnf
This came about because of the
volumes tag in the Compose file. This directory becomes
/var/lib/mysql inside the container, and the MySQL instance created these files during its initialization.
To shut down the MySQL service:
$ docker-compose stop Stopping mysql ... done
Notice that the
docker-compose command does not take the file name on the command line. Instead, it looks at the
docker-compose.yml file in the local directory. This means you can have one Compose project per directory, when using
docker-compose. When using a Compose file on Swarm (
docker stack) or AWS ECS or Azure ACI (
docker compose), we can use any file name for the Compose file.
In this tutorial we've started learning about Docker Compose by deploying a MySQL instance. We've learned that there are parallels between the
docker commands, and we've learned a little about the syntax of Compose files.