[Перевод] Полная автоматизация среды разработки с помощью docker-compose


2c7de08d18154fb0bb5d900b736c5435.png

Согласно данным, которые представил на Dockercon 2016 CEO компании Docker Бен Го́луб (Ben Golub), количество работающих в контейнерах Docker приложений за последние два года выросло на 3100%. Docker обеспечивает функционирование 460 тысяч приложений по всему миру. Это невероятно!


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


Мы бы хотели поделиться своим опытом использования docker-compose в больших проектах. Применив этот инструмент для автоматизации задач, связанных с разработкой, тестированием и конфигурированием, мы за несколько простых шагов смогли сделать нашу команду более эффективной и сфокусироваться непосредственно на разработке продукта.


Проблема


В начале карьеры, когда я был еще молодым разработчиком на c# и asp.net, развертывание окружения для разработки было непростой задачей. Требовалось установить базы данных и необходимые для работы приложения инструменты. При этом конфигурационные файлы должны были быть изменены таким образом, чтобы соответствовать настройкам локальной машины. Приходилось прописывать порты, пути к локальным директориям с обновлениями и так далее. Эти шаги обычно были плохо документированы, поэтому на запуск среды разработки уходило огромное количество времени.


Многие продукты в начале своего развития не отличаются сложностью, но по мере реализации новых функций разобраться с ними становится все труднее. В них добавляются новые инструменты и подсистемы, такие как дополнительные базы данных и очереди сообщений. Из-за растущей популярности микросервисов монолитные монстры больших приложений все чаще дробятся на много частей. Подобные изменения обычно требуют участия всей команды, работающей над проектом. Разработчик, который вносит изменения, ломающие локальные окружения, обычно пишет длинные письма со списком необходимых для настройки шагов. Вспоминается случай, когда один работающий за океаном специалист провел серьезное изменение структуры продукта, написал письмо с инструкциями по восстановлению работоспособности локальных окружений и пошел спать. Думаю, вы догадались, что случилось дальше. Правильно: он забыл упомянуть несколько важных моментов. В итоге бо́льшая часть команды потеряла следующий рабочий день в попытках заставить работать обновленный код в своих локальных рабочих средах.


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


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


Быстрый старт с docker-compose


Docker-compose — это простой инструмент, который позволяет запустить несколько докер-контейнеров одной командой. Перед тем как окунуться в детали, я должен рассказать о структуре проекта. Мы используем monorepo, и кодовая база каждого сервиса (веб-приложение, API, фоновые обработчики) хранится в своей корневой директории. У каждого сервиса есть описывающий его зависимости Docker-файл. Пример такой структуры можно увидеть в нашем демонстрационном проекте.


Давайте начнем с автоматизации простого приложения, которое зависит от MongoDB и небольшого сервиса на Node.JS. Конфигурация для docker-compose находится в файле docker-compose.yml, который обычно помещается в корневую директорию проекта.


version: '2'  
services:  
  web:
    build:
      context: ./web
      dockerfile: Dockerfile.dev
    volumes:
      - "./web/src:/web/src"
    ports:
      - "8080:8080"
  mongo:
    command: mongod
    image: mongo:3.2.0
    ports:
      - "27100:27017" # map port to none standard port, to avoid conflicts with locally installed mongodb. 
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

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


$ docker-compose up

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


  1. context: ./web — таким образом указывается путь к исходному коду сервиса в рамках monorepo.
  2. dockerfile: Dockerfile.dev— для окружений разработки мы используем отдельный Dockerfile.dev. В production исходный код копируется прямо в контейнер, а для разработки подключается в виде тома. Поэтому нет необходимости заново создавать контейнер при каждом изменении кода.
  3. volumes: - "./web/src:/web/src" — таким образом каталог с кодом добавляется в docker в виде тома.
  4. Docker-compose автоматически связывает контейнеры друг с другом, поэтому, например, веб-сервис может получить доступ к mongodb по имени: mongodb://mongo:27017

Всегда используйте аргумент --build


