Статьи

Докер встречает непрерывное развертывание

Около года назад у меня был первый контакт с Докером. Этот новый ребенок в блоке обещал избавить наши бедные компьютеры от установки всех инструментов, языков, зависимостей и операционных систем. Изолированные среды запуска появились на компьютерах разработчиков.

Хотя мои товарищи по команде выбрали более консервативный подход, я начал использовать Docker с большой радостью. Несмотря на то, что многие люди описывают Docker как инструмент, написанный разработчиками для разработки, наша отрасль нашла новые способы использования изображений и контейнеров. Образы наших приложений и сервисов стали единицами развертывания таких инструментов, как Kubernetes, Docker Swarm или Marathon.

Но как создаются эти изображения?

Установка сцены

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

Прежде всего нам нужно небольшое приложение с HTTP API, которое мы можем вызвать после его развертывания. Давайте предположим, что мы используем Gradle для создания приложения, а TeamCity — как сервер непрерывной интеграции.

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

Сложение

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

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

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

Нам нужны два артефакта. Первый — Dockerfile . Мы уже подготовили этот файл в нашем исходном коде, и он будет храниться в каталоге docker . Следующий файл — это tar- файл, который будет сгенерирован нашей сборкой Gradle . Он содержит скрипт, который выполняет наш код и все необходимые библиотеки.

build_config

Теперь мы готовы проинструктировать нашу конфигурацию сборки, как загрузить наш исходный код. Мы используем GitHub в качестве нашего репозитория кода, поэтому нам просто нужно выбрать Git в качестве типа нашей Системы контроля версий и предоставить URL-адрес нашего приложения.

build_vcs

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

TeamCity имеет предопределенные раннеры для различных инструментов сборки. В этом случае мы будем использовать бегунок Gradle для тестирования и сборки .

build_step

Теперь мы можем запустить нашу сборку и в результате мы должны увидеть наши артефакты на вкладке «Артефакты» .

build_result

Выпуск

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

Давайте внимательнее посмотрим на Dockerfile . Мы копируем содержимое simple_application.tar (который содержит все необходимые библиотеки и сценарии) в образ с помощью команды ADD . Эта команда автоматически распакует все файлы внутри изображения. Затем мы открываем порт нашего HTTP API и определяем, как запустить наше приложение, добавив команду ENTRYPOINT .

Например, наш Dockerfile может выглядеть так:

1
2
3
4
5
6
7
FROM java:8
 
ADD simple_application.tar .
 
EXPOSE 4567
 
ENTRYPOINT ["/simple_application/bin/simple_application"]

Определить конфигурацию сборки очень просто. В общих настройках мы определяем новый артефакт: image.version . Содержимое этого файла будет сгенерировано на одном из этапов сборки.

release_config

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

release_artifacts

И наконец, мы должны запустить эту сборку автоматически после прохождения всех тестов, выполненных на предыдущем шаге. Введя Finish Build Trigger, мы можем начать эту сборку сразу после того, как TeamCity успешно завершит сборку приложения.

release_trigger

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

Построить изображение

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

1
docker build --tag registry.private/simple_application:%build.number% .

Команда build Docker возьмет наш Dockerfile и Dockerfile образ, помеченный как registry.private/simple_application и версией %build.number% . Переменная %build.number% является встроенной переменной TeamCity, содержащей текущий номер сборки.

release_step_1

Push Image

Образ, созданный на предыдущем шаге, существует только на компьютере агента. Чтобы сделать изображение доступным для других, нам нужно сохранить его в репозитории. Мы можем использовать Docker Hub , но в нашем примере мы используем частный репозиторий, доступный по адресу repository.private . Мы можем выполнить следующую команду, чтобы отправить изображение в хранилище.

1
docker push registry.private/simple_application:%build.number%

release_step_2

Сохранить версию

Изображение безопасно хранится в хранилище, но нам нужно сделать еще один шаг: сохранить версию нашего изображения. Еще раз запускаем скрипт оболочки для генерации файла image.version :

1
echo %build.number% > image.version

release_step_3

Развертывание

На предыдущем шаге мы создали образ, который теперь можем использовать для развертывания нашего приложения. Мы собираемся создать другую конфигурацию сборки: Deploy . Эта сборка будет запускать контейнер Docker на агенте TeamCity на основе образа из фазы выпуска .

Наша конфигурация сборки должна содержать три элемента. Первый триггер. Опять же, мы используем Finish Build Trigger с зависимостью от конфигурации сборки Release .

deploy_trigger

Второй элемент — это строка версии нашего изображения. Мы можем получить эту информацию из артефакта, созданного сборкой Release .

deploy_artifact

Последний шаг немного сложнее. Перед тем, как мы развернем новую версию приложения, сценарий развертывания должен проверить, развернут ли на компьютере контейнер с предыдущей версией. Если у нас есть такой контейнер, нам нужно остановить и удалить его. Затем мы читаем текущую версию из артефакта и создаем новый контейнер на основе этой версии.

01
02
03
04
05
06
07
08
09
10
if [ -n "$(docker ps --filter label=simple_application -q)" ]; then
   docker stop simple_application
   docker rm simple_application
fi;
 
version_to_deploy=$(cat image.version)
docker run -d -p 80:4567 \
           --name simple_application \
           --label simple_application \
           registry.private/simple_application:${version_to_deploy}

deploy_step

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

Готово.

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

Используйте изображение Docker . Вы можете обеспечить согласованную среду выполнения для своего приложения на каждом этапе непрерывного развертывания. Теперь вы решаете, как выполняется код, и больше не можете обвинять администраторов в установке неправильной версии Java или Ruby.

Ссылка: Docker встречает Continuous Deployment от нашего партнера по JCG Роберта Фирека в блоге Crafted Software .