В статье расскажу, как настроить деплой монолита на Ansible, как перейти с PowerShell и как ускорить развёртывание приложения и базы данных. А ещё поделюсь полезными фишками, статьями и скриптами, которые помогут в работе.
- Почему Ansible, а не PowerShell
- Как настроить деплой приложения
- Как деплоить базу данных Oracle
- Деплой бэкенда и фронтенда приложения
- И пара лайфхаков
Почему Ansible, а не PowerShell
- Один подход написания плейбуков для Windows и Linux-серверов. То есть у нас есть единая среда автоматизации. И если ты умеешь писать под Linux, то сможешь и под Windows.
- Достаточно декларативный язык. Мы указываем, что хотим получить на выходе, и Ansible своим движком выдаёт результат.
- Ansible позволяет хранить всю инвентаризацию серверов и стендов (а только в нашей системе их порядка десяти, плюс различные настройки приложения для разных стендов).
- Огромное количество модулей для работы с различными фреймворками, программным обеспечением и железом.
- Есть полная поддержка Redhat.
Тем, у кого планируется большой объём автоматизации на Ansible или других скриптах и требуется комплексное управление всем этим процессом, стоит посмотреть в сторону opensource проектов AWX или Rundeck.
Как настроить деплой приложения
Подготовить серверы
Сперва открываем сетевые проходы от Linux-агентов в TeamCity до управляемых серверов. Это порты 5985/tcp, 5986/tcp для Windows и 23/tcp в Linux для SSH.
Затем добавляем учётную запись, под которой мы будем подключаться и управлять конечными серверами в локальную группу Администраторы. Включаем службу WinRM — настройки по умолчанию будет достаточно — и аутентификацию CredSSP (другие виды аутентификации либо сложно настроить в компании, либо вовсе нельзя).
winrm quickconfig
Enable-WSManCredSSP -Role Server -Force
Add-LocalGroupMember -Group Administrators -Member "ваша учетка для управления"
После остаётся только подготовить инструменты для работы с самим Ansible. Я использую Git, VS Code и набор плагинов: GitLens, Redhat Ansible, Redhat XML и Prettier — Code formatter.
Создать репозиторий для деплоя
В Bitbucket создаём отдельный репозиторий, где будем хранить всё, что связано с деплоем на Ansible. Это нужно, чтобы отделить скрипты деплоя от репозитория проекта системы, тем самым ограничив права доступа к ним. Доступ должен быть только у DevOps-инженеров и прикладных администраторов. Мало ли кто случайно поправит конфиг-файл для продуктивного сервера — и после деплоя получим неработающее приложение.
Подготовить билд конфигурации в TeamCity для запуска Ansible
Сначала готовим отдельную build configuration (далее — билд), которая собирает Ansible плейбуки для конкретной задачи. Этот билд подключается в build chain общего процесса деплоя, после того как все артефакты прошли проверки ИБ и загружены в хранилище Nexus.
Это позволяет параллельно использовать билд Ansible для разных веток проекта.
Допустим, в проекте требуется создать новый Windows-сервис. Разработчики создают отдельную ветку для этой задачи и разрабатывают сервис. Но нам надо уже готовить деплой для будущего сервиса: установить сервис, настроить файлы конфигурации. Чтобы ничего не ломать в текущем деплое, в Ansible-проекте создаём ветку с аналогичным названием под разработку этой новой фичи и тестируем их параллельно.
После того как сервис будет готов и протестирован вместе с нашим изменённым деплоем, делаем Pull Request в master-ветку Ansible. И получаем master-деплой, уже скорректированный под новый сервис.

