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

Docker Compose v2: именованные сети, volumes и первый взгляд на Swarm

Перевели staging на Compose v2 формат: именованные сети и volumes в одном файле вместо links и магических соглашений. Тестируем деплой через Docker Swarm.

Контекст момента

Docker Compose 1.7 и Docker 1.11 вышли с поддержкой формата Compose v2 и сетевых плагинов; Swarm получил совместимость с новым форматом

Docker 1.11 и Compose 1.7 вышли с разницей в несколько дней, и центральная тема у обоих - формат Compose file v2. Мы как раз возились со staging-окружением одного из проектов на сопровождении и решили не откладывать - перевели на новый формат и заодно посмотрели, как это работает через Docker Swarm.

Что было не так с v1

В старом формате docker-compose.yml не было явного понятия сети. Compose создавал сеть автоматически, именовал её по директории, и контейнеры соединялись через links. На небольшом файле это выглядело терпимо, но при трёх-четырёх сервисах с зависимостями friends-of-friends начиналась вермишель из links и volumes_from.

Главная боль - links. Мы уже писали про это в контексте Docker 1.10: link привязывает имя к конкретному запущенному контейнеру, не к абстракции. Перезапустил один сервис - остальные, которые на него ссылаются, могут потерять резолвинг. В v1 Compose это частично обходил, но не устранял.

Volumes в v1 тоже жили в рамках сервиса: либо анонимные, либо через volumes_from от другого контейнера. Никакого явного lifecycle, никакого именования на уровне файла.

Что изменилось в v2

Новый формат добавил четыре секции на верхнем уровне: version, services, networks, volumes. Это принципиальная смена - теперь сети и volumes - граждане первого класса, а не побочный эффект сервисов.

Минимальный docker-compose.yml для типичного web-проекта:

version: "2"

services:
  postgres:
    image: postgres:9.5
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend

  app:
    image: registry.internal/myapp:2.1.0
    environment:
      DATABASE_URL: postgres://postgres:5432/myapp
    networks:
      - backend
    depends_on:
      - postgres

  nginx:
    image: nginx:1.9
    ports:
      - "80:80"
      - "443:443"
    networks:
      - backend

networks:
  backend:
    driver: bridge

volumes:
  db-data:
    driver: local

Никаких links. Сервисы резолвятся по имени через встроенный DNS Docker 1.10+. depends_on - это порядок старта, не сетевая зависимость. Volume db-data существует независимо от контейнера postgres: можно удалить и пересоздать контейнер, данные останутся.

Что заметили при миграции

Перевод с v1 на v2 занял около получаса на проект, если не считать тест-прогона.

Первое - links просто удаляешь. DNS-резолвинг по имени сервиса работает из коробки в любой user-defined сети. Переменные окружения, которые раньше пробрасывал link (POSTGRES_PORT_5432_TCP_ADDR и подобные), больше не нужны - подключение через имя сервиса.

Второе - volumes_from уходит. Паттерн с data-container, где один контейнер держал только volume а другой его монтировал через volumes_from, был распространённым в v1. Теперь именованный volume монтируется напрямую в нужный сервис. Проще и явнее.

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

Четвёртое - depends_on не ждёт готовности. Это распространённое заблуждение. depends_on: postgres гарантирует, что контейнер postgres запустится раньше app, но не что postgres будет готов принимать соединения. Если приложение при старте сразу лезет в базу без retry - падает. Нужен или wait-for-it.sh, или retry-логика в самом приложении. Мы это знали, но напомнили себе ещё раз при тесте.

Docker Swarm и Compose v2

Мы смотрим на Kubernetes как на оркестратор для нагруженных проектов, но k8s - это инфраструктура с порогом входа. Для части проектов нужна оркестрация проще, и Swarm - кандидат.

Docker 1.11 с Compose v2 позволяет деплоить через docker-compose на Swarm-кластер почти без изменений в файле. Проверили на двухузловом стенде: Swarm-менеджер и один воркер.

# Создание токена кластера (Swarm Classic)
docker run --rm swarm create
# вывод: <cluster-token>

# На менеджере
docker run -d -p 2375:2375 swarm manage token://<cluster-token>

# На воркере
docker run -d swarm join --addr=10.0.1.11:2375 token://<cluster-token>

# Деплой через compose файл
DOCKER_HOST=tcp://swarm-manager:2375 docker-compose up -d

Swarm распределяет контейнеры по нодам на основе ресурсных ограничений и constraints. Без явных constraints - round-robin по доступным нодам. Для stateless-сервисов это работает. Для postgres с volume - нужен constraint на конкретную ноду, иначе контейнер может переехать на другую машину без данных.

services:
  postgres:
    image: postgres:9.5
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      constraint:node: "==node-1"

Это ограничение Swarm-а: shared storage для volumes из коробки нет. Либо NFS, либо volume-плагины (Flocker, Convoy). Мы этот вопрос оставили открытым - для текущего проекта postgres живёт на одной ноде и нас это устраивает.

Где сейчас

Staging-окружение двух проектов работает на Compose v2. По ощущениям - файлы стали читаемее, структура понятнее без знания внутренних соглашений v1.

Swarm попробовали, понял общую механику. Для проектов, где k8s избыточен, это рабочий вариант - но с оговоркой про stateful-сервисы. Пока держим на нём только staging, production ещё не трогали.

Параллельно смотрим на k8s 1.2 - там ingress и улучшенный autoscaler. Выбор между Swarm и k8s для конкретных проектов будет отдельной темой, когда наберём больше наблюдений на обоих.

Контакт

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

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