Docker windows docker compose yaml

В этой статье рассмотрим, как использовать Docker Compose для управления многоконтейнерными приложениями. Разберем, зачем нужен Docker Compose и как он помогает решать задачи разработки, тестирования и развертывания. Узнаем, как создавать и настраивать контейнеры для приложений с помощью файла docker-compose.yml. Расскажем, какие команды будут полезны в работе с контейнерами и как настраивать пользовательские сети для взаимодействия компонентов внутри проекта.

Зачем нужен Docker Compose

Docker Compose — это инструмент управления многоконтейнерными приложениями. Он упрощает разработку, развертывание и тестирование сложных проектов, где используются несколько сервисов. Ими могут быть веб-серверы, базы данных, приложения и другие компоненты.

В отличие от Docker, который работает с отдельными контейнерами, Docker Compose ориентирован на управление несколькими контейнерами одновременно. Он описывает их конфигурацию в одном файле — docker-compose.yml. Это делает его незаменимым инструментом для разработчиков и DevOps-инженеров, которые хотят автоматизировать сборку, запуск и настройку сети между контейнерами.

Сравнивать Docker Compose с Docker корректно, но важно понимать их различия. Docker — это инструмент для создания и управления отдельными контейнерами. Docker Compose же предназначен для работы с группами контейнеров, объединенных в сервисы.

Например, с помощью Docker Compose можно одной командой запустить весь проект, включая веб-сервер, базу данных и приложение, а также настроить сеть между ними.

docker-compose up

Это значительно ускоряет разработку и упрощает управление зависимостями между компонентами. За это Docker Compose и ценят в современных IT-проектах.

Что такое YAML

YAML — это человекочитаемый формат данных, который часто используется для создания конфигурационных файлов, например docker-compose.yml. Формат прост и имеет понятный синтаксис. Это делает его идеальным для описания конфигураций сервисов, сетей, портов и других параметров в Docker Compose. 

В YAML используются отступы для обозначения структуры, что позволяет легко описывать сложные зависимости и настройки. Например, из файла docker-compose.yml можно указать, какие образы использовать, как настроить сеть между контейнерами, какие порты открыть на хосте и как управлять переменными окружения. Благодаря своей гибкости и удобству, YAML стал стандартом для работы с Docker Compose и другими инструментами в сфере разработки и DevOps.

Создание проекта для запуска в Docker Compose

Запуск проекта с использованием Docker Compose предполагает создание и настройку нескольких контейнеров, которые работают вместе. В этом разделе мы рассмотрим, как создать контейнеры для Nginx и PHP, чтобы они могли взаимодействовать друг с другом. Это типичный сценарий для веб-приложений, где Nginx выступает в роли веб-сервера, а PHP обрабатывает backend-логику.

Создание контейнера для Nginx

Nginx — высокопроизводительный веб-сервер, который часто используется для обслуживания статических файлов и проксирования запросов к backend-сервисам. Для создания контейнера с Nginx необходимо:

  1. создать Dockerfile,
  2. настроить конфигурацию Nginx,
  3. описать сервис в docker-compose.yml.

Пример Dockerfile для Nginx:

    FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
COPY ./html /usr/share/nginx/html

