Docker Compose 1.3: один yaml вместо шести страниц инструкции
Docker Compose 1.3 стабилизировал YAML-формат для многоконтейнерных приложений. Перевели тестовые стенды разработчиков с ручного docker run - и больше не жалеем.
Docker Compose 1.3 вышел со стабильным YAML-форматом для описания многоконтейнерных приложений на Docker
Год назад мы пробовали Fig - тогда ещё сторонний инструмент для описания многоконтейнерных стендов через YAML. В конце прошлого года Docker.inc поглотил Fig и переименовал его в Docker Compose. Прошлой весной вышел Compose 1.2 с кучей правок, но формат ещё гулял. Теперь вышел 1.3, и авторы говорят: формат docker-compose.yml стабильный, обратную совместимость будем держать. Это был сигнал для нас.
Мы перевели тестовые стенды разработчиков с ручного docker run на Compose-файлы. Ниже - что именно сделали, что пошло не так и почему теперь хотим распространить это на все новые проекты.
Откуда взялась боль
У одного из наших клиентов в интеграционных проектах типичный стенд разработчика выглядел примерно так: Django-приложение, PostgreSQL, Redis, Celery-воркер. Четыре процесса, каждый со своими параметрами.
Инструкция в вики занимала шесть страниц. Не потому что кто-то плохо писал - просто честная документация ручного процесса неизбежно такая. Установи Docker. Создай сеть. Запусти PostgreSQL с такими флагами. Подожди пока поднимется (у нас был даже цикл с pg_isready для этого, вставь его вот сюда). Запусти Redis. Теперь Django с такими переменными окружения и маунтом. Теперь Celery с теми же переменными. Не перепутай порядок.
Новый разработчик тратил день на то чтобы просто поднять стенд. Опытный - полчаса, если ничего не изменилось с прошлого раза. Если изменилось - мог и больше.
Что изменил Compose 1.3
Главное в 1.3 - не новые фичи, а стабилизация формата. В Compose 1.2 был ряд неудобств с наследованием конфигураций через extends, порядком переменных окружения и поведением при пересборке образов. Всё это в 1.3 либо исправлено, либо задокументировано как намеренное поведение.
Конкретные вещи, которые нас интересовали:
extends:работает предсказуемо. Можно вынести базовые сервисы вdocker-compose.base.ymlи наследовать из него. Для нашей задачи это значит: один базовый шаблон для всех Django-проектов, поверх него - переопределения под конкретный проект.environment:поддерживает файл.env_file: .envвdocker-compose.yml- и разработчик хранит локальные секреты отдельно, не в самом файле. Звучит очевидно, но в ранних версиях это работало нестабильно.docker-compose up --buildпересобирает перед запуском. Одна из наших главных претензий к Fig и ранним Compose: нужно было помнить запуститьbuildотдельно. Теперь флаг, и всё в одном шаге.
Как выглядит итоговый файл
Вот то что у нас получилось для типичного Django-стенда:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
links:
- db
- cache
- worker
env_file:
- .env
db:
image: postgres:9.4
volumes:
- pgdata:/var/lib/postgresql/data
cache:
image: redis:2.8
worker:
build: .
command: celery -A myapp worker -l info
links:
- db
- cache
env_file:
- .env
Это весь стенд. docker-compose up - и через минуту-другую всё работает. docker-compose down - всё остановлено и удалено. docker-compose logs -f - все четыре контейнера в одном потоке с именем сервиса в начале каждой строки.
Шесть страниц инструкции превратились в файл и одну команду.
Что спотыкается до сих пор
Было бы нечестно написать что всё гладко.
Порядок запуска и готовность сервисов. links: гарантирует что контейнеры запускаются в правильном порядке, но не гарантирует что PostgreSQL внутри контейнера успел инициализироваться до того как Django попытается подключиться. Наш pg_isready-цикл в entrypoint никуда не делся. Это известная проблема, авторы говорят что это не дело Compose - и они правы, но новичков это сбивает с толку.
Тома при пересоздании. docker-compose down по умолчанию не удаляет тома с данными. Хорошо для базы - данные сохраняются между сессиями. Плохо когда нужен чистый стенд - надо знать про docker-compose down -v. Несколько раз получали «почему у меня в базе старые данные» от разработчиков, которые не знали этого флага.
build: кеш не всегда очевиден. Docker кеширует слои образа, и если зависимости в requirements.txt не менялись - пересборка быстрая. Но если разработчик добавил системную библиотеку в Dockerfile не на том слое - кеш слетает и всё пересобирается с нуля. Это особенность Docker вообще, не Compose, но именно через Compose это часто вылезает первый раз.
Шаблон для новых проектов
По результатам этого перехода мы начали готовить шаблон docker-compose.yml для новых проектов в рамках наших интеграционных работ. Идея простая: есть базовый файл с типовыми сервисами (web, db, cache), есть .env.example с нужными переменными, есть Makefile с командами make up, make down, make logs. Разработчик клонирует репозиторий, копирует .env.example в .env, запускает make up. Всё.
Шаблон пока сырой и живёт у нас внутри, не публичный. Надо проверить на нескольких реальных проектах прежде чем говорить что это рецепт.
Одно наблюдение, которое уже очевидно: формат docker-compose.yml можно коммитить в репозиторий рядом с кодом и Dockerfile. Это значит что конфигурация стенда живёт в том же PR что и изменения кода. Если разработчик добавил новую зависимость - он же обновил docker-compose.yml. Никакой отдельной страницы в вики, которая протухает через три месяца. Это, пожалуй, самое ценное что мы из этого извлекли.
В конце июля Kubernetes 1.0 вышел с большой помпой - писали про это отдельно. Compose и Kubernetes - разные уровни задачи: Compose про dev-окружение на одном хосте, Kubernetes про production-кластер. Пока нас устраивает Compose там где он есть, и думать о продакшн-оркестрации будем отдельно.
- Fig + Docker: один yaml-файл вместо тридцати строк баша · 17 июня 2014
- Docker 1.0: первый стабильный, но в продакшн не спешим · 3 июня 2014