Docker tutorial - dive into docker-compose
Tags: tutorial, beginners, docker, devops
Published: 2023-01-30 18:48:07

#published 
## Content 
- [Intro](#intro)
- [What is Yaml?](#what-is-yaml?)
- [The essential syntax](the-essential-syntax)
- [Further explanation about services](#further-explanation-about-services)
- [Build with compose commands](#build-with-compose-commands)
- [Conclusion](#conclusion)

## Intro
Now you should be able to run the Docker container with your image. In reality, there will be more services involved in software development. You might have your core service, database, cache, and more when you are building the backend. We can run Docker containers with the CLI and connect to components, yet it can be tricky. Docker provides a tool `Compose` to run multi-containers with ease. 

## What is Yaml?
Compose uses the YAML file to configure the multi containers. Yaml is one of the DSLs and is usually used for configuration purposes. 

## The essential syntax
Compose also offers a quite few syntaxes but I will cover only the most used from my own experience. 

```
services:
  frontend:
    image: awesome/webapp
    ports:
      - "443:8043"
    networks:
      - front-tier
      - back-tier
    configs:
      - httpd-config
    secrets:
      - server-certificate

  backend:
    image: awesome/database
    volumes:
      - db-data:/etc/data
    networks:
      - back-tier

volumes:
  db-data:
    driver: flocker
    driver_opts:
      size: "10GiB"

configs:
  httpd-config:
    external: true

secrets:
  server-certificate:
    external: true

networks:
  # The presence of these objects is enough to define them
  front-tier: {}
  back-tier: {}

```

According to the official docs, a Compose file must declare the `services` top element. Values will be service definitions. And the service contains the configuration that will be applied to each container. There are frontend and backend services in our case. A Compose file can have `volumes` as a top element, which serves as a persistent layer. A Compose file can also have `networks` as a top element. Any containers belonging to the networks can communicate. For example, frontend and backend services can communicate  via the back-tier network. Configs and secrets are read-only elements for configuration purposes.

## Further explanation about services
In most cases, `services` will be our main interest. Let's deep dive into it. 

```
web:
	build: .
    image: awesome/web
    ports:
      - "3000:3000"
    networks:
      - front-tier
      - back-tier
    depends_on:
      - db:
	      condition: service_healthy
	environment:
	  - SERVER_ENVIRONMENT: development
    configs:
      - httpd-config
    secrets:
      - server-certificate
    volumes:
      - db-data:/etc/data
```

`build` specifies the build instruction. `.` tells the compose to find the Dockerfile in the current directory. If the build exists, it's okay to omit image declaration. 

`image` specifies the image to start the container from. The Compose will try to install the image first if not existed. Then it will run other instructions.

`ports` specifies the exposed ports. The syntax is `HOST[ip:]port:CONTAINER's port`
`3000:3000` means to map port 3000 in the machine to port 3000 in the container. 

`depends_on` specifies the dependencies in start-up and shutdown. In the above example, Compose will create db. Then it will wait until the db is healthy before creating the web. This can be useful when we have dependencies and services that we have to create in order.

`environment` specifies the variables set in the container. You can also specify the variables with `env_file` . If both are present, the environment will take precedence. 

`volumes` specifies the instruction on how to mount the volume for persisting data. The syntax is `[SOURCE VOLUME:]CONTAINER_PATH`. If the source volume is empty, the compose will create a directory. Then it will mount it as a volume to the target path inside the container. 

From my experience, these specifications have covered the majority of use cases.

## Build with compose commands 
The typical workflow for development will be code, build, test, and push. You can take advantage of the compose to speed up your development process. I see this quite useful when a team onboards newcomers. Compose can save the setup time for new comers when they are not familiar with the system yet, 

Here are a few useful CLI for your disposal.

`docker-compose up` create and start containers. If pass the parameter `-d`,  you can run them in detached mode.
`docker-compose down` stop and remove containers, networks, images, and volumes.
`docker-compose stop` stop services without any destructive operations.
`docker-compose logs` shows the logs from the containers. 
`docker-compose exec` executes a command in a running container. 

With these commands, you can manage many containers without hassle.

## Conclusion
I covered the concept of docker-compose and useful commands. Try to run multi containers of yours. I will cover useful commands and aliases you can apply to your workflow in the next article.

If you like this content, follow
- @bitethecode
- [Github](https://github.com/bitethecode)
- [Substack](https://bitethecode.substack.com)
- [Youtube](https://www.youtube.com/@the-writer-dev)

## Links
- https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
- https://docs.docker.com/compose/compose-file/