Пример минимальной конфигурации для проксирования запросов к PHP (nginx.conf):

    server {
    listen 80;
    server_name localhost;

    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;  # Связь с PHP-контейнером
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Описание сервиса в docker-compose.yml:

    services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./html:/var/www/html
    depends_on:
      - php
    networks:
      - app_network

Создание контейнера для PHP

PHP-контейнер будет обрабатывать backend-логику приложения. Для его создания необходимо:

  1. использовать официальный образ PHP с поддержкой FPM (FastCGI Process Manager),
  2. настроить PHP для работы с Nginx,
  3. описать сервис в docker-compose.yml.

Пример Dockerfile для PHP:

    FROM php:8.2-fpm
RUN docker-php-ext-install pdo_mysql

Описание сервиса в docker-compose.yml:

    services:
  php:
    image: php:8.2-fpm
    container_name: php
    volumes:
      - ./php:/var/www/html  # Проброс директории с PHP-кодом
    networks:
      - app_network

Создание файла docker-compose.yml и настройка проекта

Чтобы Nginx мог передавать запросы PHP-FPM, необходимо:

  1. убедиться, что оба контейнера находятся в одной сети (app_network),
  2. указать в конфигурации Nginx правильный адрес PHP-контейнера (в нашем примере это php:9000, где php — имя сервиса в docker-compose.yml).

Итоговый docker-compose.yml:

    version: '3.8'
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/html:/var/www/html
    depends_on:
      - php
    networks:
      - app_network

  php:
    image: php:8.2-fpm
    container_name: php
    volumes:
      - ./php:/var/www/html
    networks:
      - app_network

networks:
  app_network:
    driver: bridge

Создайте файл index.php в директории php/:

    <?php
echo "Hello from PHP!";
?>

Запустите проект командой:

docker-compose up -d

Откройте браузер и перейдите по адресу http://localhost. Вы увидите сообщение «Hello from PHP!». Оно означает успешную работу связки Nginx и PHP.

Пример структуры проекта:

    project/
├── docker-compose.yml
├── nginx/
│   ├── nginx.conf
│   └── html/
├── php/
│   └── index.php

Работа с контейнерами в Docker Compose

Docker Compose предоставляет удобный способ управления контейнерами, начиная от их сборки и запуска, заканчивая остановкой и удалением. 

Сборка контейнеров в Docker Compose

Используйте команду:

docker-compose build

Она позволяет создать образы на основе инструкций, указанных в docker-compose.yml.

Запуск всех сервисов

docker-compose up

Запуск контейнеров в фоновом режиме

Для этой задачи можно добавить в команду флаг -d:

docker-compose up -d

Это особенно полезно для развертывания приложений на сервере.

Остановка контейнеров без их удаления

Это делается командой:

docker-compose stop

Она приостанавливает работу всех сервисов, описанных в docker-compose.yml, но сохраняет их состояние.

Остановка и полное удаление контейнеров, сети и тома

Выполните команду:

docker-compose down

Удаление образов

Чтобы удалить образы, необходим дополнительный флаг —rmi all:

docker-compose down --rmi all

Удаление томов

А для удаления томов добавьте флаг —volumes:

docker-compose down --volumes

Выполнение команд внутри запущенного контейнера

Для выполнения команд внутри запущенного контейнера используется команда:

docker-compose exec <service_name> <command>

Здесь <service_name> — это имя сервиса, а <command> — команда, которую нужно выполнить. Например, чтобы открыть терминал в контейнере с PHP, выполните:

docker-compose exec php bash

Зависимости между сервисами

Зависимости между сервисами задаются в docker-compose.yml с помощью опции depends_on. Например, чтобы база данных запускалась перед приложением:

    services:
  app:
    depends_on:
      - db

Для запуска контейнера от имени определенного пользователя, нужно настроить опцию user в конфигурации:

    services:
  app:
    user: "<user_id_or_user_name>:<group_id_or_group_name>"

Проброс папок между хостом и контейнером настраивается через volumes. Например:

    services:
  app:
    volumes:
      - ./app:/var/www/html

Переменные окружения задаются с помощью environment в docker-compose.yml:

    services:
  db:
    environment:
      MYSQL_ROOT_PASSWORD: password

Для просмотра логов конкретного сервиса применяется команда:

docker-compose logs <service_name>

Например, если нужно, чтобы nginx показал логи веб-сервера, нужно запустить команду:

docker-compose logs nginx

Проброс портов настраивается через ports. Например, чтобы связать порт 80 на хосте с портом 8080 в контейнере, выполните:

    services:
  web:
    ports:
      - "80:8080"

Лимиты на использование CPU и памяти задаются через deploy.resources.limits:

    services:
  app:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

Метки добавляются для удобства управления контейнерами. Например:

    services:
  web:
    labels:
      com.example.description: "Web service"

Docker Compose делает процесс работы с контейнерами простым и эффективным, автоматизируя множество рутинных задач.

Работа с сетью

Создание пользовательских сетей

Docker Compose позволяет создавать пользовательские сети для улучшения управления коммуникацией между контейнерами. По умолчанию Compose создает одну сеть для всех сервисов, но можно определить дополнительные сети в файле docker-compose.yml. Например, чтобы создать сеть с именем app_network, используется следующий код:

    networks:
  app_network:
    driver: bridge

Чтобы подключить к этой сети нужные сервисы, нужно указать их в разделе networks каждого сервиса. Это полезно для сложных проектов, где требуется разделение сетевых ресурсов.

Изоляция сетей

Изоляция сетей позволяет ограничить взаимодействие между контейнерами, что повышает безопасность и упрощает управление. В Docker Compose можно создать несколько сетей и подключить к ним только определенные сервисы. Например, база данных может быть изолирована в отдельной сети, доступной только для backend-сервисов. Это делается через конфигурацию networks в docker-compose.yml:

    services:
  db:
    networks:
      - db_network
  backend:
    networks:
      - db_network
      - app_network

Здесь сервис db изолирован в сети db_network, а backend имеет доступ как к db_network, так и к app_network.

Коммуникация между контейнерами

Docker Compose упрощает коммуникацию между контейнерами, автоматически настраивая DNS-имена для каждого сервиса. Это позволяет обращаться к контейнерам по имени сервиса, указанному в docker-compose.yml. Например, если у вас есть сервис db, вы можете подключиться к нему из другого контейнера, используя имя db как хост. Пример:

    services:
  db:
    image: postgres
  app:
    image: my_app
    environment:
      DB_HOST: db

В этом случае сервис app сможет подключиться к базе данных, используя db как хост. Docker Compose автоматически настроит сеть и DNS, чтобы обеспечить корректную коммуникацию между контейнерами.

Читайте также:

Эта публикация — текстовый вариант и сценарий для видео на YouTube (оно удобно разбито на эпизоды).

  • Плейлист на YouTube

  • Docker для новичков — #2 Все инструкции Dockerfile

  • Docker для новичков — #4 Оптимизация Dockerfile

Привет, сегодня я расскажу о том что такое Docker compose файл, из чего он состоит и как его написать.

Docker compose — команда Docker, которая позволяет запустить несколько контейнеров в Docker. Благодаря Compose-файлам можно описать взаимодействие контейнеров, правила их запуска и работы, сделать отдельный файл, который позволит запускать мультиконтейнерные приложения в помощью одной команды.

У compose есть официальная спецификация, которая детально рассказывает о том, как ее реализация должна работать. Ссылку я оставлю в описании. Ее не то чтобы стоит читать, так как там очень много текста, который вам не поможет так хорошо как эта статья или документация с сайта Docker, но знайте, что спецификация есть и к ней можно обратиться в случае каких-то проблем.

Чтобы воспользоваться Compose, вам необходимо выполнить лишь три шага:

  1. Создайте Dockerfile для вашего приложения, либо же воспользуйтесь docker image с какого-нибудь репозитория, например, DockerHub

  2. Создайте Docker compose файл, в котором опишите сервисы, которые должны работать вместе в изолированной среде.

  3. Запустите docker compose up и Docker запустит все ваше приложение

Compose application model

Спецификация Compose позволяет определить платформонезависимое контейнерное приложение. Такое приложение является набором контейнеров, которые должны работать совместно с адекватным разделением ресурсов и каналом коммуникации.

В Docker используется термин mount, который используется для обозначения привязки какого-то ресурса, например папки с файлами, к контейнеру. Я не нашел красивого русского обозначения, поэтому буду употреблять слово “связан”. То есть это не абстрактная связь, а конкретная связь контейнера и каких-то данных вне контейнера. Учитывайте это по ходу видео.

Компоненты приложения называются сервисами.

service — абстракция и по сути просто конфигурация того, какой конкретный image и с какими настройками должен быть запущен. То есть запуская сервис много раз вы должны получить один и тот же результат.

Сервисы взаимодействуют между собой через сеть (network). В Compose network — это еще одна абстракция, которая позволяет устанавливать IP соединения между контейнерами, внутри которого сервисы могут коммуницировать друг с другом.

Сервисы хранят и разделяют между собой данные в volumes. Мы говорили немного о volumes в прошлой статье о Dockerfile. Если не смотрели его, то посмотрите, там много интересного и подробного. Спецификация compose описывает то какие данные и где должны храниться.

Некоторые сервисы требуют конфигурационные данные для работы, поэтому есть отдельный концепт — config. С точки зрения контейнера configs похожи на volumes — так как это тоже файлы, которые могут быть связаны с контейнером и читаться им.

secret — тип конфигурационных данных для чувствительных данных. Чувствительные данные — это пароли, ключи и все то, что не должно попасть в чужие руки. Секреты так же могут быть связаны с контейнером.

project — это индивидуальное размещение (деплой) приложения на конкретной платформе. То есть тот docker compose файл, который вы напишите — это отдельный проект. Имя проекта используется для того, чтобы группировать ресурсы вместе и изолировать их от других приложений или других таких же запущенных проектов.

Давайте рассмотрим пример приложения. Сначала он может показаться сложным, однако к концу статьи вы с легкостью сможете его прочитать и разобраться.

Изображение взято из документации Docker.

Изображение взято из документации Docker.

Это пример приложения с фронтендом и бэкендом.

frontend сконфигурирован в рантайме при помощи HTTP Configuration файла, а также HTTPS сертификатом, который встроен в secret store приложения — то место, где хранятся секреты.

backend хранит данные в постоянном хранилище. Оба сервиса взаимодействуют между собой при помощи изолированной backend сети, фронтенд также состоит во фронтенд сети, открывая https порт 443 наружу.

Это приложение состоит из следующих частей:

  • 2 сервиса — frontend с image webapp и backend с image database

  • 1 секрета — HTTPS-сертификата, включенных во frontend

  • 1 конфигурации, включенной во frontend

  • 1 постоянного хранилища — volume, связанного с backend

  • 2 сетей — frontend и backend сети

Так описан compose-файл

services: 
  frontend: 
    image: example/webapp    
    ports:    
      - "443:8043" 
    networks:  
      - front-tier  
      - back-tier    
    configs:    
      - httpd-config 
    secrets:    
      - server-certificate
  backend:  
    image: example/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: 
  front-tier: {} 
  back-tier: {}

В этом примере показаны различия между volume, secret и config:

  • они все связаны с контейнером, однако только volume может изменять данные

  • secrets и configs readonly

Compose file

Compose файл — это .yaml файл, который определяет конфигурацию приложения, а именно версию файла, сервисы — контейнеры, которые должны быть запущены, сети, volumes, конфигурации и secrets.

По умолчанию используется имя compose.yaml или docker-compose.yaml, однако предпочтительно использовать первый вариант compose.yaml

Несколько compose файлов могут быть объединены вместе. Такое объединение перезаписывает или дополняет другой compose файл, собирая несколько файлов в один.

Вы можете использовать фрагменты, расширения или include-команду для работы с несколькими Compose файлами.

YAML формат стал популярным относительно недавно и он считается самым удобным для восприятия человеком. Данные в этом файле представляются как ключ: значение, табуляция позволяет понять уровень вложенности.

В нем поддерживаются списки, ассоциативные массивы или мапы. Вы можете ознакомиться с синтаксисом на сайте yaml.org. Однако я верю, что на приведенных в этой статье примерах вы сможете понять как писать и читать файлы с таким форматом данных.

Будем называть верхним уровнем те ключи и значения, которые не имеют родительских. То есть они начинаются с начала строки и могут включать или не включать вложенные поля, однако сами не включены ни в какое другое поле.

На предыдущем примере на верхнем уровне находятся элементы services, networks, secrets, configs, volumes.

Версия Docker compose файла

В начале docker compose файла на верхнем уровне можно указать версию compose — она опциональна. Она используется для обратной совместимости и только предоставляет информацию для разработчиков.

Версия не указывает Docker на то каким образом необходимо парсить конфигурацию из этого файла для создания приложения. Docker предпочтет использовать более новую версию. Docker пытается прочитать весь Compose файл и если некоторые поля не знакомы ему, потому что они появились в новой версии файла, то вы получите предупреждение.

Имя Docker compose файла

Имя указывает на то, как должно называться приложение, которое создается при помощи Compose файла. Если вы не укажите его явно, то оно будет сгенерировано. Как только имя определено, к нему можно обратиться по ключу COMPOSE_PROJECT_NAME

services:
  foo:
    image: busybox
    command: echo "I'm running ${COMPOSE_PROJECT_NAME}"

Service

Сервис — это абстрактное определение какого-то ресурса, который используется для работы всего приложения. Ваше приложение состоит из нескольких ресурсов — например, бэкэнд, фронтенд и база данных. Вот каждый этот ресурс это отдельный сервис.

Сервисы представляют собой контейнеры, то есть запущенные image, и набор аргументов, поясняющих то, как этот контейнер должен работать.

В Compose файле должен быть верхнеуровневый элемент services, который состоит из пар ключ: значение, где ключ — это название сервиса и значение — его атрибуты.

Вы можете использовать как существующий image, который будет пулиться из какого-нибудь registry, так и собирать image при помощи Dockerfile, указав к нему путь.

Оригинальная документация состоит из описания вообще всех аргументов. В этой статье я расскажу лишь о части из них, которые используются повсеместно.

Атрибут build — описывает путь к Dockerfile из которого будет собран image, а также параметры для этого Dockerfile, например, контекст.

services:
  frontend:
    image: example/webapp
    build: ./webapp

  backend:
    image: example/database
    build:
      context: backend
      dockerfile: ../backend.Dockerfile

В этом примере сервисы frontend и backend собираются из Dockerfile, который находится соответственно в ./webapp и ../backend.Dockerfile

command — переопределяет инструкцию CMD, которая указана в Dockerfile этого сервиса, таким образом именно эта команда будет использоваться при запуске контейнера, а не та, что была указана в Dockerfile. Этот атрибут принимает как shell, так и exec форму команды, как и инструкция CMD.

configs — позволяет сервису использовать конфигурации, про которые мы говорили раньше в этом видео.

services: 
  redis:   
    image: redis:latest  
    configs:         
      - my_config    
      - my_other_config

configs:  
  my_config:  
    file: ./my_config.txt     
  my_other_config:    
    external: true

container_name определяет имя для контейнера, который представлен этим сервисом. Этот атрибут принимает строку — название контейнера.

depends_on говорит о том, в каком порядке должны быть запущены контейнеры сервисов.

services: 
  web:   
    build: .
    depends_on: 
      - db    
      - redis 
  redis:   
    image: redis 
  db:  
    image: postgres

Здесь сервис web зависит от базы даных и от Redis, контейнер web будет запущен только после того, как будут запущены контейнеры Redis и базы данных. Это нужно для того, чтобы ваше приложение не упало, попытавшись подключиться к еще не поднятому контейнеру, от которого оно зависит. Как и в локальной разработке, если вы пользуетесь базой, то она должна быть запущена до того, как вы запустите приложение.

entrypoint переопределяет ENTRYPOINT, который будет вызван при запуске контейнера. В прошлой статье мы узнали, что в Dockerfile выполняется только последняя инструкция ENTRYPOINT, но здесь мы ее переопределяем и можем задать новое поведение при запуске контейнера.

entrypoint:  
  - php  
  - -d 
  - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so 
  - -d  
  - memory_limit=-1  
  - vendor/bin/phpunit

env_file указывает путь к файлу с переменными, которые будут использоваться для работы контейнера. Чтобы не указывать все переменные, вы можете поместить их в один файл и сослаться на него. Вы можете передать как путь к отдельному файлу, так и список таких путей. По умолчанию это файл .env .

env_file:  
  - ./a.env  
  - ./b.env

environment определяет пары ключ: значение для переменных, которые нужны при работе контейнера. Вы можете использовать два разных синтаксиса — map и list.

environment: 
  RACK_ENV: development 
  SHOW: "true" 
  USER_INPUT:
environment:   
  - RACK_ENV=development  
  - SHOW=true   
  - USER_INPUT

Если вы указали и env_file и env переменные, тогда переменные из env имеют приоритет и будут перезаписывать переменные с таким же названием из env файла.

image определяет image, который будет запущен в контейнере. Вы можете указать просто название, но можете и дополнить его тегом или дайджестом. В сервисе должен быть указан либо image либо build атрибут, для того, чтобы Docker смог понять что должно быть запущено.

image: redis
image: redis:5
image: redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7
image: library/redis
image: docker.io/library/redis
image: my_private.registry:5000/redis

networks — указывает список network, к которым принадлежит сервис.

services: 
  some-service:   
    networks:    
      - some-network  
      - other-network

В этом примере сервис some-service принадлежит к двум сетям, которые были описаны в Compose файле.

ports определяет порты, которые отображаются наружу из контейнера. Мы говорили в прошлой статье, что EXPOSE в Dockerfile не открывает порты, а лишь говорит разработчикам о том, что этот порт нужен для работы приложения. А вот в Compose файле ports как раз и открывает порты наружу.

ports: 
  - "3000" 
  - "3000-3005" 
  - "8000:8000"
  - "9090-9091:8080-8081" 
  - "49100:22" 
  - "8000-9000:80" 
  - "6060:6060/udp"

Таким образом обратившись к localhost:3000 вы попадете внутрь контейнера на порт 3000. Вы можете поменять порт и обращаться к другому локальному порту, который в свою очередь будет отображен в контейнер. Порты на локальной машине должны быть доступны, иначе Compose файл не будет запущен.

pull_policy говорит о том, должен ли image пулиться из registry при каждом запуске контейнера, чтобы был запущен именно актуальный image.

Возможные значения:

  • always — image всегда пулятся

  • never — не пулятся никогда и должны быть на локальной машине, чтобы Compose файл мог ими воспользоваться

  • missing — Docker пулит только недостающие image

  • build — Docker будет создавать image из Dockerfile каждый раз

restart — определяет правила, по которым контейнер будет перезапускаться, когда он упал.

Есть несколько опций:

  • no — по умолчанию. Docker не перезапускает контейнер если он упал

  • always — Docker всегда перезапускает контейнер, пока он не будет удален

  • on-failure — Docker перезапускает контейнер, если он упал из-за ошибки, а не был остановлен правильно

  • unless-stopped — Docker перезапускает контейнер, пока сервис не будет остановлен или удален

restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

secrets — позволяет сервису обратиться к секретам, которые были указаны в текущем Compose файле.

services: 
  frontend:   
    image: example/webapp 
    secrets:      
      - server-certificate

secrets: 
  server-certificate:  
    file: ./server.cert

volumes определяет путь хранения volume из контейнера на локальном хосте. Вы можете создать volume в какой-нибудь директории локально или же сослаться на описанный volume из текущего Compose файла.

services: 
  backend:  
    image: example/backend   
    volumes:    
      - type: volume 
        source: db-data   
        target: /data     
        volume:     
          nocopy: true
      - type: bind       
        source: /var/run/postgres/postgres.sock  
        target: /var/run.postgres/postgres.sock 

volumes: 
  db-data:

Network

Networks или сети позволяют сервисам общаться между собой. Поместив несколько сервисов в один network, они могут свободно обращаться друг к другу по названию сервиса. Это удобно и быстро конфигурируется, что делает деплой приложения в Docker простым.

В сервисе мы рассмотрели атрибут networks, в котором указывается список сетей, к которым принадлежит сервис.

Сейчас на примерах мы рассмотрим то, как настроить сеть и какие атрибуты у нее есть.

services: 
  frontend: 
    image: example/webapp 
    networks:     
      - front-tier  
      - back-tier

networks:
  front-tier: 
  back-tier:

В этом простом примере создается сервис и две сети, к обеим из которых он принадлежит. Когда сервис один, то ему сети и не нужны, так как ему не с кем общаться.

services:
  proxy: 
    build: ./proxy  
    networks:   
      - frontend
  app:   
    build: ./app  
    networks:    
      - frontend  
      - backend  
  db:  
    image: postgres   
    networks:    
      - backend

networks:
  frontend:   
    driver: custom-driver-1 
  backend:   
    driver: custom-driver-2 
    driver_opts:   
      foo: "1" 
      bar: "2"

В этом более сложном примере есть три сервиса — прокси, приложение и база данных. Этот пример напоминает тот, что мы рассмотрели в начале видео.

Прокси сервис изолирован от базы, так как они находятся в разных сетях. Но прокси может обратиться к сервису приложения.

Приложение может обратиться как к прокси, так и к базе данных, потому что принадлежит к обеим сетям, в которых находятся эти сервисы.

Также у этих сетей есть свои настройки — драйверы и их опции. Эти атрибуты необязательны, однако помогают настроить сеть.

Атрибут driver указывает на то, какой драйвер для работы сети следует использовать. Будет выброшена ошибка, если драйвер недоступен для использования при запуске Compose файла.

Есть два типа драйверов, которые поддерживаются Compose: host и nonehost использует возможности хоста, то есть того компьютера, на котором запущен Compose файл, для коммуникации между сервисами, none отключает коммуникацию.

networks: 
  db-data:   
  driver: overlay

driver_opts определяет список опций для драйвера. Про них можно узнать из документации конкретного драйвера.

networks: 
  db-data:  
    driver_opts: 
      foo: "bar"     
      baz: 1

Если атрибут attachable установлен в true, тогда контейнеры вне этого Compose файла могут подключиться к сети и общаться с сервисами внутри этой сети.

networks: 
  mynet1:   
    driver: overlay   
    attachable: true

Если атрибут external установлен в true, тогда Docker ожидает, что эта сеть уже создана и будет обращаться к ней, иначе будет выброшена ошибка при запуске Compose файла. Все остальные атрибуты не будут учитываться, кроме атрибута имени, а если они отличаются от настоящих, то будет выброшена ошибка.

services:
  proxy:
    image: example/proxy
    networks:
      - outside
      - default
  app:
    image: example/app
    networks:
      - default

networks:
  outside:
    external: true

В данном примере Compose будет использовать уже существующую сеть outside, а не пытаться создать новую.

Если атрибут internal установлен в true, тогда вы запретите внешнее подключение к этой сети снаружи Compose файла, так как по умолчанию Compose предоставляет возможность внешнего подключения.

Атрибут name устанавливает имя для сети, как и с volume, вы можете использовать переменные для подстановки значений при запуске Compose файла.

networks:  
  network1:   
    external: true  
    name: "${NETWORK_ID}"

Если вы используете внешний network, тогда вы можете указать настоящее имя сети, а внутри текущего Compose файла обращаться к сети с другим именем.

Volume

Volumes — это постоянные хранилища данных вашего контейнера.

Когда Docker контейнер перезапускается, все его данные, которые не хранятся в volume очищаются. Так как контейнер изолирован от окружающей среды, логично, что он будет очищен при перезапуске. Для того, чтобы сохранить важные данные, например, состояние базы данных из контейнера, необходимо создать volume, который будет хранить эти данные на хосте, на котором работает контейнер.

Верхнеуровневая конфигурация volumes позволяет описать volumes, которые будут использованы сервисами. Как мы заметили раньше, в сервисе нужно явно указывать то, какие данные будут сохраняться и куда.

Давайте рассмотрим несколько примеров конфигурации volumes.

services:
  backend:
    image: example/database
    volumes:
      - db-data:/etc/data
  backup:
    image: backup-service
    volumes:
      - db-data:/var/lib/backup/data

volumes:
  db-data:

На этом примере мы видим два сервиса, один из которых база данных, а второй — бэкап сервис, делающий бэкапы базы данных. Оба этих сервиса имеют доступ к данным из базы данных благодаря volume.

Сервис базы данных сохраняет данные в volume db-data, а сервис бэкапа сохраняет эти данные в своем контейнере по пути /etc/data. Таким образом данные из одного контейнера попадают во второй.

У конфигурации volume есть несколько атрибутов, но они не являются обязательными.

driver указывает какой драйвер для volume должен быть использован. Некоторые могут быть платформозависимыми. Если драйвер недоступен, то Docker выбросит ошибку при попытке запуске Compose файла.

volumes:  
  db-data:   
    driver: foobar

driver_opts определяет опции volume, пар ключ: значение, они тоже платформозависимые, то есть на Windows и на Linux эти опции могут отличаться.

volumes:
  example:
    driver_opts:
      type: "nfs"
      o: "addr=10.40.0.199,nolock,soft,rw"
      device: ":/docker/example"

Если атрибут external установлен в true, тогда Docker ожидает, что этот volume уже существует и управляется из другого Compose файла. Если такого volume не будет, то выбросится ошибка при запуске Compose файла. Также в этом случае все атрибуты кроме имени не будут учитываться, раз volume был создан и управляется из другого места. Но если эти атрибуты отличаются от оригинальных, то будет выброшена ошибка. Таким образом определяя external как true, вы можете указать только имя volume для этого Compose файла.

services:
  backend:
    image: example/database
    volumes:
      - db-data:/etc/data

volumes:
  db-data:
    external: true

На этом примере volume не создается, а ищется уже существующий.

name устанавливает имя для volume. Вы можете воспользоваться подстановкой переменных при запуске Compose файла. В данном случае имя volume будет получено из переменной DATABASE_VOLUME из .env файла.

volumes:
  db-data:
    name: ${DATABASE_VOLUME}
volumes:
  db-data:
    external:
      name: actual-name-of-volume

В этом примере внешний volume имеет имя actual-name-of-volume, а внутри текущего Compose файла вы можете обратиться по имени db-data .

Config

Config позволяет сервисам изменять их поведение без необходимости изменения используемых image.

Сервисы могут обратиться к конфигам только если имеют атрибут configs.

Как и volume, конфиги связаны с файловой системой контейнера. По умолчанию они находятся в корне файловой системы контейнера.

В конфиге указывается информация, которую могут использовать сервисы в Compose приложении. Источником конфига может быть файл или внешний источник.

  • file — создается конфиг со всем содержимым файла

  • environment — создается конфиг со значением переменной

  • content — создается конфиг со значением, которое было передано в этом атрибуте

external — Если указано как true, Docker ожидает, что конфиг был создан, он не пытается его создать заново и выбрасывает ошибку, если такого конфига не существует.

name — имя конфига, которое Docker ищет.

configs: 
  http_config:  
    file: ./httpd.conf

В этом примере создается config, значением которого будет содержимое файла. Имя для него будет <project_name>_http_config.

configs:
  app_config:
    content: |
      debug=${DEBUG}
      spring.application.admin.enabled=${DEBUG}
      spring.application.name=${COMPOSE_PROJECT_NAME}

В этом случае создается конфиг, содержимым которого является указанный текст.

Secret

Секреты используются для использования чувствительных данных. Сервисы могут обратиться к секретам только если имеют атрибут secrets.

На верхнем уровне элемент secrets определяет или ссылается на те самые чувствительные данные, источником которых служит либо file либо environment.

  • Если источник file, тогда создается секрет с содержимым этого файла.

  • Если источник environment, тогда создается секрет со значением этой переменной.

external обозначает то, что секрет уже был создан и Docker не пытается его создать. Если он не был создан, тогда вы получите ошибку при выполнении Compose файла.

name — название секрета в Docker

Давайте рассмотрим несколько примеров.

secrets: 
  server-certificate:  
    file: ./server.cert

В этом примере создается секрет с названием <project_name>_server-certificate в момент запуска Compose файла, а значение секрета берется из файла server.cert.

secrets:  
  token:  
    environment: "OAUTH_TOKEN"

В этом случае секрет называется как <project_name>_token и создается во время запуска Compose файла, значением является значение переменной.

secrets:  
  server-certificate:  
    external: true

В таком случае Docker пытается найти уже созданный секрет и использовать его.

Fragments

В Compose файле можно использовать встроенные возможности языка YAML, которые сделают Compose файл более аккуратным и эффективным.

Вы можете создать фрагменты для того, чтобы переиспользовать некоторые части файла. Например, если у вас есть несколько сервисов с одинаковой конфигурацией, вы можете объединить эту конфигурацию в одну и не дублировать ее повторно.

Фрагменты создаются при помощи символы амперсанд & и имени фрагмента. Они устанавливаются на ключи YAML файла, раз этот формат — это ключ-значение, то подстановки происходят после двоеточия. С помощью * и имени можно обратиться к этому фрагменту и получить его значение.

Фрагменты подставляются до подстановки переменных, поэтому здесь их нельзя использовать для создания названий.

volumes:  
  db-data: &default-volume
    driver: default 
  metrics: *default-volume

В данном примере создается фрагмент default-volume, который подставит все свойства volume db-data в volume metrics, то есть metrics также будет иметь свойство driver: default.

services:
  first:
    image: my-image:latest
    environment: &env
      - CONFIG_KEY
      - EXAMPLE_KEY
      - DEMO_VAR
  second:
    image: another-image:latest
    environment: *env

В этом примере переменные одного сервиса помечаются якорем, чтобы быть переиспользоваными во втором сервисе.

Иногда вы можете хотеть переопределить значения фрагмента только частично, при помощи YAML merge вы можете это сделать как в данном примере.

services:
  backend:
    image: example/database
    volumes:
      - db-data
      - metrics
volumes:
  db-data: &default-volume
    driver: default
    name: "data"
  metrics:
    <<: *default-volume
    name: "metrics"

В данном примере default-volume подставит только driver, а name подставлен не будет, так как мы его явно указали.

Вы также можете дополнить значения фрагмента дополнительными элементами как в этом примере.

services:
  first:
    image: my-image:latest
    environment: &env
      FOO: BAR
      ZOT: QUIX
  second:
    image: another-image:latest
    environment:
      <<: *env
      YET_ANOTHER: VARIABLE

Но в таком случае вы должны указывать переменные в формате Ключ: Значение, а не - Ключ=Значение.

Если вы хотите использовать фрагменты в нескольких файлах, тогда воспользуйтесь расширениями Extension.

Extensions

Как и фрагменты, Extensions могут быть использованы для того, чтобы сделать Compose файл более эффективным и поддерживаемым.

Для этого на верхнем уровне нужно определить элемент, который начинается с x-. Так Docker поймет, что это расширение и будет знать о нем.

Давайте рассмотрим несколько примеров:

x-env: &env
  environment:
    - CONFIG_KEY
    - EXAMPLE_KEY
 
services:
  first:
    <<: *env
    image: my-image:latest
  second:
    <<: *env
    image: another-image:latest

В первом примере определен extension x-custom и x-foo, мы можем добавить x-foo в сервис и Docker его обработает, но ничего не сделает, однако значение x-foo можно получить в самом приложении webapp.

Во втором примере env не принадлежит ни одному из сервисов. Такой extension определяет новую ноду, в которой содержится поле environment. А якорь используется для того, чтобы обратиться к этому полю в двух сервисах.

x-environment: &default-environment
  FOO: BAR
  ZOT: QUIX
x-keys: &keys
  KEY: VALUE
services:
  frontend:
    image: example/webapp
    environment: 
      << : [*default-environment, *keys]
      YET_ANOTHER: VARIABLE

В этом примере можно использовать объединение из нескольких расширений, тогда все ключи из обоих расширений будут подставлены. Как и в случае с фрагментами, вы должны указывать переменные в формате Ключ: Значение, а не - Ключ=Значение.

Interpolation

Как и в Dockerfile, мы можем воспользоваться wildcard для подстановки значений переменных из конфигурационного файла.

Значение переменной можно получить написав ${VARIABLE} или $VARIABLE. Можно обратиться к вложенным переменным, например как здесь.

${VARIABLE:-${FOO}}
${VARIABLE?$FOO}
${VARIABLE:-${FOO:-default}}

Есть несколько видов подстановки.

${VARIABLE:-default} - подставит default, если переменная VARIABLE не задана или пустая

${VARIABLE-default} - подставит default только если переменная VARIABLE не задана

${VARIABLE:?err} - завершит программу с сообщением об ошибке err если переменная VARIABLE не задана или пустая

${VARIABLE?err} - завершит программу с сообщением об ошибке err только если переменная VARIABLE не задана

Если Docker не может найти переменную и нет значения по умолчанию, тогда выводится предупреждение и используется пустая строка. Стоит помнить об этом.

Если используется слияние нескольких Compose файлов, тогда подстановка происходит до слияния, чтобы не допустить ошибок.

Подстановки работают только для значений, а не ключей, поэтому такой пример не будет работать.

services: 
  foo:  
    labels:    
      "$VAR_NOT_INTERPOLATED_BY_COMPOSE": "BAR"

Merge

Вы можете использовать несколько compose файлов и объединить их в один, чтобы собрать свое приложение.

Давайте рассмотрим правила, которые соблюдает merge.

Если мержутся мапы, то есть пары ключ-значение, то недостающие ключи добавляются, а существующие перезаписываются.

#первый файл
services: 
  foo: 
    key1: value1
    key2: value2

#второй файл
services: 
  foo: 
    key2: VALUE 
    key3: value3

#результат
services: 
  foo: 
    key1: value1 
    key2: VALUE 
    key3: value3

Если мержутся последовательности, тогда они дополняются.

#первый файл
services: 
  foo:   
    DNS:   
      - 1.1.1.1

#второй файл
services: 
  foo:   
    DNS:   
      - 8.8.8.8

#результат
services:  
  foo: 
    DNS:  
      - 1.1.1.1 
      - 8.8.8.8

COMMAND, ENTRYPOINT, HEALTHCHECK перезаписываются, так как мы помним, что они не могут дублироваться в Dockerfile, то и здесь они не могут использоваться совместно, а только последняя инструкция будет применена.

#первый файл
services:
  foo:   
    command: ["echo", "foo"]

#второй файл
services: 
  foo: 
    command: ["echo", "bar"]

#результат
services: 
  foo:  
    command: ["echo", "bar"]

В случае с уникальными ресурсами, такими как порты, volume, секреты и конфигурации если уникальные значения конфликтуют, тогда они будут перезаписаны.

#первый файл
services:  
  foo:   
    volumes:      
      - foo:/work

#второй файл
services: 
  foo:   
    volumes:     
      - bar:/work

#результат
services:  
  foo:   
    volumes:  
      - bar:/work

В качестве уникальных ресурсов выступают:

Эта публикация - текстовый вариант и сценарий для видео на YouTube (оно удобно разбито на эпизоды).-48

Изображение взято из документации Docker.

Include

Вы можете сослаться на другой Compose файл если вы хотите:

  1. Переиспользовать Compose файлы,

  2. Вы хотите вынести общие части в отдельные compose файлы, которые должны управляться независимо друг от друга

  3. Вам нужно, чтобы Compose файл был не большого размера или включал в себя ресурсы определенной части большого приложения

Для того, чтобы подключить другой файл, вы должны указать на верхнем уровне секцию include. Как только compose файлы из этой секции загрузились, они копируются в данный проект. Вы увидите предупреждение, если файлы конфликтуют между собой и Docker не будет сам мержить их.

Вы можете ссылаться на ресурсы из других Compose файлов, как будто эти ресурсы есть в вашем файле. Как на этом примере.

include:  
  - my-compose-include.yaml #здесь описан serviceB

services: 
  serviceA:   
    build: .  
    depends_on:     
      - serviceB #используем этот сервис как будто он описан прямо в этом файле

Вы также можете использовать переменные в include для указания относительного пути папок, например для сборки приложения для разработки и production.

include: 
  - ${INCLUDE_PATH:?FOO}/compose.yaml

Можно использовать длинный синтаксис и более детально сконфигурировать include.

include:  
  - path: ../commons/compose.yaml    
    project_directory: ..     
    env_file: ../another/.env

В данном случае path — обязательный элемент и он определяет расположение compose файла относительно текущего. Это может быть как одна строка, так и список строк, если нужно подключить несколько Compose файлов.

project_directory определяет путь, относительно которого относительные пути из path будут применены. По умолчанию это папка с текущим Compose файлом.

env_file указывает путь к файлу с переменными, которые будут использоваться в указанных Compose файлах. По умолчанию это .env файл в директории project_directory. Тут так же можно указать как строку так и список строк. Переменные в локальной директории, то есть в той, в которой находится текущий Compose файл имеют преимущество перед импортируемыми и могут их переписать.

An open-source platform called Docker makes designing, shipping, and deploying applications simple. It runs an application in an isolated environment by compiling its dependencies into a so-called container. for additional information on Docker. In a normal case, several services, such as a database and load balancing, are required to support an application.

We’ll look at Docker Compose’s assistance with setting up many services in this article. Also, we will see a demonstration of installing and utilizing Docker Compose. Let’s try to understand docker-compose simply.

Docker Compose will execute a YAML-based multi-container application. The YAML file consists of all configurations needed to deploy containers Docker Compose, which is integrated with Docker Swarm, and provides directions for building and deploying containers. With Docker Compose, each container is constructed to run on a single host.

Table of Content

  • Key Concepts in Docker Compose
  • Install Docker Compose
  • Install Docker Compose on Ubuntu — A Step-By-Step Guide
  • Docker Container
  • Why Docker Compose?
  • How to Use Docker Compose?
  • Docker-compose.yaml File
  • Run the application stack with Docker Compose
  • Important Docker Compose Commands
  • Best Practices of Docker Compose
  • Features of Docker Compose

Key Concepts in Docker Compose

Docker Compose is a powerful tool for managing multi-container applications, and mastering its key components—like services, networks, volumes, and environment variables—can greatly enhance its usage. Let’s break down these concepts and how they work within a Docker Compose file.

Docker Compose File (YAML Format)

Docker Compose configurations are mainly stored in a file named docker-compose.yml, which uses YAML format to define an application’s environment. This file includes all the necessary details to set up and run your application, such as services, networks, and volumes. To use Docker Compose in an effective way you have to know the structure of this file.

Key Elements of the YAML Configuration

  • Version: It defines the format of the Compose file, by ensuring compatibility with specific Docker Compose features and syntax.
  • Services: The services section lists each containerized service required for the application. Each service can have its configuration options, such as which image to use, environment variables, and resource limits.
  • Networks: In network section, you can define custom networks that enable communication between containers. Additionally, it allows you to specify network drivers and custom settings for organizing container interactions.
  • Volumes: Volumes allow for data persistence across container restarts and can be shared between containers if needed. They enable you to store data outside the container’s lifecycle, making it useful for shared storage or preserving application state.

Example docker-compose.yml

Here’s a sample Compose file that defines two services, a shared network, and a volume:

version: '3.8'

services:
web:
image: nginx:latest
ports:
- "80:80"
networks:
- frontend
volumes:
- shared-volume:/usr/share/nginx/html
depends_on:
- app

app:
image: node:14
working_dir: /app
command: node server.js # Specify a command
networks:
- frontend
volumes:
- shared-volume:/app/data

networks:
frontend:
driver: bridge

volumes:
shared-volume: # Remove incorrect syntax

docker-compose-output

Explanation:

  • The web service runs an Nginx container, and app runs a Node.js container.
  • Both services connect through the frontend network, allowing them to communicate.
  • The shared-volume volume is mounted in both containers, providing shared storage for files.

Docker Compose Services

In Docker Compose, every component of your application operates as a separate service, with each service running a single container tailored to a specific role—such as a database, web server, or cache. These services are defined within the `services` section of the `docker-compose.yml` file. This section lets you configure each service individually, specifying details like the Docker image to pull, environment variables, network connections, and storage options. Through this setup, you can control how each part of your application interacts, ensuring smooth communication and resource management across the services.

Key Service Configuration Options

  • Image: Image option defines which Docker image we are going to use by the service from the Docker Hub or any other registry.
  • Build: Instead of pulling an image, you can build one locally by specifying a directory containing a Dockerfile. The build is ideal for including custom code in your application.
  • Ports: This setting maps a container’s internal ports to those on the host machine, enabling access to services from outside the container.
  • Volumes: Volumes attach persistent storage to a service ensuring that the data remains accessible even when a container restart.
  • Environment: Environment variables allow you to pass configurations or sensitive information, like database credentials or API keys, to the service.
  • Depends_on: Depends_on controls the startup order of services, ensuring that certain containers are running before others begin.

Example of docker-compose.yml Configuration

Here’s a sample configuration that demonstrates how these options are used:

version: '3.8'
services:
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- db_data:/var/lib/postgresql/data

web:
build: ./web
ports:
- "5000:5000"
volumes:
- web_data:/usr/src/app
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db

volumes:
db_data:
web_data:

Explanation:

  • The db service runs a PostgreSQL container. It uses environment variables to set up a database username and password, and stores data on the db_data volume to ensure it’s retained.
  • The web service is built from a Dockerfile in the ./web directory and exposes port 5000. The web_data volume is mounted to store application files persistently. It depends on the db service, ensuring the database is available when the web service starts.

Docker Compose Networks

Docker Compose deployments use networks to allow secure communications between the services. Services defined in a docker-compose.yml file are by default placed on one network and are able to connect to each other without any additional setup. For more strict control, you can create additional networks and assign services to them in order to control the way they communicate or to separate some groups of services as the need arises.

Key Network Configuration Options

  • Driver: Driver are used in the network driver type, such as bridge (the default for local networks) or overlay (for multi-host networks in Docker Swarm), which determines how services connect to each other.
  • Driver Options (driver_opts): Driver Options(driver_opts) allows for additional settings on the network driver, useful for fine-tuning network behavior to meet specific needs.
  • IP Address Management (ipam):IP address management configures network-level IP settings, like subnets and IP ranges, to give you greater control over the IP address space assigned to your services.

Example docker-compose.yml with Custom Networks

Below is an example Compose file that sets up two networks, one for database communication and another for web access.

version: '3.8'

services:
db:
image: postgres:13
networks:
- backend

web:
image: nginx:latest
networks:
- frontend
- backend
ports:
- "80:80"

networks:
frontend:
driver: bridge

backend:
driver: bridge
ipam:
config:
- subnet: 172.16.238.0/24

Explanation:

  • The db service uses thebackend network, isolating it from the frontend network to limit access.
  • The web service is connected to both frontend and backend networks, allowing it to communicate with the db service while remaining accessible via the frontend network.
  • The backend network includes IPAM settings with a specific subnet range, ensuring custom IP address management.

Docker Compose Volumes

Volumesin docker compose are used to persist data created or used by the docker containers. By doing so they enable the data to persist even if containers are stopped or removed in your docker-compose. Within a docker-compose. yml file, the volumes section describes all the volumes that are attached to the services allowing you to manage data that exists independently of the container lifecycle.

Key Volume Configuration Options

  • External: Set true to signify that the volume was created externally, outside of Docker Compose (such as via docker volume create) and simply referenced in the configuration.
  • Driver: This indicates which volume driver the volume should use, and it controls how are these volumes being handled. By default, the driver is local, but other options are also available.
  • Driver Options (driver_opts): Additional options to customize the volume driver like filesystem type or different storage parameters.

Example docker-compose.yml with Volumes

Here’s a practical example showing how to configure a volume for a Pos-tgreSQL database, ensuring that its data is stored persistently.

version: '3.8'

services:
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- db_data:/var/lib/postgresql/data

volumes:
db_data:
driver: local
driver_opts:
type: none
o: bind
device: /path/to/local/db_data

Explanation

  • The db service runs a PostgreSQL container, with its data stored in the db_data volume. This setup ensures that the database information remains intact across restarts or removals of the container.
  • The db_data volume is configured to use the localdriver, and it has driver options set to create a bind mount pointing to a specific path on the host system (/path/to/local/db_data). This means that all database files are saved in that designated directory on the host.
  • By using volumes in this way, you can keep essential data safe and easily accessible, separate from the container itself.

Docker Compose Environment Variables

Environment variables are a simple and effective way to pass configuration settings from your host operating system through Docker Compose in order to get to your services. You can set these variables directly on the service definition by using the environment section or load them from an external file.

How to Set Environment Variables in Docker Compose?

  • Inline: you may declare env vars directly in the service definition.This approach is simple and gives everything in one place.
  • env_file: This option allows you to load environment variables from an external file, making it easier to manage configuration, especially when dealing with many variables.

Example docker-compose.yml Using Environment Variables

Here’s an example that demonstrates both methods of setting environment variables for a web application and a database service.

version: '3.8'

services:
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes: - db_data:/var/lib/postgresql/data

web: image: my-web-app:latest
build: ./web
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
env_file:
- .env

volumes:
db_data:

Explanation

  • In the db service, the POSTGRES_USER and POSTGRES_PASSWORD environment variables are defined inline, specifying the database credentials directly.
  • The web service uses an inline variable for DATABASE_URL, which connects to the PostgreSQL database. Additionally, it loads environment variables from an external file named .env. This file can contain various settings, such as API keys, application configurations, and other sensitive information.

With a good understanding of these basic principles, developers are ready to use Docker Compose to manage and orchestrate applications that can be quite complex and involve many Docker containers.

Install Docker Compose

We can run Docker Compose on macOs, Widows, and 64-bit Linux. 

  • For any significant activity, Docker Compose depends on Docker Engine. Depending on your arrangement, we must ensure that Docker Engine is installed either locally or remotely.
  • A desktop system such as Docker for Mac and Windows comes with Docker Compose preinstalled. 
  • Install Docker first as instructed in Docker installation on the Linux system before beginning the installation of Docker Compose. 

Install Docker Compose on Ubuntu — A Step-By-Step Guide

Step 1: Update the package Manager

  • The following scripts will install the most recent version of Docker Compose and update the package management.
sudo apt-get update

Step 2: Download the Software

  • Here we are using the Ubuntu flavor in the Linux Operating system. So the package manager is «apt-get» If you want to install it in Redhat Linux then the package manager will be «yum».

Step 3: Apply Permissions

  • Apply the Executable permissions to the software with the following commands:
sudo chmod +x /usr/local/bin/docker-compose

Step 4: Verify the Download Software

  • Verify the whether the docker compose is successfully installed or not with the following command:
docker-compose --version

Docker Container

A docker container is a lightweight Linux-based system that packages all the libraries and dependencies of an application, prebuilt and ready to be executed. It is an isolated running image that makes the application feel like the whole system is dedicated to it. Many large organizations are moving towards containers from VMs as they are light and simple to use and maintain. But when it comes to using containers for real-world applications, usually one container is not sufficient. For example, Let’s assume Netflix uses a microservices architecture. Then it needs services for authentication, Login, Database, Payment, etc, and for each of these services, we want to run a separate container. It is preferred for a container to have only a single purpose.

Now, imagine writing separate docker files, and managing configuration and networks for each container. This is where Docker Compose comes into the picture and makes our lives easy.

Why Docker Compose?

As discussed earlier,  a real-world application has a separate container for each of its services. And we know that each container needs to have a Dockerfile. It means we will have to write maybe hundreds of docker files and then manage everything about the containers individually, That’s cumbersome. 

Hence we use docker-compose, which is a tool that helps in the definition and running of multi-container applications. With the help of Docker Compose you can start and stop the services by using its YAML file. Docker-compose allows us to start and stop all of the services with just a few simple commands and a single YAML file for each configuration.

In contrast to utilizing a prebuilt image from Docker Hub, which you may configure with the docker-compose.yaml file, if you are using a custom image, you will need to declare its configurations in a separate Dockerfile. These are the features that docker-compose support:

  • All the services are isolated running on a single host.
  • Containers are recreated only when there is some change.
  • The volume data is not reset when creating new containers, volumes are preserved.
  • Movement of variables and composition within environments.
  • It creates a virtual network for easy interaction within the environments.

Now, let’s see how we can use docker-compose, using a simple project.

How to Use Docker Compose?

In this project, we will create a straightforward Restfull API that will return a list of fruits. We will use a flask for this purpose. And a PHP application will request this service and show it in the browser. Both services will run in their own containers.

Step 1: Create Project Directory

  • First, Create a separate directory for our complete project. Use the following command.
mkdir dockerComposeProject
  • Move inside the directory.
cd dockerComposeProject

Step 2: Create API

we will create a custom image that will use Python to serve our Restful API defined below. Then the service will be further configured using aDockerfile.

  • Then create a subdirectory for the service we will name it product. and move into the same.
mkdir product
cd product
  • Create requirements.txt

Inside the product folder, create a file named requirements.txt and add the following dependencies:

flask
flask-restful

Step 3: Build Python  api.py

  • The following is the python file that helps in making an API call:
  • Create a Dockerfile to define the container in which the above API will run.
from flask import Flask
from flask_restful import Resource, Api

# create a flask object
app = Flask(__name__)
api = Api(app)

# creating a class for Fruits that will hold
# the accessors
class Fruits(Resource):
def get(self):
# returns a dictionary with fruits
return {
'fruits': ['Mango', 'Pomegranate', 'Orange', 'Litchi']
}

# adds the resources at the root route
api.add_resource(Fruits, '/')

# if this file is being executed then run the service
if __name__ == '__main__':
# run the service
app.run(host='0.0.0.0', port=80, debug=True)

Step 4: Create Dockerfile For Python API 

FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "api.py"]

