Before we start to talk about docker-compose, ensure you have installed Docker and Docker Compose.
If your project/app uses multiple containers then it will be unreasonable trying to run the individual containers separately and then linking them. It is possible but tiresome, brings complexity in scaling and not one of DevOps recommendations. So here comes docker-compose to the rescue. Docker-compose helps you to run multiple containers, linking them and defining various container properties in one file. This file is called docker-compose.yml.
This file contains all the defined container properties and their images to be used in a specified project/app. It has changed over the years from version 1 to version 3. So it is a requirement to specify which version you are using at the start of each docker-compose.yml file. Docker-compose files are written in YAML format and are in the format of docker-compose.yml. A typical docker-compose file looks like this:
version: "3" services: db: image: postgres environment: POSTGRES_USER: zuri POSTGRES_DB: zuri POSTGRES_PASS: zuri1234 volumes: - pgdata:/var/lib/posgresql/data zuri: build: context: . ports: - "8000:8000" volumes: - ./zuri:/zuri command: python manage.py runserver 0.0.0.0:8000 depends_on: - db volumes: pgdata:
The docker-compose file consists of various components. I won’t talk about all of them but I will touch on some important ones. Essentially the compose file defines services that control the containers. From the above example you can see I have two services, db and zuri. The db service is my database container while the zuri service is my project. The two communicate via the property depends_on. Which tells docker-compose to create the service should it not exist when running zuri service.
Under each service you can define many properties like ports to expose, volumes to use, network e.t.c. Of course, each container uses an image and therefore you can directly define the image in the docker-compose file or specify a Dockerfile to be used. In this example, you can see in zuri service I have defined build context with a . this tells docker-compose to look for a docker file in the same directory to build an image for the service. But the database service has the image Postgres directly defined.
Here is the zuri service dockerfile:
#base image FROM python:3 #maintainer LABEL Author="CodeGenes" # The enviroment variable ensures that the python output is set straight # to the terminal with out buffering it first ENV PYTHONBUFFERED 1 #directory to store app source code RUN mkdir /zuri #switch to /app directory so that everything runs from here WORKDIR /zuri #copy the app code to image working directory COPY ./zuri /zuri #let pip install required packages RUN pip install -r requirements.txt
Docker container lifecycle
Docker containers are ephemeral, that is, they are deleted when you delete the container. Their status depends on the status of the applications they host. The various states include:
1. Create container
$ docker create –name <container-name> <image-name>
2. Run container
$ docker run -it -d –name <container-name> <image-name>
3. Pause container
$ docker pause <container-id/name>
4. Unpause container
$ docker unpause <container-id/name>
5. Start container
$ docker start <container-id/name>
6. Stop container
$ docker stop <container-id/name>
7. Restart container
$ docker restart <container-id/name>
8. Kill container
$docker kill <container-id/name>
9. Remove/delete container
$docker rm <container-id/name>
Compose of course being that it manages the containers, it also contains the various container commands to manage the whole container lifecycle. Some of the commands include start, stop, build view e.t.c
To view these commands and what they do, run this on your terminal:
$ docker-compose --help Define and run multi-container applications with Docker. Usage: docker-compose [-f <arg>...] [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) --verbose Show more output --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 (for example if your docker host is an IP address) --project-directory PATH Specify an alternate working directory (default: the path of the Compose file) Commands: build Build or rebuild services bundle Generate a Docker bundle from the Compose file 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 the Docker-Compose version information
Docker-compose run, up, exec – what’s the difference?
Well, when I started to use docker, I would notice that the tutorials often used docker-compose up, docker-compose run and docker-compose exec mostly without explaining. I know you can simply read the difference in the docs of the help command line but sometimes it is helpful to fully appreciate the difference by digging a little deeper.
So which one should you use and at what point? I will illustrate these differences using my Django docker project on GitHub.
Docker Compose Up
This command is used when you want to start or bring up all services in your docker-compose.yml file. Docker-compose.yml file defines your services, their properties, variables, and dependencies.
$ docker-compose up
To view containers running:
$ docker-compose ps
You can also tell docker-compose to run only one service e.g.
$ docker-compose up zuri
Docker Compose Run
This command will spin up a new container for you to use. It is mostly used when you want a new container or the container is not running and is a one-off process which avoids conflicts with other services in your
docker-compose.yml that might be running.
$ docker-compose run zuri python manage.py migrate
Docker Compose Exec
This command is used when you want to interact with a container that is already running. It, therefore, requires to be run with a command argument. Before you exec the container must be in running state!
$ docker-compose exec zuri bash
That’s it for now. Peace Out!