По умолчанию, если контейнеры уже есть на хосте, docker-compose up их не пересоздает. Для принудительного выполнения этой операции используется аргумент --build. Это необходимо, когда меняются сторонние зависимости или сам Docker-файл. Мы приняли за правило всегда выполнять docker-compose up --build. Docker отлично кэширует слои контейнера и не станет их пересоздавать, если ничего не изменилось. Постоянное использование --build может на несколько секунд замедлить загрузку, но предохраняет от неожиданных проблем, связанных с работой приложения с устаревшими сторонними зависимостями.
Совет: вы можете абстрагировать запуск проекта с помощью простого скрипта:


#!/bin/sh
docker-compose up --build "$@"  

Такой прием позволяет по необходимости менять опции и используемые при запуске инструменты. А можно просто выполнить ./bin/start.sh.


Частичный запуск


В примере docker-compose.yml одни сервисы зависят от других:


  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    volumes:
      - "./api/src:/app/src"
    ports:
      - "8081:8081"
    depends_on:
      - mongo

В этом фрагменте сервису api требуется база данных. При использовании docker-compose можно указать имя сервиса, чтобы запустить только его: docker-compose up api. По этой команде запустится MongoDB и после него сервис API. В больших проектах такие возможности могут пригодиться.


Эта функциональность полезна, когда разным разработчикам нужны разные части системы. Например, специалисту по фронтэнду, который работает над landing-страницей, не нужен проект целиком, достаточно лишь самой landing-страницы.


Ненужные логи в >/dev/null


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


  mongo:
    command: mongod
    image: mongo:3.2.0
    ports:
      - "27100:27017"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    logging:
      driver: none

Несколько файлов docker-compose


После запуска команды docker-compose up она по умолчанию ищет файл docker-compose.yml в текущей директории.


В некоторых случаях (мы поговорим об этом чуть позже) может понадобиться несколько файлов docker-compose.yml. Для подключения другого файла конфигурации может быть использован аргумент --file:


docker-compose --file docker-compose.local-tests.yml up  

Так зачем же нужны несколько файлов конфигурации? В первую очередь для разбиения составного проекта на несколько подпроектов. Радует, что сервисы из разных compose-файлов все равно могут быть связаны. Например, вы можете поместить в один файл docker-compose контейнеры, связанные с инфраструктурой (базы данных, очереди и т. д.), а в другой — контейнеры, связанные с приложениями.


Тестирование


Мы используем различные виды тестирования: unit, integrational, ui, linting. Для каждого сервиса разработан отдельный набор тестов. Например, интеграционные и UI-тесты требуют для запуска api- и web-сервисы.


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


version: '2'  
services:  
  api-tests:
    image: app_api
    command: npm run test
    volumes:
      - "./api/src:/app/src"
  web-tests:
    image: app_web
    command: npm run test
    volumes:
      - "./web/src:/app/src"

Наш compose-файл с тестами зависит от основного docker-compose-файла. Интеграционные тесты подключаются к development-версии api, UI-тесты — к web frontend. Тестовый compose-файл лишь запускает контейнеры, созданные в основном docker-compose-файле. Если нужно запустить тесты только для одного сервиса, можно использовать частичный запуск:


docker-compose --file docker-compose.local-tests.yml up api-tests  

Эта команда запустит только тесты для api.


Префиксы имен контейнеров


По умолчанию всем контейнерам, запущенным с помощью docker-compose, присваивается префикс в виде имени родительской директории. Имя директории в различных средах разработки может меняться. Из-за этого Docker-compose-файлы с тестами, о которых мы говорили ранее, могут перестать работать. Мы используем префикс (app_) для контейнеров в основном файле docker-compose. Для согласованной работы конфигурации в различных средах мы создали специальный .env-файл в директории, в которой запускаем docker-compose:


COMPOSE_PROJECT_NAME=app  

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


Заключение


Docker-compose — это полезный и гибкий инструмент для запуска ПО, используемого для работы над проектами.


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


1) установить Docker и Docker-compose,
2) скопировать репозиторий GitHub,
3) выполнить в терминале команду ./bin/start.sh.


Чтобы лучше понять изложенные в этой статье концепции, рекомендуем посмотреть демонстрационный проект, размещенный на GitHub. Делитесь своим опытом и задавайте вопросы.


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


Ссылки:
Оригинал: Fully automated development environment with docker-compose

Комментарии (0)

© Habrahabr.ru