FROM accepts an image name and a version that the docker will download from the docker hub. The current working directory’s contents can be copied to the location where the server expects the code to be by using the copy command. Moreover, the CMD command takes a list of commands to start the service once the container has been started.

Step 5: Create PHP HTML Website 

Let’s create a simple website using PHP that will use our API.

  • Move to the parent directory and create another subdirectory for the website.
cd ..
mkdir website
cd website

index.php

<!DOCTYPE html>
<html lang="en">
<head>
<title>Fruit Service</title>
</head>
<body>
<h1>Welcome to India's Fruit Shop</h1>
<ul>
<?php
$json = file_get_contents('http://fruit-service');
$obj = json_decode($json);
$fruits = $obj->fruits;
foreach ($fruits as $fruit){
echo "<li>$fruit</li>";
}
?>
</ul>
</body>
</html>
  • Now create a compose file where we will define and configure the two services, API and the website.
  • Move out of the website subdirectory using the following code.
cd ..
  • And then create the file name as . docker-compose.yaml 

Step 6: Create Docker-compose.yaml file 

  • The following is the sample docker compose file code:
version: "3"

services:
fruit-service:
build: ./product
volumes:
- ./product:/usr/src/app
ports:
- 5001:80

website:
image: php:apache
volumes:
- ./website:/var/www/html
ports:
- 5000:80
depends_on:
- fruit-service

