Как стать автором
Обновить

Обновление процесса CI/CD: octopus deploy

.NET *DevOps *
image Третья часть статьи об обновлении CI/CD процессов. 

На данном этапе у нас есть (по крайней мере должно быть) понимание того как будет работать CI, что мы имеем на входе, как из этого получить результат, и что нужно сделать чтобы выход билда стал полноценным модулем проекта и запустился в работу. А также установленный octopus (желательно LTS). В прошлой части рассматривался процесс настройки teamcity: всё что происходит с момента когда код попадает в репозиторий и до момента получения готового архива который можно распаковать, и в теории, он должен заработать.

Напоминаю оглавление:

Часть 1: что есть, почему оно не нравится, планирование, немного bash. Я бы назвал эту часть околотехнической.
Часть 2: teamcity.
Часть 3: octopus deploy.
Часть 4: за кадром. Неприятные моменты, планы на будущее, возможно FAQ. Скорее всего, тоже можно назвать околотехнической.

Octopus: общее


На входе у нас код, на выходе артефакты сборки, тут все понятно. Но результат для каждого модуля состоит из двух частей: core-пакет (результат непосредственно билда), и individual-пакет (уникальные конфигурации и библиотеки). Значит, для того чтобы конкретно взятый модуль заработал, нам нужно примерно следующее:

  1. Инстанс в IIS, у которого есть свой пул и который «смотрит» в document root. Для каждого модуля выделяется отдельный пул.
  2. Файлы core-пакета в document root.
  3. Файлы individual-пакета в document root.
  4. Желательно, забекапить перед всем этим старое содержимое document root. Мы, естественно, в себе уверены, но люди делятся на два типа: те кто не делает бекапы, и те кто уже делает. 
  5. По требованию (установке параметра) забекапить базу с которой работает модуль (требование заказчика). Регулярные бекапы есть отдельно, но иметь свежий бекап лучше, чем суточной давности.
  6. Было бы неплохо проверить, а запускается ли то, что мы там надеплоили.
  7. Уведомить чат о результате, если деплоили прод.
  8. Показать весь список клиентов (доменов) которых обслуживает данный модуль.

Не то чтобы сильно много, но и не мало. С шагом 1 проблем никаких. У octopus есть шаблон Deploy to IIS, в нем есть все необходимое. На шагах 2 и 3 остановимся подробнее чуть ниже. Для шага 4 достаточно prompted переменной, которую мы будем передавать из teamcity. Шаг 5 — это powershell скрипт. Он же будет и ротировать бекапы. Зачем нам хранить их все, старые то можно удалить. Шаг 6 — тоже powershell. Шаг 7 — готовый шаблон Slack — Detailed Notification. Шаг 8 — снова powershell. Звучит как план, значит будем реализовывать.

Тенанты


Немного подробнее о шагах 2 и 3. Я предполагаю, что читатель знаком с octopus deploy. Если нет — есть замечательные (и единственные) статьи тут же, на хабре. Базовое понимание они дать смогут. Вот они: одна и вторая. Итак, у нас есть вкладки проектов, инфраструктуры, тенантов, библиотеки, текущих заданий и настроек. Нужная — тенанты. Дословный перевод слова tenant — наниматель. В данном контексте, скорее всего имелось ввиду заказчик, клиент. Октопус говорит нам, что тенанты позволяют деплоить разные инстансы проекта сразу для множества клиентов (тенантов). Так выглядит страница с их списком.

image

В данном случае тенанты используются немного по другому. Каждый тенант представляет собой отдельного клиента. Для тенанта есть возможность добавить проекты которые будут запускаться при его деплое. Тут соответственно будет core-проект и individual-проект.
Для каждого тенанта существует два тега: TenantRole — общий для всех, обозначающий что на все тенанты с этим тегом деплоится модуль конкретного типа (core), и ModName — тег совпадающий с именем модуля (individual). 

image

Последний октопус получает из тимсити в качестве параметра, таким образом идентифицируется модуль который деплоится в данный момент (Deploy.Env и Deploy.2).

