Appveyor + Docker = ❤

Appveyor + Docker = ❤

Не так давно Appveyor добавили поддержку Linux агентов сборки, а это значит, что пришло новое счастье для множества open source проектов. Ведь раньше для сборки проекта под Linux необходимо было использовать что-то другое, например Travis CI, и выглядело это все как-то так:

Старая схема CI

И все бы хорошо, но скорость Travis CI для открытых проектов оставляет желать лучшего. Сейчас же мы можем построить полный цикл неприрывной интеграции (CI) на Appveyor. Я хочу рассказать, как настроить сборку docker образа, публикацию образа в репозиторий, а так же запуск скрипта для обновления docker контейнеров. Все примеры подразумевают использование open source проектов и не требуют оформления подписки. Если интересно, как скрыть приватные файлы конфигураций в открытом проекте, а так же не иметь проблем с доступом к этим файлам уже в docker контейнере, пишите комментарии, опишу отдельно этот вопрос.

Appveyor поддерживает конфигурацию в 2х режимах - через визуальную админ часть или через конфигурационные файлы *.yml. Оба подхода имеют одну и ту же суть, разница в том, что админ часть создает конфигурационный файл на сервере и после использует его, а ручной вы можете создать в любом текстовом редакторе и использовать его, храня у себя в исходном коде. По умолчанию используется серверная конфигурация, но если добавить конфигурационный файл в репозиторий вашего проекта, то будет использоваться он. Я покажу всю настройку на примере собственного файла. В качестве подопытного выступает непосредственно этот блог. Для него сформирована следующая конфигурация:

# Версия, которая будет присваиваться каждой сборке. Переменная build - инкремент номера сбороки.
version: 1.0.0.{build}
# Образ виртуальной машины на которой будет происходить сборка.
image: ubuntu

# Глубина клонирования репозитория. По умолчанию клонируется вся история. Флаг 1 указывает клоинирование без истории.
clone_depth: 1

# Номер сборки не будет инкрементироваться при создании pull request'а.
pull_requests:
  do_not_increment_build_number: true

# Переменные окружения.
environment:
  DOCKER_PASS:
    secure: tyCs62GdU59M1vNsnAwKrw==
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
  DOTNET_CLI_TELEMETRY_OPTOUT: true

# Параметр указывает, что сборка будет производиться только при изменении master ветки. По умолчанию сборка производится на всех ветках.
branches:
  only:
    - master

skip_commits:
  files:
    - README.md

install:
- docker version

build_script:
- ps: .\build.ps1

# Этап выполнения тестов отключен
test: off

# Используется потому как не поддерживается скрипт и провайдер одновременно в deploy
before_deploy:
- ps: .\deploy.ps1

deploy:
- provider: Webhook
  url:
    secure: bwtxM3PwlQqNCxXgKZ7Aul9FWEx9OTMjV4suCKf0E6vOI
  authorization:
    secure: xFex7sajn/RKjS/cZrqSJnEjrhRO4535ALSzvC+OjzY7xVlq==
  on:
    branch: master

К каждому пункту конфигурации добавлены комментарии, так будет проще и быстрее понять общий сценарий. Отдельно отмечу официальную документацию https://www.appveyor.com/docs/ в ней расписаны детально все ньюансы, но только на английском языке. Давайте посмотрим непосредственно на скрипты build.ps1 и deploy.ps1. Они запускаются в соответствующие названию этапы обработки. К каждому этапу можно добавить сразу несколько скриптов.

build.ps1 - скрипт запускает сборку docker образа и присваивает ему сразу несколько тегов.

$Version=$env:APPVEYOR_BUILD_VERSION
Write-Host Starting build $Version

docker build -t d2funlife/blog:latest -t d2funlife/blog:$Version .

deploy.ps1 - скрипт логинится в docker hub репозиторий и выполняет push образа, который мы собрали на предыдущем этапе.

$Version=$env:APPVEYOR_BUILD_VERSION
Write-Host Starting deploy $Version

docker images

Write-Host Docker login

$env:DOCKER_PASS | docker login --username d2funlife --password-stdin

Write-Host Push to docker hub

docker push d2funlife/blog

И когда образ опубликован, остается только один вопрос - как же обновить его на нашем хосте. Список доступных вариантов деплоя представлен в документации https://www.appveyor.com/docs/deployment/. К сожалению, не поддерживается выполнение ssh команды на удаленном сервере. С использованием docker контейнеров мы можем запустить такой сценарий, но это что-то из разряда костылей и подпорок + необходимо быть внимательным к вопросу безопасности. Хорошей альтернативой является Webhook. Это веб-запрос на указаный вами url, с параметрами авторизации (для безопасности). Приняв запрос на сервере, вы можете выполнить необходимую команду для обновления docker контейнеров. Я сделал простой веб-сервис на .NET Core, который принимает запрос и запускает shell скрипт обновления. Таким образом мы получаем полностью настроенную непрерывную интегарцию с помощью Appveyor.

d2funlife | Даниил Павлов 2015-2020
Powered by ASP.NET Core 2.2, Entity Framework Core 2.2. Web + UI