Docker-compose.yaml File

The first line is optional where we specify the version of the docker-compose tool. Next services define a list of services that our application is going to use. The first service is fruit service which is our API and the second one is our website. The fruit service has a property build that contains the dockerfile that is to be built and created as an image. Volumes define storage mapping between the host and the container so that we can make live changes. Finally, port property exposes the containers port 80 through the host’s 5001.

The website service does not use a custom image but we download the PHP image from the Docker hub and then map the websites folder that contains our index.php to /var/www/html (PHP expects the code to be at this location). Ports expose the container port. Finally, the depends_on specifies all the services on which the current service depends.

  • The folder structure after creating all the required files and directory will be as follows:

Run the application stack with Docker Compose

  • Now that we have our docker-compose.yml file, we can run it.
  • To start the application, enter the following command.
docker-compose up -d 
docker-compose-up-d-output

Now all the services will start and our website will be ready to be used at localhost:5000.

  • Open your browser and enter localhost:5000.

Output

Application from internet

  • To stop the application, either press CTRL + C or
docker-compose stop

Advantages of Docker Compose

The following are the advantages of Docker Compose:

  • Simplifies Multi-Container Management: Docker Compose facilitates with features such as define, configure, and run multiple containers with a single YAML file, streamlining the management of complex applications.
  • Facilitates Environment Consistency: It facilitates with the development, testing, and production environments that are consistent with reducing the risk of environment-related issues.
  • Automates Multi-Container Workflows: With Docker Compose, you can easily automate the setup and teardown of multi-container environments, making it ideal for CI/CD pipelines and development workflows.
  • Efficient Resource Management: It enables efficient allocation and management of resources across multiple containers, improving application performance and scalability.

