Pull to refresh

Полная автоматизация «development» среды с помощью docker-compose

Reading time 7 min
Views 158K

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


Docker в 2017


На конференции Dockercon 2016 CEO компании Docker рассказал, что количество приложений, которые запускаются в Docker выросло на 3100% за последние два года. Боле 460 тысяч приложений по всему миру запускаются в Docker. Это невероятно!


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


Что не так?


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


Многие проекты просты в начале, но становятся больше со временем. Это приводит к увеличению внешних зависимостей, таких как базы данных, очереди. В связи с ростом популярности микросервисов, многие проекты перестают быть монолитными и разделяются на несколько небольших частей. Любое такое изменение требует внимания всей команды, так как после таких изменений, проект нужно запускать по-другому. Обычно, разработчики, занимающиеся корневыми изменениями, пишут письмо, либо создают вики страничку с описанием шагов, которые нужно сделать, чтобы проект снова запустился на рабочих окружениях. Обычно это работает, но не всегда :) Однажды наша команда попала в ситуацию, когда разработчик с другого континента сделал много изменений в проекте, написал длинное письмо и ушел спать. Я полагаю, Вы знаете, что было дальше. Все верно, он забыл упомянуть несколько важных моментов. В результате, на следующий день часть команды просто не смогла запустить проект и день был потерян.


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


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


Docker-compose это простой инструмент, который позволяет настроить и запустить несколько контейнеров одной командой. До того, как мы нырнем глубже в docker-compose, нужно немного остановиться на структуре проекта. Мы используем "monorepo". Код каждого сервиса (frontend, api, worker, etc) находится в своей директории и имеет Dockerfile. Пример структуры проекта можно посмотреть здесь.


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


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, конфигурационный файл для docker-compose должен быть более-менее понятен, но стоит обратить внимание на несколько деталей:


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

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


По умолчанию, docker-compose up не будет перестраивать контейнеры, если они уже есть на хосте. Чтобы заставить докер делать это, нужно использовать аргумент --build. Обычно это нужно, когда сторонние зависимости проекта меняются или меняется докерфайл. В нашей команде мы всегда используем 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 сайтом, нет нужды запускать проект целиком. Он может просто запустить только те части, которые ему действительно нужны.


>/dev/null назойливые логи


Часто мы используем инструменты, которые генерируют много логов, тем самым отвлекая нас от полезных логов нашего приложения. Чтобы отключить логи для конкретного сервиса, нужно просто установить logging driver в 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 ищет конфигурационный файл docker-compose.yml в текущей директории. В некоторых случаях (поговорим об этом через минуту), вам придется создать несколько таких конфигурационных файлов. Чтобы сделать это, вам достаточно использовать аргумент --file:


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

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


Запуск тестов


Наши тесты включают в себя различные типы: юнит, интеграционные, UI тестирование, проверку синтаксиса кода. У каждого сервиса свой набор тестов. Интеграционные и UI тесты требуют api и web frontend для их работы.


В самом начале нам показалось, что мы должны запускать тесты при каждом запуске docker-compose. Но очень скоро мы поняли, что это не всегда удобно и занимает слишком много времени. В каких-то случаях нам также хотелось иметь немного больше контроля над тем, какие тесты запускать. Для этого мы используем отдельный конфигурационный docker-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"

Для запуска тестов необходимо, чтобы основной docker-compose был запущен. Интеграционные тесты используют рабочую версию api сервиса, а UI тесты используют web frontend сервиса. По сути тесты просто используют образы, которые собраны в основном docker-compose. Также возможен запуск тестов только для конкретного сервиса, например:


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

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


Префикс для контейнеров


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


COMPOSE_PROJECT_NAME=app

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


Заключение


Docker-compose это очень полезный и гибкий способ автоматизации запуска проектов.


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


  1. Установить Docker и Docker-compose
  2. Склонировать репозиторий
  3. Запустить в терминале ./bin/start.sh

Для того, чтобы Вам было проще понять эту статью, у нас есть пример проекта на Github. Делитесь вашим опытом и задавайте вопросы.


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


Версию на английском, можно почитать здесь.

Tags:
Hubs:
+21
Comments 53
Comments Comments 53

Articles