Build chain
Как деплоить базу данных Oracle
- Разработчики готовят изменения в БД в виде SQL-запросов и помещают в changeset Liquibase, которые применяются в процессе обновления.
- Liquibase артефакты проходят проверку синтаксиса и проверку на безопасность, после чего загружаются в Nexus.
- Ansible в виде плейбука запускает процесс применения миграций.
- После обновления БД, Ansible проверяет логи Liquibase, была ли успешной миграция. Если всё хорошо, он переходит к следующей схеме, если нет — выводит сообщение об ошибке.
Liquibase можно запускать напрямую из агентов TeamCity, в виде команды liquibase —changelog-file=master.changelog.xml update-sql, но мы поместили его запуск в Ansible, чтобы весь процесс деплоя был в плейбуке и не хранился в билдах TeamCity.
Зелёным цветом подсвечиваем успешные выполнения задач, а красным то что выполняется с ошибкой.
Иногда миграция БД завершается ошибкой, чаще всего из-за неверных SQL-скриптов. Такие ошибки передаются специалисту, который занимался их разработкой.
Почему Liquibase
В нашем проекте, в БД хранятся не только данные, но логика приложения (да-да, так тоже бывает, когда приложению более 10 лет).
И так как у нас есть множество стендов (Dev/Test/Prod и другие), то нужно контролировать процесс миграции схемы базы данных. Запускать файлы с SQL-скриптами для можно, но процесс требует человеческого контроля. А где человек — там потенциальные ошибки: не доглядел, забыл, не проверил. В итоге через какое то время БД на разных стендах будет отличаться. Поэтому для управления процессом миграции схем мы используем Liquibase. Кроме того, он:
- позволяет хранить все изменения, которые мы вносим в базу данных в виде кода;
- позволяет делать ревью миграции;
- лучше контролирует процесс изменения схемы БД;
- разрешён к использованию внутри компании.
Деплой бэкенда и фронтенда приложения
Ansible из коробки управляет файлами и фаерволом, создаёт сайты и пулы приложений в IIS, настраивает порты, привязывает SSL-сертификаты и многое другое.
Это ускоряет создание деплоя. Мы можем взять пустой сервер с ОС Windows, запустить установку фронтенда или бэкенда и Ansible настроит всё за нас:
проверит папку приложения и SSL-сертификат;
- загрузит файлы приложения из Nexus и скопирует на сервер в нужную папку;
- настроит пул приложений и сайт и привяжет порты и SSL-сертификата;
- настроит XML-файлы конфигурации приложения — об этом подробнее ниже;
- настроит аутентификацию для приложения;
- проверит, что все всё запущено и работает;
- настроит сервис Windows.
На выходе мы получаем готовый бэкенд, фронтенд или интеграционный сервис. Все настройки приложения хранятся в файлах инвентаризаций для стендов. А для каждого сервера есть переопределения имен, путей к папкам, сертификатов, конфигурационных параметров и прочее.
Кстати, мы используем и сокращённую версию деплоя, которая пропускает половину задач: останавливает сервис, обновляет файлы и конфигурацию и запускает сервис в работу. Делается это при выполнения плейбука Ansible с помощью тэгов. Такой подход ускоряет деплой примерно на 30%, используется при разворачивании фич на тестирование.
Отдельное внимание стоит обратить на файлы конфигураций
В .NET Framework 4.8 и ниже конфигурация приложения хранится в XML-файлах web.config или app.config (далее — конфиг-файл). Из-за этого возникают проблемы:
- надо заботиться об информационной безопасности — то есть в файлах конфигураций не должно быть паролей, токенов и всего что запрещено регламентами ИБ;
- при автоматизации деплоя, нужно оставить возможность откатиться на предыдущую версию;
- кто-то из сотрудников может случайно изменить конфигурацию при коммите; и приложение может перестать работать
- сложно динамически переопределить некоторые параметры конфигурации для стендов.
В .NET версий, 5.0 и выше всё чуть проще: можно хранить конфиги внутри проекта с переопределением для конкретных стендов, а также использовать app secrets и dotnet_environment. В другом проекте, мы уже используем именной такой подход.
Есть два варианта решения.
Использовать механизм трансформации конфиг-файлов msbuild
Мы делаем несколько конфиг-файлов под разные стенды или серверы. И в момент сборки, указываем в Configuration Manager, для какой среды собираем проект: Test, Dev или Prod. Затем msbuild через схему XDT меняет конфиг и помещает его в папку с остальными артефактами приложения
У такого подхода есть несколько минусов:
- все настройки и схема изменения хранятся внутри проекта;
- конфиг собирается в момент сборки, хранится в артефактах приложения и уже в готовом виде попадает на стенд в процессе деплоя;
- изменениями конфиг-файлов занимаются разработчики;
- хранение паролей в открытом виде в Git и артефактах.
Да, есть механизм шифрования секций конфигурационных файлов. Но он не снимает проблемы с хранением в Git — и достаточно сложный в эксплуатации. Лучше доработать приложение, чтобы оно взаимодействовало с ПО HashiCorp Vault или Cyberark.
Шаблонизировать конфиг-файлы в Ansible
В Git по умолчанию хранится конфигурационный файл с базовыми настройками. Так, разработчикам удобнее работать на локальных машинах.
В Ansible хранится файл вида app.config.xdt.j2, в котором хранится шаблонизированная схема XDT для модификации эталонного файла. Звучит странно, но это самый гибкий, безопасный и удобный в эксплуатации способ.