Disadvantages of Docker Compose

The following are the disadvantages of Docker Compose:

  • Limited Scalability: Docker Compose is not developed for large scaling mechanism which can limit its effectiveness for managing complex deployments.
  • Single Host Limitation: Docker Compose will operate on a single host, making it unsuitable for distributed applications with requiring multi-host orchestration.
  • Basic Load Balancing: It lacks with advanced load balancing and auto-scaling features found in more robust orchestration tools like Kubernetes.
  • Less Robust Monitoring: Docker Compose provides minimal built-in monitoring and logging capabilities compared to more comprehensive solutions.

Important Docker Compose Commands

Command

Description

Example

docker-compose up

This command starts all the services defined in your docker-compose.yml file. It creates the necessary containers, networks, and volumes if they don’t already exist. You can run it in the background by adding the -d option.

docker-compose up -d

docker-compose down

Use this command to stop and remove all the containers, networks, and volumes that were created by docker-compose up. It’s a good way to clean up resources when you no longer need the application running.

docker-compose down

docker-compose ps

This command lists all the containers associated with your Compose application, showing their current status and other helpful information. It’s great for monitoring which services are up and running.

docker-compose ps

docker-compose logs

This command lets you view the logs generated by your services. If you want to focus on a specific service, you can specify its name to filter the logs, which is useful for troubleshooting.

