docker compose
: https://docs.docker.com/compose/compose-file/docker-compose.yml
file in the root of the repository, or view it online:docker-compose up
looks for a docker-compose.yml
file in the current directory and translates the instructions in that file to a series of docker run
CLI commands (behind-the-scenes; you won't see this part). The stdout
output of each container will go to your console window unless you add the option-d
for --detach
(ed); that output will still be available to you in Docker's logs but you'll get your console back right away.#
in a compose file, so we can comment on the same line as our directives.docker-compose.yml
:docker-compose.yml
that tracks which directives are included and deprecated over time. Which versions of docker-compose.yml map to which versions of the Docker engine are available in the official document reference, but unless you know you have to support an older version of Docker, use a version corresponding to a recent stable
Docker CE release.docker volume
, a virtual, persistent storage device managed by the Docker daemon. We can remove our container entirely and it will persist unless we explicitly delete the volume using docker volume delete
.volumes
directive in docker compose
and docker stack
, which governs both docker volumes and bind mounts. Both involve persistent storage, but a bind mount takes a directory that already exists on the host and mounts it into the container. This means that the host (rather than Docker) is managing the filesystem. Bind mounts are slower than docker volumes -- especially on Windows and MacOS. The benefit of bind mounts is that they allow for easy read and write access between the host and the container, which is what we want for a development environment; on a regular docker volume, you can only copy files between the host and the container through the docker copy
command, unless your container supports some other form of file sharing (e.g. NFS). volumes
directive that specifies the source and target mount point for each volume it requires.networks
directive is to create a "project" network that applies to any service created in the same directory. This would work just fine, but it's best to name your networks so that you can assign each container to one or more of them explicitly. Docker networking is fairly complicated under the hood, but for our purposes, we just need to know which containers need to be able to access which other containers (and so should share a network). Our simple solution here places all of our containers on one network so that each container can access any other container by its name; we'll take advantage of this when configuring our nginx proxy and our CF datasources.networks
directive just names the network we'll be referring to later on.networks
directive, the top-level secrets
directive defines a "Docker Secret" that we'll refer to in each of our services. We'll cover this in more detail below; for now, it's sufficient to know that we want to define individual pieces of data or files that we will mount into one or more containers, and that they're called secrets because this is a mechanism that is used to great effect for storing credentials and any other sensitive information we don't want accessible to anything outside our container.[registry address]/<image name>:[tag]
:latest
tag. This can have unintended consequences -- you might upgrade your database or your web server without realizing that you've done so. :latest
for MySQL is currently version 8, but we'll get MySQL 5.7 because we've specified that tag. The README for a Docker image will usually document available tags (like the official Docker Hub page for MySQL does/), and a full list is often available under the tags link on that page.docker container ls
, we want to be able to refer to our containers quickly and easily, whether we're outputting logs (docker logs cfswarm-mysql
) or logging in to the container (docker exec -it cfswarm-mysql bash
).container_name
directive when you have a single container in your service. If we were replicating our service -- running multiple, identical containers in parallel -- container_name
would be forbidden.docker-compose.yml
. These particular variables set the root password, create a database called cfswarm-simple-dev
(if it doesn't already exist), routes the log to the console, and allows connections from any host.volume
directive under a service, you'll know to look for a top-level volume configuration reference. Because volumes can be shared between services, only the service-specific information goes in the service-level definition.ports
directive maps one or more ports on the service container to the specified port on the host. In this example, we're mapping MySQL's port 3306 to the same port on our host. Without this directive, our other containers could access MySQL since they all share a network
, but we wouldn't be able to connect to MySQL using any tools on our host unless we ran those tools in Docker as well.volumes
and some other docker compose
directives, ports
has both a short form (shown above) and a long form that can be easier to read. Here is the same directive in long form:networks
directive refers back to the top-level network
name we defined earlier. Every container that needs to access that network should have a service-level directive referring to it.mysql
service, but with a few alternatives and additions.volume
, but instead of letting Docker create and manage the volume, we're "binding" a directory on the host into the container. This is generally not suitable for production, but if we're actively developing an app then Docker isn't much use to us if we can't read and write to our app folder while it's running.docker compose.yml
. The effect is the same -- the container created for your service doesn't care whether you put your environment variables in your compose file or somewhere else, but the best practice is to keep them separate.secrets
directive refers back to the secret we defined at the top level. But what is a secret, and why are we using it?docker configs
are not (yet) supported by docker compose
-- they're meant for docker stack
deployments, which we'll use in production.docker secrets
are for files containing sensitive information: credentials, encryption keys, anything that you don't want to put somewhere easily accessible (such as an environment variable or a layer of your Docker image). These are meant to be supplied to Docker once and then referenced in deployments, rather than read from a file. config
is really what we need, but since it's not supported in docker compose
, we'll use a secret
instead. The syntax is nearly identical; much like volumes
and networks
, secrets can be shared between services, so this service-level definition only supplies the source
(the name of the secret in the top-level secrets
config) and the target
(name of the file to mount into the /run/secrets
folder in our container).docker compose
that this service requires other services to be running before it can start. Since our CF engine isn't much use to us without NGINX and MySQL, docker compose
will start these services prior to starting cfswarm-cfml
. Note that Docker doesn't check to see if the services are actually ready -- health checks are more involved and beyond the scope of our development stack. Even so, we want Docker to bring up MySQL and NGINX before starting the CF container.Dockerfile
that governs how the image is built. Our CF containers are running a script that triggers Commandbox's server start
. NGINX's Dockerfile starts the container with the nginx
command, but for development we'll run NGINX in DEBUG mode.CMD
directive from the Dockerfile with the command
directive in docker-compose.yml
:nginx-debug -g daemon off
.