Как видно из скрина, при деплое модуля для конкретной организации сначала запустится core проект (шаг deploy.2 teamcity), затем individual проект (шаги deploy.3 и deploy.4 teamcity). Каждый тенант имеет доступ к переменным всех проектов которые в него входят, а также к переменным непосредственно тенанта (собраны в variable set, и содержат тег ModName).

image

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

  • переменная env.tenantRoleTag — содержит TenantRole (имя тега тенанта в octopus), и собственно его значение;
  • env.modName — значение тега ModName в octopus.

Переменные


Переменные — это достаточно важная часть проекта, и надо их грамотно структурировать. В данном проекте, переменные octopus делятся на три типа: общие для всех проектов (собраны в Library set «Environment»), индивидуальные для каждого проекта (настраиваются непосредственно в проекте), и переменные непосредственно тенантов (тоже library set). В Environment, как уже и было сказано, вынесено все общее для всех проектов, в частности: 

  • базовые пути к document root (например, C:\inetpub\);
  • базовый URL (например, для всех модулей общим доменом является organization.com);
  • доступы к базам данных (нужны для организации бекапов);
  • etc.

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

image

Например, строки 3, 6 и 7 на скрине. Строка 3 — это общий случай. Сюда попадает все, что нужно деплоить в окружение production. Строки 6 и 7 описывают вполне конкретные случаи, например: окружение production с ролью «CompanyWebsite» (строка 6), и окружение production с тегом тенанта «orgznizationX». Это нужно для идентификации серверов на которые будет попадать пакет, потому что для organizationX, например, выделен отдельный сервер.

В переменные для проекта входит все остальное. То, что не является общим и отличается для разных окружений, например полное доменное имя модуля. В общем случае, оно составляется как #{Tenant.ModName}.#{BaseModuleDomain}, однако, может отличаться в некоторых случаях.

image

 Как раз такой случай представлен на картинке.

Каналы


Ещё стоит рассказать про каналы. Если коротко и быстро — каналы позволяют устанавливать логику деплоймента. Очень прям классно это описано в официальной документации, в частности тут и тут. Например, в канал production может попасть релиз только с тегом stable, и т.д. В проекте есть 4 канала, которые совпадают с окружениями (development, test, staging, production). Релизы в них попадают согласно pre-release тегам. Он указывается в build number format teamcity (Build.General settings части про teamcity). Из teamcity канал передается в шаге Deploy.2 в поле additional command line arguments (параметр --channel).

Шаблоны


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

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

$url = "$scheme" + "://" + "$domain"
Write-Host "Requesting '$url'"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$request = [System.Net.WebRequest]::Create($url)
$request.Timeout = $timeout
$response = $request.GetResponse()
$response.Close()

Скрипт принимает на вход схему, домен и таймаут по истечении которого считаем что сайт не работоспособен. Создать шаблон можно в Library — Step templates.

