Docker-compose
Docker-compose — это надстройка над докером, приложение написанное на Python, которое позволяет запускать множество контейнеров одновременно и маршрутизировать потоки данных между ними.
Для каждого проекта (кластера контейнеров) Docker создаёт свою сеть, где контейнеры могут обращаться друг к другу по именам, которые мы укажем в docker-compose.yml. Все настройки запуска кластера контейнеров находятся в этом же файле, который располагается в корневой директории проекта.
Преимущества Docker Compose
- Декларативный подход к созданию контейнеров
- Все необходимые контейнеры запускаются одной командой
- Автоматическое создание необходимых образов на основании Dockerfile каждого приложения
- Автоматическое создание изолированной сети для взаимодействия контейнеров
- Благодаря DNS возможно взаимодействие между контейнерами используя имена сервисов
Есть два подхода при работе с Docker, императивный и декларативный:
При работе в декларативным подходе используются docker-compose.yml файлы с помощью как раз этих файлов и создаются инструкции для создания образа и запуска многих контейнеров:
Синтаксис YAML-файлов
Строка
Строка — простая примитивная запись данных по типу ключ : значение
. Строки можно записать двумя способами.
Однострочной записью:
string: This is a string.
Блочной записью:
---
string:
Это очень длинная строка,
которую сложно читать в однострочной записи,
так как она вылезает за пределы окна.
...
Блочная запись имеет три режима интерпретации:
- Стандартный (показан выше)
- С добавлением знака конца строки в завершении текста
string: >
- с добавлением всех знаков конца строки при каждой новой строке
string: I
Несколько строчных записей образуют словарь:
---
string_1: This is string 1.
string_2: This is string 2.
string_3: This is string 3.
...
Такая запись будет интерпретирована так:
{'string_1': 'This is a string 1.', 'string_2': 'This is a string 2.', 'string_3': 'This is a string 3.'}
Список
Список это набор разнородных данных. Списки могут быть трех видов:
- Простыми (одномерными)
- Вложенными (многомерными)
- Именованными
Пример простого, одномерного списка:
---
- element1
- element2
- element3
...
Именованный список по записи похож на строку. Название отделяется двоеточием, а элементы списка перечисляются через дефис:
---
list:
- 2
- element2
- true
...
Вложенные именованные списки образуются добавлением двоеточия к нужному элементу, который станет названием вложенного списка, и добавлением отступов к другим элементам, которые станут элементами вложенного списка:
---
list_level_1:
- 2
- element2
- true
- list_level_2:
- element1
- element2
- element3
...
Образовать простой список с вложенными неименованными списками просто, достаточно указать дефис, сделать отступ и перечислить вложенные элемент:
---
-
- element_1
- element_2
- element_3
-
- element_1
- element_2
- element_3
...
Словарь
Словарь — это набор данных по типу ключ : значение
. Словарь в YAML ничем не отличается от строк, то есть если мы сделаем такую запись:
---
author: Ivan Katkov
job: Tech Writer
skill: Normal
...
Мы получим словарь с 3 ключами, но если запись немного изменить на приведенную ниже, получится словарь с ключом author
, в качестве значения будет другой словарь, содержащий: name
, job
, skill
:
---
author:
name: Ivan Katkov
job: Tech Writer
skill: Normal
...
Мультивложенность элементов
Многоуровневая вложенность формируется отступами и спец. символами: двоеточием — знаком образования строки или ключа словаря и дефисов — символом образования списки или элемента списка.
Вложенные неименованные списки — дефис обозначает начало списка, а дефис с пробелом и название — является элементом списка:
-
- Яблоки
- Груша
- Банан
-
- Картофель
- Редька
- Капуста
-
- Лук
- Петрушка
- Укроп
Вывод Python:
[['Яблоки', 'Груша', 'Банан'], ['Картофель', 'Редька', 'Капуста'], ['Лук', 'Петрушка', 'Укроп']]
Вложенные именованные списки образуются путём замены пустых дефисов, обозначающих список на именованную строку с двоеточием:
Фрукты:
- Яблоки
- Груша
- Банан
Овощи:
- Картофель
- Редька
- Капуста
Зелень:
- Лук
- Петрушка
- Укроп
Вывод Python:
{'Фрукты': ['Яблоки', 'Груша', 'Банан'], 'Овощи': ['Картофель', 'Редька', 'Капуста'], 'Зелень': ['Лук', 'Петрушка', 'Укроп']}
Вложенные словари формируются с помощью именованных строк, двоеточий и отступов:
Фрукты:
Яблоки:
Красные:
1кг.
Зеленые:
0,5кг.
Груши:
Гвидон:
0,3кг.
Велеса:
0,1кг.
Вывод Python:
{'Фрукты': {'Яблоки': {'Красные': '1кг.', 'Зеленые': '0,5кг.'}, 'Груши': {'Гвидон': '0,3кг.', 'Велеса': '0,1кг.'}}}
Файл docker-compose.yml
version
обязательный тег, указывает на версию синтаксиса файла docker-compose.ymlservices
обязательный тег, в него как бы заворачиваются наши два сервисаapp
название сервиса может быть произвольным, для каждого сервиса Docker создаст отдельный контейнер, в данном примере будет два контейнера, этот для приложенияbuild: ./app
инструкцияbuild
просит Docker создать кастомный образ на основании Dockerfile,./app
указывает местоположение Dockerfile для этого приложения. Когда вы введете командуdocker-compose up -d
которая начнет процесс создания контейнеров, сначала для сервисаapp
будет создан кастомный образ на основании Dockerfile который находится в папке./app
и только после этого из образа будет развернут контейнер для соответствующего сервисаmongo
название сервиса может быть произвольным, для каждого сервиса Docker создаст отдельный контейнер, в данном примере будет два контейнера, этот для базы данныхimage: mongo
инструкцияimage
просит Docker скачать указанное название официального образаmongo
которое будет найдено локально или на hub.docker.com и использовано для соответствующего контейнераmongo
Получается, в одном интеграционном файле docker-compose.yml можно указать различные методы запуска разных контейнеров. В примере выше, для первого сервиса app
мы хотим создать свой собственный образ и развернуть из него контейнер, для второго сервиса mongo
мы хотим использовать официальный образ.
Синтаксис файла docker-compose.yml
В docker-compose.yml очень важное значение имеют отступы, которые добавляются с помощью пробела, обычно один отступ равен двум пробелам. При этом, редактор кода Visual Studio Code позволяют нажимать Tab
который автоматически конвертируется в пробелы, одно нажатие обычно равняется двум пробелам.
Большая часть руководства будет посвящена настройке контейнеров с помощью services
раздела. Ниже перечисленны директивы, используемые для установки и настройки контейнеров и пример боевого файла docker-compose.yml:
version: '2'
services:
nginx:
# используем последний стабильный образ nginx
image: nginx:latest
# маршрутизируем порты
ports:
- "8000:80"
# монтируем директории, слева директории на основной машине, справа - куда они монтируются в контейнере
volumes:
- ./hosts:/etc/nginx/conf.d
- ./www:/var/www
- ./logs:/var/log/nginx
# nginx должен общаться с php контейнером
links:
- php
php:
# у нас свой образ для PHP, указываем путь к нему и говорим что его надо собрать
build: ./images/php
# этот образ будет общаться с mysql
links:
- mysql
# монтируем директорию с проектами
volumes:
- ./www:/var/www
mysql:
image: mariadb
ports:
- "3306:3306"
volumes:
- ./mysql:/var/lib/mysql
# задаем пароль для root пользователя
environment:
MYSQL_ROOT_PASSWORD: secret
pma:
# используем последний стабильный образ phpmyadmin
image: phpmyadmin/phpmyadmin
restart: always
links:
- mysql:mysql
ports:
- 8183:80
environment:
# прописываем название нашего MySQL хоста
PMA_HOST: mysql
MYSQL_USERNAME: root
MYSQL_ROOT_PASSWORD: secret
version
Файл docker-compose.yml должен начинаться с тега версии. Мы используем 2
, это самая свежая версия на момент написания этого кода:
version: '2'
build
Указывает расположение файла Dockerfile, который будет использоваться для сборки этого контейнера:
build: ./images/php
command
Команда, которую нужно запустить после создания образа. Следующая команда означает запуск python ./server.py
:
command: python ./server.py
client
Пользовательский интерфейс, или интерфейс командной строки, для взаимодействий с демоном Docker. Клиент Docker — это основной способ взаимодействия с Docker. Когда вы используете интерфейс командной строки (CLI) Docker, вы вводите в терминал команду, которая начинается сdocker. Затем клиент Docker использует API Docker для отправки команды демону Docker:
client:
command
Команда, которую нужно запустить после создания образа. Следующая команда означает запуск python ./client.py
:
command: python ./client.py
network_mode
Ключевое слово network_mode
используется для описания типа сети. Тут мы указываем, что контейнер может обращаться к localhost
компьютера:
network_mode: host
image
Задает образ, который будет использоваться для сборки контейнера. Использование этой директивы предполагает, что указанный образ уже существует локально, либо в hub.docker.com. Используем последний стабильный образ nginx:
image: nginx:latest
restart
Сообщает контейнеру о перезапуске, если система перезагружается:
restart: always
volumes
Монтируем директории, слева директории на основной машине локально, справа куда они монтируются в контейнере:
volumes:
- ./hosts:/etc/nginx/conf.d
- ./www:/var/www
- ./logs:/var/log/nginx
environment
Определяет переменные среды, которые будут переданы в команду запуска Docker, например прописываем название нашего MySQL хоста, логин и пароль:
environment:
PMA_HOST: mysql
MYSQL_USERNAME: root
MYSQL_ROOT_PASSWORD: secret
port
Сопоставляет порт из контейнера с хостом следующим образом: порт компьютера:порт контейнера
, приложение работает на 80-м порту, после перенаправления будет доступно на 8000-м порту:
ports: - "8000:80"
links
Связь контейнера с любыми другими контейнерами:
links: - php
working_dir
Задает рабочий каталог создаваемого контейнера:
working_dir: /var/www/html/
depends_on
Ключевое слово depends_on
позволяет указывать, должен ли сервис в котором указан depends_on
прежде чем запуститься, ждать когда будут готовы к работе другие сервисы. Например, чтобы сервис в котором указан depends_on
дождался перед запуском готовности к работе сервиса под названием server
, мы должны прописать:
depends_on: - server
networks
За сеть у нас отвечает ключ networks
:
networks:
default:
driver: bridge
ipam:
config:
- subnet: 172.16.1.0/24
default
название нашей сети. Можете использовать любое названиеdriver
драйвер нашей сетиipam
конфигурация сети, в данном случае мы указываем нашу локальную подсеть 172.16.1.0/24
Все контейнеры могут работать в одной сети которая устанавливается автоматически. Если прямой доступ по IP до контейнеров не нужен, значит в файле docker-compose.yml можно вообще убрать все упоминания о сетях.
Команды
В docker-compose.yml есть две пары команд для запуска и остановки приложения целиком:
up
запускdown
остановка
В docker-compose.yml есть две пары команд для запуска и остановки отдельных приложений:
start
запускstop
остановка
Информация по командам:
docker compose --help
Запустить или вызвать все службы в файле docker-compose.yml:
docker-compose up
Пересобрать или вызвать все службы по новой в файле docker-compose.yml:
docker-compose up -d
Запустить только одну службу в файле docker-compose.yml:
docker-compose up название_службы
Просмотр запущенных контейнеров:
docker-compose ps
Откатить назад и удалить, всё что сделала команда up
, останавливает и затем удаляет контейнеры и сети:
docker-compose down
Остановить запущеный контейнер не удаляя его:
docker-compose stop название_контейнера
Запустить остановленный контейнер:
docker-compose start название_контейнера