docker-compose logs web

docker-compose exec

With this command, you can run a command inside one of the running service containers. It’s particularly useful for debugging or interacting with your services directly.

docker-compose exec db psql -U user -d mydb

docker-compose build

This command builds or rebuilds the images specified in your docker-compose.yml file. It’s handy when you’ve made changes to your Dockerfiles or want to update your images.

docker-compose build

docker-compose pull

Use this command to pull the latest images for your services from their respective registries. It ensures that you have the most current versions before starting your application.

docker-compose pull

docker-compose start

This command starts containers that are already defined in your Compose file without recreating them. It’s a quick way to get your services running again after they’ve been stopped.

docker-compose start

docker-compose stop

This command stops the running containers but keeps them intact, so you can start them up again later using docker-compose start.

docker-compose stop

docker-compose config

This command validates and displays the configuration from your docker-compose.yml file. It’s a useful way to check for any errors before you deploy your application.

docker-compose config

Best Practices of Docker Compose

The following are the some of the best practices of Docker Compose:

  • Use Environment Variables: It is suggestable to store configuration values and secrets in environment variables to keep your docker-compose.yml clean and secure.
  • Keep Services Lightweight: It is preferred to design each service to handle a single responsibility to ensure modularity and ease of maintenance.
  • Leverage Volumes: Usage of volumes with enhancing in maintaining the persistent data storage, allowing data to persist across container restarts and updates.
  • Version Control Your Compose Files: It is preferred to maintain your docker-compose.yml file in version control (e.g., Git) to track changes and collaborate with your team effectively.

