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

Итак, задача:
Имеется софт, который билдят при помощи того же Azure DevOps, собирают из проекта на WIX. Если будет интерес, напишу и об этом инструменте. По факту это более оптимизированный под автоматизацию способ сборки виндовых установщиков, заменяющий собой стандартный InstallShield. Итак, наш софт успешно собирается и генерирует артефакт, некий setup.exe, который ставит приложение в систему Windows. Необходимо это приложение поставить в похожую на прод виртуалку, скопировать туда автоматизированные тесты, подготовленные командой тестирования, запустить их и забрать результаты, чтобы считать ветку хорошей или плохой, прежде чем мерджить. Все как в GitLab, только через ж....

В качестве среды виртуализации, где мы будем исполнять наши тесты, используем, очевидно, Azure DevTest Labs, некую сущность в подписках Azure, которая для того и создана, чтобы крутить в ней всякую тестовую ерунду за приемлимые деньги.

1. Интеграция на стороне облака


Для начала нам потребуется интегрировать наш DevTest Labs с Azure DevOps, для чего нам необходим некий Service Principal, по сути сервисная учетная запись, которая позволяет ходить пайплайнам в облако и создавать/удалять там ресурсы для себя.

Идем в подписку и находим сервис Azure Active Directory



Находим App Registrations и кликаем на New Registration, это нам создаст нашего service principal. Подробно разбирать какие настройки выбрать при создании не буду, это может отличаться у разных подписок.



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



Далее в Access Control жмем Role Assignment и ищем в поиске по созданному только что имени эту учетку. Даем роль Contributor, этого достаточно.



Далее возвращаемся к нашему Service Principal в Azure AD и открываем его свойства. Позже, нам будут нужны будут все ID которые там есть, сохраняем их.

На этом наши настройки портала заканчиваются и мы переходим в Azure DevOps.

2. Интеграция на стороне Azure DevOps


Первым делом мы зайдем в настройки проекта и выбираем Service Connections. Создаем новый элемент типа Azure Resource Manager.



Сейчас нам понадобятся все ID что мы записали. Кликаем на use the full version of the service connection dialog. И вводим все данные что мы получили от Service Principal. Жмем verify и если все отлично сохраняем соединение. Теперь наши пайплайны могут его использовать для подключения к облаку.



3. Создание пайплайна


Теперь приступаем к самому интересному, построению непосредственно пайплайна. Открываем меню Pipelines-Builds



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



Из многообразия шаблонов нам потребуется простой Empty Pipeline. После его создания нас встречает пустое окошко редактирования, в нем мы дальше проведем довольно много времени.



Итак, нажимаем на + и попадаем в некий магазин модулей, откуда нам потребуются по списку следующие компоненты.



Прежде чем мы приступим к конфигурации тасков пайплайна, нам нужно сформировать и положить в проект несколько файлов. Это будут ARM Template нашей виртуальной машины, который мы сгенерируем в Azure DevTest Labs, скрипт доставания IP машины после того как она создана ну и, по желанию, скрипты наших тестов или того, что мы хотим на хосте запускать.

4. Генерация ARM Template


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

Идем в нашу лабу и находим меню Formulas (reusable bases), жмем создать новую.



Нас встретит длинный список имаджей в качестве базы, выбор размера машины, все то же самое как и при создании виртуалки. На этом этапе мы останавливаться не будем, сразу перейдем к последнему пункту свойств машины, а именно артефактам. Вы можете использовать любые конфигурации, которые необходимы для вашей среды. Например, я добавляю машину в домен и добавляю на нее сервисный аккаунт в качестве админа, чтобы пайплайн потом мог на эту машину заходить под этой учетной записью. Это все может варьироваться, но для успешного тестирования кода нам необходим один артефакт, на котором остановимся подробнее. Чтобы на нашу машину поставилась последняя версия тестируемого нами софта, мы будем использовать артефакт «Download Azure Pipelines Artifact and Run Script». Помните в начале я говорил что где-то собирается билд с установщиком приложения? Вот сейчас нам необходимо сказать виртуалке, а точнее темплейту, чтобы он пошел и забрал этот артефакт. И не просто забрал, а еще установил, для чего заполняем специальные поля с указанием проекта, названия билда и секретного ключа. Секретный ключ, как и во всех системах подобного рода, генерируется в учетной записи, в данном случае в Azure DevOps и сохраняется в Secrets в вашей лабе. Тут есть маленькая оговорка, в Secrets то мы его сохраним, но темплейту от этого ни холодно ни жарко, он будет запускаться уже от другого пользователя в рамках пайплайна, по этому секретный ключ нам надо будет еще раз вручную внести в темплейт.

Еще один артефакт, который нужно обязательно включить, это «Configure WinRM», нам он понадобится для последующего доступа на машину. Там всего один параметр, hostname. Так как мы его заранее не знаем, воспользуемся переменной %COMPUTERNAME%.



Итак мы добавили все нужные артефакты, переходим к тому зачем мы сюда вообще пришли. Достаем сгенерированный ARM Template во вкладке Advanced того же окна создания формулы.



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

5. Конфигурация пайплайна


Начнем с самого важного и инетерсного, создания виртуалки, ради этого мы и делали все эти интеграции и темплейты. В пункте Azure RM Subscription мы выбираем наш Service connection, который мы сконфигурировали в пункте 2. Далее должна будет выскочить доступная нам лабороторная среда. Потом выбираем json который мы сгенерировали и определяем некоторые обязательные переменные. Логин и пароль от машины можно задать или на прямую или переменными, но я вообще не уверен что он работает, что бы я туда не писал, зайти на машину потом под этими кредами не получалось, главное задать имя машины, чтобы оно по возможности было всегда уникальным. Для этого я использую переменную среды билда.



