ADG Оставить заявку
Блог DevOps 5 мин чтения

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 там где он есть, и думать о продакшн-оркестрации будем отдельно.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.