Features of Docker Compose

The following are the features of Docker Compose:

  • Multi-Container Deployment: it facilitates with easily define and run applications with multiple containers using a single YAML file.
  • Service Isolation: Each service runs in its own container, with ensuring the isolation and reducing conflicts between services.
  • Simplified Configuration: It helps in centralizing all the configurations, including networking, volumes, and dependencies, in the docker-compose.yml file.
  • Scalability: It provides the effortlessly scaling of services up or down with a single command, allowing for flexible and dynamic resource management.

Conclusion

In this article, we learned about Docker Compose, and why and when to use it. And demonstrated its usuage through a simple project. It helps in automating the creating process of containers through services, networks and volumes with through respective keywords availability. Through using docker compose management and automation of containers and its volumes, networks will be easier.

This is a short hands-on introduction to docker-compose. If you want to know what docker is, check this link out. This is simply a commented version of the official django with docker-compose.

Tested Configuration:
MacOS: Sierra 10.12.06
Docker: 18.09.2 CE
Docker-compose: 1.23.2

1. Requirements

Make sure you have docker and docker-compose installed on your machine. To do this, simply type

and make sure you have something like Docker version 18.09.2-ce, build 6247962

Then type

and make sure you have something like docker-compose version 1.23.2, build 1110ad01

2. Dockerfile

