In this post, I will write about docker and docker-compose as I learn them. I will try to explain the concepts and the commands I use. I will also try to explain the differences between docker and virtual machines.

Introduction

First of all let’s start with some definitions:

Containers are a way to package an application and its dependencies together. Containers are isolated from each other and bundle their own software, libraries and configuration files; they can communicate with each other through well-defined channels. All containers are run by a single operating system kernel and are thus more lightweight than virtual machines since they do not need to emulate a full hardware stack.
Containers are created from images that specify their precise contents. Images are often created by combining and modifying standard images downloaded from public repositories.

Docker is a set of platform as a service (PaaS) products that use container virtualization to deliver software in packages called containers.
Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and ship it all out as one package. By doing so, thanks to the container, the developer can rest assured that the application will run on any other Linux machine regardless of any customized settings that machine might have that could differ from the machine used for writing and testing the code.

To create an image from scratch using docker, you can use a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build you can create an automated build that executes several command-line instructions in succession.

Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

Docker installation

To install docker on Fedora 36, you can use the following procedure:

  • Install the docker repository:
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager \
  --add-repo \
  https://download.docker.com/linux/fedora/docker-ce.repo
  • Install docker:
sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  • Install docker-desktop:
sudo dnf install docker-desktop-XX.YY.ZZ.x86_64.rpm

First steps

To start docker, you can use the following command:

sudo systemctl start docker

To check if docker is running, you can use the following command:

sudo systemctl status docker

To enable docker at boot, you can use the following command:

sudo systemctl enable docker

To add your user to the docker group in order to run docker commands without sudo, you can use the following command:

sudo usermod -aG docker $USER

To run an example hello-world container, which is a simple container that prints a message, you can use the following command:

docker run hello-world

As you can notice, the container image is downloaded from the docker hub, than a container is created from the downloaded image and launched. The docker hub is a registry of docker images. You can find a lot of images on the docker hub. You can also create your own images and upload them to the docker hub.

To list all the containers, you can use the following command:

docker ps -a

To remove a container, you can use the following command:

docker rm <container_id>

Docker images

To list all the images, you can use the following command:

sudo docker images

To remove an image, you can use the following command:

sudo docker rmi <image_id>

Dockerfile

To create a Dockerfile, you can use the following command:

sudo docker build -t <image_name> .

To run a container from an image, you can use the following command:

sudo docker run -d -p 80:80 <image_name>

The -d option runs the container in detached mode (the background). The -p option maps the container port 80 to the host port 80.

Docker file example

In this example, we will create a docker image that runs a postgresql database. The docker image will be based on the official postgresql image. The docker image will be created using a Dockerfile.

The Dockerfile:

FROM postgres:latest
ENV POSTGRES_PASSWORD=postgres
ENV POSTGRES_USER=postgres
ENV POSTGRES_DB=postgres

The docker image can be created using the following command:

sudo docker build -t postgresql .

The -t option specifies the name of the image. The dot at the end of the command specifies the path to the Dockerfile.

The docker image can be run using the following command:

sudo docker run -d -p 5432:5432 postgresql

The -d option runs the container in detached mode (the background). The -p option maps the container port 5432 to the host port 5432.

Docker compose

The docker compose tool is used to run multiple containers. The docker compose tool uses a docker-compose.yml file to define the containers and their configuration.

Docker compose file example

In this example, we will create a docker-compose.yml file that runs a postgresql database and a pgadmin4 container. The postgresql container will be based on the official postgresql image. The pgadmin4 container will be based on the official pgadmin4 image.

The docker-compose.yml file:

version: '3.8'

services:
  postgresql:
    image: postgres:latest
    container_name: postgresql
    restart: always
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    ports:
      - 5432:5432
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    networks:
      - postgresql_network

  pgadmin4:
    image: dpage/pgadmin4:latest
    container_name: pgadmin4
    restart: always
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@example.com
      PGADMIN_DEFAULT_PASSWORD: admin
    ports:
      - 80:80
    volumes:
      - pgadmin4_data:/var/lib/pgadmin
    networks:
      - postgresql_network
      
volumes:
  postgresql_data:
  pgadmin4_data:

networks:
  postgresql_network:

The docker-compose.yml file can be run using the following command:

sudo docker compose up -d --build

The -d option runs the containers in detached mode (the background). The –build option builds the containers before running them.

I usually use custom names for docker compose files. For example, I use the following command to run the docker-compose-CUSTOM.yml file:

  sudo docker compose -f docker-compose-CUSTOM.yml up -d

Docker compose networks

Networks are used to connect containers. The docker compose tool creates a default network for the containers. The containers can be connected to the default network or to a custom network. The docker compose tool can create a custom network.

A custom network can be created using the following command:

sudo docker network create <network_name>

The network can be removed using the following command:

sudo docker network rm <network_name>

The docker compose file can be used to create a custom network as shown in the postgresql and pgadmin4 example above.

Docker compose volumes

Volumes are used to store data. The docker compose tool can create volumes. The docker compose tool can also use existing volumes.

A volume can be created using the following command:

sudo docker volume create <volume_name>

The volume can be removed using the following command:

sudo docker volume rm <volume_name>

The docker compose file can be used to create a volume as shown in the postgresql and pgadmin4 example above.

Docker compose ports

Ports are used to expose the container ports to the host. The docker compose file can be used to expose the container ports as shown in the postgresql and pgadmin4 example above.

Docker compose environment variables

Environment variables are used to set the environment variables for the container. The docker compose file can be used to set the environment variables as shown in the postgresql and pgadmin4 example above.

Docker compose restart policy

The docker compose file can be used to set the restart policy for the container. The following restart policies are available:

  • no - Do not restart the container if it stops or crashes
  • on-failure - Restart the container if it stops or crashes, but only if the container exits with an error code
  • always - Always restart the container if it stops or crashes
  • unless-stopped - Always restart the container unless it is explicitly stopped

The docker compose file can be used to set the restart policy as shown in the postgresql and pgadmin4 example above.

Docker compose healthcheck

The docker compose file can be used to set the healthcheck for the container. The following healthcheck options are available:

  • disable - Disable the healthcheck
  • interval - The interval between healthchecks
  • timeout - The timeout for the healthcheck
  • retries - The number of retries before the container is considered unhealthy
  • start_period - The time to wait before starting the healthchecks
  • test - The command to run to check the health
  • none - Do not perform any healthchecks
  • cmd - Run a command inside the container

The following docker compose file can be used to set the healthcheck for the postgresql container:

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

The following command can be used to check if the postgresql container is healthy:

sudo docker inspect --format='' postgresql

If the container is healthy, the following output will be displayed:

{"Status":"healthy","FailingStreak":0,"Log":[{"Start":"2021-07-05T13:59:00.000000000Z","End":"2021-07-05T13:59:00.000000000Z","ExitCode":0,"Output":"2021-07-05 13:59:00.000 UTC [1] LOG:  listening on IPv4 address \"
...

Docker compose depends_on

Docker compose build

Docker compose file version

Docker compose file reference

Docker cleanup

In most cases after you have finished with experiments with a docker compose file, you will have a lot of images, containers, volumes and networks that you don’t need anymore. To remove all the unused images, containers, volumes and networks, you can use the following command:

sudo docker compose down --rmi all --volumes --network all --remove-orphans

The –network all option removes all the networks. The –rmi all option removes all the images. The –volumes option removes all the volumes. The –remove-orphans option removes all the orphan containers.

Leave a comment