Можете представить, что Вам больше никогда не придется устанавливать зависимости и настраивать конфигурации вручную на вашем сервере непрерывной интеграции? А вы верите в то, что каждый шаг вашего билда может быть по-настоящему изолированным и работать исключительно в Docker контейнерах? В конце концов, хотели бы вы попробовать инструмент, который входит в топ 20 всех открытых проектов, написанных на Golang, и имеет 9k+ звездочек на Github?
В этой статье мы хотели бы рассказать о великолепном Drone CI, который уже помог нам упростить и сделать лучше нашу непрерывную интеграцию. Мы поделимся деталями установки Drone CI и покажем на примере небольшого проекта все детали использования. Если вы не любите много читать и хотите сразу попробовать, в конце статьи есть ссылки на Github репозитории, которые помогут с быстрым стартом.
Перед тем как перейти к основной теме, я хотел бы поблагодарить наших читателей за большое количество добрых отзывов о статье о docker-compose и, конечно же, Brad Rydzewski, автора Drone CI, без которого этой статьи не было бы. Это огромная мотивация для нас писать дальше!
Если вы уже знакомы с термином "Continuous Integration" (CI) и хотели бы узнать побольше — начните с замечательной статьи от Martin Fowler. Непрерывная интеграция уже давно стала для нас чем-то привычным, тем, что помогает нам выявлять неполадки и обеспечивает полную автоматизацию процесса развертывания приложения, сохраняя при этом огромное количество времени для всей команды.
Я использую CI серверы на протяжении уже 7 лет и, как многие из нас, начинал с Jenkins, позже TeamCity, а затем влюбился в Travis CI. Каждый из этих продуктов сделал очень многое для развития практики непрерывной интеграции. Одна из идей, которая заставляет меня менять инструменты время от времени — это возможность полной автоматизации любых процессов. У Jenkins и TeamCity очень развитый пользовательский интерфейс, позволяющий настроить непрерывную интеграцию для любого проекта, но это достаточно сложно поддается автоматизации. Travis — очень хороший иструмент и по-прежнему остается вариантом номер 1 для всех моих open-source начинаний, так как "Testing your open source project is 10000% free". Travis был первым инструментом, который позволял настроить непрерывную интеграцию с помощью одного файла .travis.yml
.
"Pipeline as a code" — это относительно новый подход, позволяющий настроить 'deployment pipeline' с помощью кода вместо ручной настройки запущенного CI сервиса. На сегодняшний день эта концепция очень популярна, и мне известно как минимум пять игроков в этом сегменте: LambdaCD, Concourse, Drone, GoCD и Travis CI. Этот подход позволяет не только проще автоматизировать непрерывную интеграцию и непрерывное развертывание, но также позволяет тестировать инфраструктуру для развертывания. Эта концепция заставила взглянуть на мир по-другому, но самое главное — это то, что она действительно позволяет использовать CI более эффективно и изящно.
На сегодняшний день у нас в команде работает 6 человек, и наш подход к непрерывной интеграции и развертывания достаточно прост. Мы активно используем Github, Pull Requests, Code Review. Если вы хотите глубже познакомиться с "Continuous Delivery" — обратите внимание на книгу "Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation" от авторов Jez Humble и David Farley.
Основные шаги нашей непрерывной интеграции и непрерывного развертывания:
Процесс имеет несколько недостатков и потребует некоторых изменений, когда наша команда станет больше. Но на сегодняшний день он нас полностью устраивает и, на наш взгляд, идеально подходит для небольших команд до 10 человек. К основным недостаткам можно отнести:
Устранением этих недостатков мы займемся по мере роста нашей команды. На сегодняшний день мы уже можем почти безболезненно разворачивать наш проект несколько раз в день. Мы еще не разворачиваем приложение как Github, но первые шаги уже сделаны :)
После сравнения различных инструментов наш выбор пал на Drone CI и за последние три месяца мы полностью перешли на него. Drone CI имеет 9003 звездочек на Github (15 марта, 2017). Drone CI входит в top 20 приложений написанных на Go на Github. Канал в Gitter никогда не спит — там можно получить ответы на любые вопросы.
Drone CI представляет собой один Docker контейнер размером в 8 мегабайт. Этот контейнер содержит два сервиса:
Все данные о прошедших и текущих билдах сохраняются в базу данных. По умолчанию используется Sqlite, но есть возможность использовать другие реляционные базы данных, такие как PostgreSQL и MySQL. Специально для этой статьи, мы подготовили Github репозиторий, который поможет вам установить Drone CI на локальном окружении или на вашем production окружении всего за несколько минут.
Для начала хотелось бы вкратце познакомить вас с принципом работы Drone CI. После того как вы установили и залогинились в Drone CI с помощью Вашего Github аккаунта, Drone автоматически отображает все ваши репозитории. Первым шагом Вам нужно включить репозитории, для которых вы хотите настроить непрерывную интеграцию:
На этом работа с пользовательским интерфейсом практически закончена. Дальше он будет нужен только для того, чтобы смотреть состояние ваших билдов.
Вся настройка шагов развертывания осуществляется в одном файле: drone.yml
. Этот файл обычно находится в корне вашего репозитория и полностью описывает все, что происходит у вас на CI сервере. По сути, .drone.yml
представляет собой произвольный набор шагов, каждый из которых запускается в отдельном, изолированном Docker контейнере. При каждом коммите, перед запуском шагов из .drone.yml
, Drone автоматически клонирует наш репозиторий и добавляется его как Docker volume для каждого шага.
Чтобы стало понятней, давайте посмотрим на простую конфигурацию, которая запускает тесты, собирает образ докера, публикует его на Dockerhub и отправляет простую нотификацию в Slack, когда сборка закончена.
pipeline:
run-tests:
image: node:6.3.0
commands:
- cd ./api && npm i --quiet
- npm test
publish-api-docker:
image: plugins/docker:1.12
username: ${DOCKER_USERNAME}
password: ${DOCKER_PASSWORD}
email: ${DOCKER_EMAIL}
repo: anorsich/ds-api
tags:
- latest
dockerfile: ./api/Dockerfile
context: ./api/
slack-notification:
image: plugins/slack
webhook: https://hooks.slack.com/services/...
username: drone-ci
channel: andrew
icon_emoji: ":rocket:"
Это все! Теперь при каждом коммите в репозиторий у вас будут запускаться тесты, собираться контейнеры и приходить нотификация в Slack.
Каждый шаг в Drone выполняется в отдельном Docker контейнере, что позволяет вам не беспокоиться об установке и обновлении зависимостей на агентах сервера. Важным отличием является и то, что зависимости для каждого шага могут быть совершенно разными. В одном шаге вы можете запустить тесты для Node.JS, а уже в следующем спокойно запускать сборку приложения написанного на Go. Переход на новые версии платформ осуществляется одним лишь изменением версии контейнера. Например, мы легко можем добавить новый шаг, который запустит тесты и сборку проекта на последней версии Node.JS:
run-tests-on-latest-node:
image: node:7.7
commands:
- cd ./api && npm i --quiet
- npm test
По сути, настраивать ваш CI сервер нужно лишь в одном случае — если вышла новая версия самого CI сервера. Все остальное настраивается прямо в репозитории в .drone.yml
, без единого клика. Установил — и забыл :)
Важно отметить, что каждый шаг абсолютно изолирован от других, так как выполняется в отдельном контейнере. Больше никаких конфликтов версий!
Еще одна возможность, которую мы пока не используем, но хотелось бы упомянуть, — это поддержка матричных сборок, которая позволяет сразу тестировать ваш код на различных версиях платформ, баз данных и так далее.
Иногда есть необходимость запускать шаг в вашем билде только при определенных условиях. По умолчанию все шаги запускаются последовательно и, если один из шагов сломался, последующие не запускаются. Основные ограничения, которые мы используем это:
Например мы хотим отправлять нотификацию в Slack когда билд выполнился успешно и когда сломался. Для этого мы используем секцию when
и добавляем status
:
slack-notification:
image: plugins/slack
...
when:
status: [ success, failure ]
event: [ push, tag, deployment, pull_request ]
Более подробно с ограничениями можно познакомится здесь.
Еще одна интересная особенность, которую стоит упомянуть, — это возможность не запускать билд, добавив в сообщения коммита: [ci skip]
.
Плагины — это подход Drone CI для интеграции со сторонними сервисами, такими как Amazon S3, Dockerhub, Slack. Полный список всех плагинов можно найти здесь. Каждый плагин представляет из себя отдельный Docker контейнер, который выполняет заранее определенную задачу. В нашем примере выше мы использовали два плагина:
plugins/docker
) — для сборки и публикации образа Docker на Dockerhub. plugins/slack
) — для отправления уведомления в Slack.На сегодняшний день плагины решают большинство типичных задач, но не все. Для запуска любой специфической для вашего проекта задачи Вам достаточно обернуть эту задачу в образ Docker — и Вы можете начать использовать ее в Drone CI. Вам доступны любые языки программирования, которые можно запустить в Docker. Непрерывную интеграцию для ваших специальных шагов можно организовать опять же с помощью Drone.
Консольная утилита Drone позволяет общаться с удаленным сервером и выполнять различные административные задачи. После установки вам нужно подключиться к вашему удаленному серверу. Для этого в терминале нужно экспортировать две переменные:
export DRONE_SERVER=http://MY_DRONE_URL
— URL вашего Drone сервера.export DRONE_TOKEN=
— персональный токен, который создается после того, как вы авторизовались в Drone UI. Вы можете найти его на следующей странице: https://MY_DRONE_URL/account
. Просто нажмите SHOW TOKEN
и скопируйте. Теперь консольная утилита должна быть настроена.
У Drone есть очень удобные инструменты для безопасной работы с приватной информацией, такой как пароли и ssh ключи (одним словом — секреты). В примере выше мы использовали Docker плагин для публикации образа Docker на Dockerhub, которому нужен ваш пароль и имя пользователя.
В Github репозитории с примером организации непрерывной интеграции для Node.JS приложений с помощью Drone мы также используем Ansible. С его помощью мы запускаем Docker контейнеры на удаленном сервере. Для общения с удаленным сервером требуется отдельный ключ. Как известно, хранить секретную информацию в Github репозитории считается плохой практикой, которая может быть использована хакерами для компрометации вашего приложения. В Drone CI эта проблема решена.
Для начала, давайте разберемся как мы можем добавить пароль и имя пользователя для Dockerhub (используется плагином plugins/docker
):
drone global secret add DOCKER_USERNAME andrew
drone global secret add DOCKER_PASSWORD password
drone global secret add DOCKER_EMAIL email
Вы можете добавлять секреты только к конкретному репозиторию или для всех репозиториев в рамках вашего Drone CI с использование global
. Если Вы хотите добавить секрет только для определенного репозитория, Вам нужно указать его имя и не использовать global
:
drone secret add maqpie/drone-starter DOCKER_USERNAME andrew
Как только ваши секреты добавлены, вы можете использовать их с помощью простого синтаксиса ${DOCKER_USERNAME}
прямо в .drone.yml
. Вот небольшой пример:
publish-api-docker:
image: plugins/docker:1.12
username: ${DOCKER_USERNAME}
password: ${DOCKER_PASSWORD}
email: ${DOCKER_EMAIL}
Как упоминалось ранее, нам также необходим ssh ключ для работы с удаленным сервером. Ключ можно добавить следующим образом:
drone global secret add SSH_KEY @/Users/andrew/.ssh/id_rsa-drone-demo
Чтобы защитить ваши секреты, Drone использует простой механизм подписи вашего .drone.yml
. При каждом изменении в .drone.yml
, Вам нужно его переподписать. Drone проверяет подпись каждый раз перед запуском билдов. Если подпись не совпадает, секреты не будут публиковаться и не будут доступны.
Для подписи используется ваш личный авторизационный ключ, который вы добавили при настройке консольной утилиты drone:
drone sign maqpie/drone-starter
При подписи нужно указать имя репозитория, в котором находится .drone.yml
. В результате этой команды должен появиться файл .drone.yml.sig
, который должен комититься в репозиторий.
Важно: Перед тем как подписывать ваш .drone.yml
для вашего репозитория, убедитесь, что этот репозиторий включен в Drone UI — иначе файл подписи не будет создан.
Несмотря на хороший механизм защиты секретов в Drone, у хакеров все же остается несколько возможностей получить к ним доступ. Например, если Вы используете баш скрипт в каком-то из шагов, хакер может использовать curl
либо другой инструмент, чтобы получить доступ к вашему паролю или ssh ключу. Всегда применяйте общие принципы безопасности.
Сервисы в Drone UI позволяют запустить любой контейнер во время выполнения вашего билд процесса. Все сервисы находятся в одной подсети с контейнерами билд процесса. Такая возможность может быть очень полезной для различных типов интеграционного тестирования. Сервисы обычно объявляются в конце .drone.yml
. Пример запуска базы данных MySQL выглядит следующим образом:
services:
database:
image: mysql
environment:
- MYSQL_DATABASE=test
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
На текущем этапе мы не используем сервисы, вместо этого мы используем более простой для нас подход с использованием docker-compose.
В нашем предыдущей статье мы поделились нашей любовью к docker-compose. Хотелось бы немного остановиться на том, как мы запускаем наши тесты в Drone CI. Шаг в .drone.yml
выглядит вот так:
run-tests-in-compose:
image: michalpodeszwa/docker-compose:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- ./bin/drone-run-tests.sh api-tests
- ./bin/drone-run-tests.sh web-tests
when:
event: [pull_request]
/bin/drone-run-tests.sh
запускает тесты с помощью небольшой обертки над docker-compose. Перед тем как углубиться в наш подход, хотел бы рассказать о компромиссах, на которые мы пошли. В первую очередь, мы отказались от изоляции контейнера вот в этой строчке:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Если вкратце, то эта строчка фактически дает возможность этому контейнеру запустить любую команду, которую может запустить сервис docker, что позволяет запустить любую команду на хосте, так как docker сервис запускается под root
пользователем. Более подробно со всеми последствиями Вы можете ознакомиться в этой статье. Такой способ однозначно не подходит для общих репозиториев.
Почему же мы пошли на такой риск? Есть несколько причин:
Теперь, когда вы знаете о минусах в нашем подходе, давайте разберем сам подход. По сути, для запуска тестов мы просто используем docker-compose up --file docker-compose.drone-tests.yml
. Таким же образом тесты запускаются и на рабочих окружениях. Единственная проблема, с которой мы столкнулись, запуская тесты через docker-compose, это то, что код выхода всегда у docker-compose был 0. Drone, как и любой другой CI, в таком случае считает, что билд был успешным и переходит к следующему шагу. Чтобы исправить эту ситуацию, мы написали небольшой скрипт, который анализирует коды выхода контейнеров после запуска тестов и, если хотя бы один из контейнеров вышел с ненулевым кодом, использует этот код для выхода. Содержимое скрипта /bin/drone-run-tests.sh
, который вы видели выше, в шаге запуска тестов, выглядит следующим образом:
#!/bin/sh
# remove old containers
docker-compose --file docker-compose.drone-tests.yml rm -f
# run tests
docker-compose --file docker-compose.drone-tests.yml up --build
echo "Inspecting exited containers:"
docker-compose --file docker-compose.drone-tests.yml ps
docker-compose --file docker-compose.drone-tests.yml ps -q | xargs docker inspect -f '{{ .State.ExitCode }}' | while read code; do
if [ "$code" != "0" ]; then
exit $code
fi
done
Как и в любом инструменте, в Drone CI есть некоторые вещи, которые находятся в разработке и не всегда работают так, как хотелось бы. К счастью, их оказалось всего несколько:
--quiet
при запуске npm install
— стало выводиться меньше бесполезной информации. Не совсем понимаю, почему в npm эта опция не используется по умолчанию. Если у нас получилось убедить Вас попробовать Drone, ниже Вы можете найти список ссылок, которые могут быть полезными:
Drone CI — это современное решение проблем непрерывной интеграции. Используя Drone, Вы больше никогда не будете настраивать ваши сервера, получите полностью изолированную среду для запуска ваших билдов и сможете масштабировать ваш CI сервер до бесконечности. За два месяца работы мы полностью в него влюбились.
Чтобы помочь Вам начать использовать Drone CI, мы подготовили два Github репозитория:
Делитесь Вашим опытом и задавайте вопросы!
И да, если статья понравилась вам хотя бы на 51%, или не понравилась всего на 49% — не стесняйтесь поставить нам звездочку на Github :)
Надеемся, что статья была полезной и поможет сделать Ваш продукт лучше!
Версию на английском, можно почитать здесь.
К сожалению, не доступен сервер mySQL