Вводим название, выбираем логотип. Далее настраиваем входные параметры и их значения по умолчанию. Значением по умолчанию может выступать и значение указанной переменной (значение переменной #{HostName} является дефолтным значением входного параметра #{Domain}).

image

Далее, немного модифицируем скрипт, и добавляем его в качестве шага к исполнению:

image

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

image

Octopus: подробности процесса


Все проекты поделены на группы, описание будет проводиться на примере группы Modules. В ней содержатся следующие проекты: 

  • core;
  • individual module projects.

Начнем с core проекта.

Вот так выглядит обзорная страница:

image

Тут можно наглядно посмотреть какие версии ядра куда попали. Прошу прощения за сумбур в номерах версий, сейчас переходим на версионность завязанную на git tag, и везде кроме production уже используется новая версионность.

Как видно, для некоторых тенантов нет определенных окружений. Они либо еще не созданы, либо не предусмотрены. 

Процесс деплоя core


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

image

Шаг 1: бекап базы данных. Он опционален, и основывается на значении параметра variable который передается из teamcity в шаге Deploy.2 в поле additional command line arguments. По умолчанию, эта переменная установлена в false, так как бекап базы нужно делать только по требованию, а не постоянно. В тимсити запуск с изменением этого параметра выглядит так (нужно нажать на троеточие рядом с кнопкой run):

image

В octopus же, это выглядит так: 

image

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

На что стоит обратить внимание: 

  • все значащие параметры задаются переменными, это позволяет быстро менять их значение в одном месте, не копаясь глубоко в настройках процесса;
  • имя пула IIS совпадает с именем инстанса и с именем сервисного домена (для удобства);
  • в шаге deploy IIS установлено Merge with existing bindings, вместо замены. Так как кроме сервисного у нас есть еще куча клиентских доменов на каждом инстансе;
  • Очистка document root (галочка purge this directory before installation) не выполняется. Новые файлы заменяют старые по ходу копирования. Это сделано для того, чтобы сайт не «крашился» во время деплоя. Задержка при загрузке страницы гораздо приятнее чем экран смерти.

Скрипт ротации бекапов:

$days = $OctopusParameters["DaysStoreBackups"]
$limit = (Get-Date).AddDays(-$days)
$pathDb = $OctopusParameters["DbBackupDir"]
$pathFiles = $OctopusParameters["FilesBackupDir"] + '\' + $OctopusParameters["HostName"]

# Delete files older than the $limit.
Get-ChildItem -Path $pathDb -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $pathDb -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse

# Delete files older than the $limit.
Get-ChildItem -Path $pathFiles -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $pathFiles -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse

Процесс деплоя individual пакета


На этапе доставки индивидуального пакета модуля выполняется деплой конфигов и уникальных библиотек, расположение и/или переименование файлов, запрос сайта (во первых, сайты на .net первый раз загружаются долго, данный шаг позволяет выполнить эту процедуру сразу, во вторых это позволяет понять не возникает ли каких-то ошибок при загрузке сайта). Также отправляется уведомление в slack и обратно в teamcity передается актуальный список всех доменов принадлежащих данному модулю. 

image

Из важного:

  • В шаге deploy очистка document root не производится (там же уже новый core package);

Шаг 3, скрипт. Был чуть выше при описании шаблонов.

Шаг 5. Он же связан с шагом deploy.4 в teamcity.

В octopus это выглядит так:

$baseDomain = $OctopusParameters["HostName"]
$domains = (Get-WebBinding $baseDomain).bindingInformation | %{ $_.Split(':')[2] } | Sort-Object | Get-Unique
Write-Host "##teamcity[setParameter name='AllInstanceDomains' value='$domains']"
Write-Host "##teamcity[setParameter name='ServiceDomain' value='$baseDomain']"

Синтаксис ##teamcity… позволяет установить параметр билда в teamcity. Собственно, в нем, этот шаг выглядит так:

Write-Host "All domains on instance %ServiceDomain% :"
$domains = "%AllInstanceDomains%"
$domains = $($domains.Split(" ") | ForEach-Object {"http://$_"} | Out-String)
Write-Host $domains
Write-Host "Service domain: http://%ServiceDomain%"

Octopus: итоги


Весь процесс билда выглядит следующим образом:

  • из определенной ветки (соответствующей окружению) собирается код;
  • результат сборки (артефакты) отдаются октопусу и состоят из двух частей: ядра и индивидуального пакета модуля. Это позволяет эффективно использовать дисковое пространство и экономит общее время доставки обновления. 
  • Каждый пакет имеет версию, состоящую из номера и pre-release тега, который определяет канал и окружение в которое попадет данный релиз.
  • Октопус по входным данным создает релиз и доставляет пакеты на соответствующие сервера, руководствуясь данными об организации (tenant tags), каналами (pre-release tag) и дополнительными инструкциями (опциональный бекап базы), а также делает элементарную проверку работоспособности модуля.

На этом вроде как все. Скорее всего, будет еще одна околотехническая статья, так сказать, за кадром. В нее войдет все что не вошло сюда: некоторые подробности, что стало новым в процессе работы и с чем возможно придется столкнуться тем кто решится на подобное, исправления и апдейты по проекту, возможно частые вопросы/ответы. Спасибо за внимание.
Теги:
Хабы:
Всего голосов 3: ↑3 и ↓0 +3
Просмотры 3.7K
Комментарии Комментировать