Далее мы настраиваем еще один немаловажный момент. После того как машина взлетит, нам же нужно как то знать ее параметры, а лучше не нам а пайплайну. Для этого мы делаем скрипт, к примеру GetLabVMParams.ps1 и кладем его туда же, в проект. Текст скрипта я взял на сайте Microsoft, но немного подправил его для своей среды, т.к. он брал PublicIP и FQDN машины. Ни того ни другого у меня нет, но есть PrivateIP который не так то просто достать, по этому я дописал кусок.

Param( [string] $labVmId)

$labVmComputeId = (Get-AzureRmResource -Id $labVmId).Properties.ComputeId

# Get lab VM resource group name
$labVmRgName = (Get-AzureRmResource -Id $labVmComputeId).ResourceGroupName

# Get the lab VM Name
$labVmName = (Get-AzureRmResource -Id $labVmId).Name

# Get lab VM public IP address
# $labVMIpAddress = (Get-AzureRmPublicIpAddress -ResourceGroupName $labVmRgName -Name $labVmName).IpAddress

# Get lab VM FQDN
# $labVMFqdn = (Get-AzureRmPublicIpAddress -ResourceGroupName $labVmRgName -Name $labVmName).DnsSettings.Fqdn

# Get lab VM private IP address
$VmNetworkdetails= (((Get-AzureRmVM -ResourceGroupName $labVmRgName -Name $labVmName).NetworkProfile).NetworkInterfaces).Id
$nicname = $VmNetworkdetails.substring($VmNetworkdetails.LastIndexOf("/")+1)
$labVMnetwork = (Get-AzureRmNetworkInterface -Name $nicname -ResourceGroupName $labVmRgName)|Select-Object -ExpandProperty IPConfigurations 
$labVMIpAddress = $labVMnetwork.PrivateIpAddress

# Set a variable labVmRgName to store the lab VM resource group name
Write-Host "##vso[task.setvariable variable=labVmRgName;]$labVmRgName"

# Set a variable labVMIpAddress to store the lab VM Ip address
Write-Host "##vso[task.setvariable variable=labVMIpAddress;]$labVMIpAddress"

# Set a variable labVMFqdn to store the lab VM FQDN name
Write-Host "##vso[task.setvariable variable=labVMFqdn;]$labVMFqdn"

Write-Output $labVMIpAddress

Set-Item wsman:\localhost\client\trustedhosts * -Force

Из всего, что считывает скрипт, нам нужна только переменная labVMIpAddress. Ну это мне, может вам еще что-то понадобится, по этому я ничего не удалял и просто закоментировал лишнее.

Так же объясню последнюю строчку скрипта, она разрешает нашей билд машине доступ до любого хоста по WinRM.

Следующим этапом мы запускаем наш замечательный скрипт. Ему потребуется то же самое соединение с облаком, входная переменная с ID машины, которое будет к тому моменту уже известно из предыдущего шага. Как? Тут нужно упомянуть о такой замечательной вещи, как Output Variables. У каждого шага может быть список переменных которые передаются дальше, следующим шагам пайплайна. Соответственно для нашего супер скрипта такой переменной будет labVMIpAddress, не забудьте это указать.



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

New-Item “C:\test" –type directory
New-SMBShare –Name “test” –Path “C:\test”  –FullAccess everyone

Из названия тасок понятно, что дальше мы копируем некоторый sample script на машину и еще одним этапом его исполняем. В качестве адреса удаленной машины нам пригодится наша переменная $(labVMIpAddress). Далее мы используем таску «забрать артефакт с шары» и копируем результаты исполнения скрипта себе в билд среду, потом такой же стандартной таской сохраняем эти файлы в артефакт билда. После того как машина нам больше не нужна, последним этапом её убиваем. Главная сложность, как видно по объему статьи, это интегрироваться с облаком и наладить контакт с виртуалкой, которую вы создали, дальше уже можно развлекаться сколько нужно.

Это моя первая статья, так что не судите строго, замечания приветствуются.

Комментарии (3)


  1. mikechips
    18.07.2019 20:49

    Кажется, у Microsoft опять та же болезнь — желание сделать всё по-своему убивает все надежды продукта на жизнь, создавая упомянутые вами проблемы в духе "нет документации" и "слишком сырой, чтобы пользоваться". Или просто ничего не понятно. Жаль :(


    1. vsantonov Автор
      18.07.2019 21:42

      Я бы сказал наоборот, они пытаются взять все лучшее и с разной степенью успеха повторить, конечно это пока хреново выходит, но вот уже богомерзкий TFS сменили на git и пайплайны из графического интерфейса потихоньку становятся YAML. В любом случае, корпоративный клиент плачет, но кушает кактус, т.к. очень удобно иметь одну подписку где у тебя сразу и облако и разработчики кодят и возможно лицензии на винду в догонку. Чтобы собрать подобное на классических Gitlab-Jenkins-Docker нужен целый штат админов, причем высококлассных, а тут какой-никакой селф сервис.


      1. mikechips
        18.07.2019 21:46
        +1

        Согласен насчёт взять всё лучшее, но, к сожалению, пересаживаться с привычных инструментов на Microsoft — каждый раз горе очам, не в последнюю очередь из-за громоздких интерфейсов