First, create your Dockerfile, please type :

mkdir docker_folder
cd docker_folder
touch Dockerfile

Edit your Dockerfile

You are still in the /docker_folder, type

It will open your Dockerfile. Now copy the following code in your Dockerfile :

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

What is happening in the Dockerfile:

FROM python:3 will import the python 3 image from the docker hub
ENV PYTHONUNBUFFERED 1 define an environment variable. Later in the script, it will tell python to print STDOUT without buffering anything. that’s useful when you want to print something before the machine crashes
RUN mkdir /code will create a folder on the Image
WORKDIR /code will set the current directory as /code -> acts like a ` cd /code` on the image
COPY requirements.txt /code/ copies the file requirements.txt to the image at the location /code/requirements.txt
RUN pip install -r requirements.txt install all python packages on the image
COPY . /code/ copy the rest of the folder to the folder code

3. Create your requirements

Create your requirements.txt in your folder /docker_folder and then edit it:

sudo nano requirements.txt

Inside, you can write this:

Django>=2.0,<3.0
psycopg2>=2.7,<3.0

This will tell Docker to install Django and psycopg2 on the image

4. Create docker-compose.yaml

Create a file called docker-compose.yaml in your project directory /docker_folder and then edit it:

sudo nano docker-compose.yaml

Inside, you can write this:

version: "3"

services:
  db:
    image: postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
From the documentation:

The docker-compose.yml file describes the services that make your app. In this example those services are a web server and database.
The compose file also describes which Docker images these services use, how they link together, any volumes they might need mounted inside the containers.
Finally, the docker-compose.yml file describes which ports these services expose.

Short analysis:
  • we create two services: web & db
  • we use format version 3 (different from the docker-compose version…). You can see the Compatibility matrix
  • service db has an image based on the postgres image (from docker hub)
  • service web has an image based on the Dockerfile we wrote earlier: that’s the build: .
  • service web launches the command: python manage.py runserver 0.0.0.0:8000 after the image is built
  • service web mounts the folder /docker_folder to the image folder /code (ex: modify the code on the fly)
  • service web mounts the ports 8000 [container] to 8000 [image] (simple)

Note: we don’t have any access to the database, exept through the docker network. It means only the service web can access the database, because this docker network is restricted to db and web (type docker network inspect testdjango_default). Even you, if you want to use a GUI in order to manipulate the Postgres data, you will need to add a line in the db configuration:

5. Create a Django project

in your terminal type:

sudo docker-compose run web django-admin startproject composeexample .

Explanations:

compose will run django-admin startproject composeexample in a container, using the web service’s image and configuration. This command instructs Django to create a set of files and directories representing a Django project.

Now, if you look at the files in the /docker_folder, you have:
Dockerfile (was here before)
composeexample
docker-compose.yaml (was here before)
manage.py
requirements.txt (was here before)

6. Connect the database

In the composeexample/settings.py file, replace DATABASES = ... with:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}

To know more about the database: docker & postgres

7. Run

Simply run your docker project using

and see you server working by clicking http://localhost:8000

Monitor

Prints all the running containers

Stop

Two method:

  1. Ctrl-C in the same shell in where you started it

  2. open a new shell, go to /docker_folder directory, and run docker-compose down

Notes:

docker-compose.yaml VS docker-compose.yml

Shall we use the extension .yml or .yaml ? IT DOESN’T MATTER
Everyone could use .yaml but some Windows developer are afraid of using more than 3 letters extensions…

file ownership

After runnig sudo docker-compose run web django-admin startproject composeexample . several files were created. Running ls -l will show who is owning the files. You may need to change their ownership back to you (espcially Linux users) using sudo chown -R $USER:$USER .

Other docker-compose.yaml proposition

This can be interesting for two reasons:

version: "3"

services:
  db:
    image: postgres
    environment:
      PGDATA: /var/lib/postgresql/data/django_test_pgdata
      POSTGRES_USER: yourusername
      POSTGRES_PASSWORD: yourpassword
      POSTGRES_DB: test
    volumes:
      - /usr/local/var/postgres/data/django_test_pgdata:/var/lib/postgresql/data/django_test_pgdata
    ports:
      - "8002:5432"

  web: [SAME CONFIG]

First you may have port 5432 already in use on another Postgres container, or because you already run postgres on your laptop.

Second, you have control on the data. instead of staying inside your postgres container (and beiing deleted when the container stops) you can have persistent data.

Run only one docker

If you want to launch only the database for instance, you can write down:
docker-compose run --service-ports postgres

With --service-ports meaning you attach the docker to the host network (usually what you want when you don’t launch docker together)

if you have Volumes

Let’s say you have decided to inculde the volume option, like so (probably to cache your node_modules folder):

version: "3"

services:
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - node_modules:/code/node_modules
    ports:
      - "8000:8000"

Then, if you need to “clean the cache” (because you want to reinstall the dependancies for instance), use this command: docker-compose down --volumes

Ressources:

docker-compose.yaml VS docker-compose.yml: stackoverflow link
More about PYTHONUNBUFFERED
the official docker-compose tuto

Provide feedback

Saved searches

Use saved searches to filter your results more quickly

Sign up

Appearance settings

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как сделать интерфейс windows 7 на русском
  • Установка языкового пакета windows 10 dism
  • Utorrent не удаляется windows 10
  • Windows 10 snmp сбой установки
  • Media player classic codec pack windows 7