Файл app.config.xdt.j2
Первичные затраты времени на подготовку таких xdt.j2-файлов, сводятся к сравнению эталонного конфиг-файла с тем что есть на стенде. С этим помогает утилита FatAntelope, которая сравнивает файлы и готовит XDT-схему для модификации. Схему мы помещаем в Jinja-файл и шаблонизируем конкретные параметры.
То есть в процессе деплоя приложения мы делаем следующее:
- формируем схему: xdt.j2 -> XDT-схема;
- применяем её к эталонному конфигу: Git config + XDTсхема
- gолучаем .config-файл для стенда
Минусы:
- тратится время на модификацию xdt.j2-файлов
И пара лайфхаков
Ускоряем Ansible
Когда мы использовали PowerShell для деплоя приложения, запуск скрипта и обновление файлов занимали 20–30 секунд. Но плейбуки на Ansible занимали по 1–2 минуты. Мы начали искать способы ускорить работу.
Сперва оптимизировали запуск самого PowerShell. В чём суть? Библиотеки, связанные с PowerShell, лежат не в машинных кодах, а выполняют JIT-компиляцию каждый раз в момент запуска PowerShell. Это тормозит запуск любого скрипта Ansible.
Чтобы такого не было, достаточно выполнить скрипт, который предлагают сами разработчики Ansible, один раз на всех серверах.
function Optimize-PowershellAssemblies {
# NGEN powershell assembly, improves startup time of powershell by 10x
$old_path = $env:path
try {
$env:path = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | % {
if (! $_.location) {continue}
$Name = Split-Path $_.location -leaf
if ($Name.startswith("Microsoft.PowerShell.")) {
Write-Progress -Activity "Native Image Installation" -Status "$name"
ngen install $_.location | % {"`t$_"}
}
}
} finally {
$env:path = $old_path
}
}
Optimize-PowershellAssemblies
Так, выполнение плейбуков на Windows ускорится на 20–30%.
Затем мы выяснили, что в Ansible начиная с версии 2.7 добавлена поддержка протокола PSRP. Это ускоряет работу плейбуков ещё на 30%. Из минусов — протокол не так просто включить в нашем случае, так как нужно скачать несколько пакетов и установить их на агентах в TeamCity, где используется Ansible. Для большинства же, всё просто: pip install pypsrp
.
Третий способ — установить OpenSSH 8-й версии на Windows Server и подключаться к нему по SSН-протоколу, а не WinRM. Это тоже ускорит работу, но фактические замеры мы не делали.
Добавляем статистику
В Teamcity логи Ansible красиво подсвечены, и в конце есть статистика скорости выполнения плейбука и конкретных задач. Это включается добавлением в корень проект деплоя Ansbile конфига anisible.cfg.
[defaults]
force_color = True
deprecation_warnings = False
host_key_checking = False
callback_enabled = timer, profile_tasks, profile_roles
callback_whitelist = timer, profile_tasks, profile_roles
stdout_callback = teamcity
Линтинг скриптов Ansible
Мы достаточно много времени тратим на разработку деплоя. И хорошо бы перед его запуском проверять общее качество кода, синтаксические ошибки. Для этого используйте линтер Anisble, который установлен на агентах TeamCity.
Нужно установить триггер в TeamCity на появление нового коммита или Pull Request в проекте деплоя. И после автоматом проверяете код на потенциальные ошибки.
Тестируем деплой
Обычно стенды Dev/Test/Prod отличаются друг от друга. На Dev стенде всё устанавливается на одном сервере, а в продуктиве всё разнесено по разным серверам и используется балансировка.
Мы часто меняем и рефакторим деплой приложений. И чтобы быть уверенными, что на всех стендах от отработает корректно, необходимо тестировать его после каждого изменения.
Самое простое — запускать его на стенды. Но если это не получается сделать, например, они все заняты тестировщиками и разработчиками, можно добавить параметр —check во время запуска плейбука. Тогда плейбук отработает всю логику, но без фактического применения на сервере, и вы убедитесь, что всё работает корректно. Для удобства можно сделать кнопку «Тестовый запуск».
Важно! Не все модули корректно работают в режиме --check
. Если вы сначала загружаете файл артефакта, а потом распаковываете его, даже при тестовом запуске он будет реально искать файл для распаковки. А не найдя его выдаст ошибку. Решается это с помощью параметра check_mode: no
.
На этом всё, если будут вопросы пишите. А если что-то заинтересует, расскажем подробнее.