Об IaC под Windows пишут мало, потому что
DevOps/SRE ассоциируется в основном c Linux и Kubernetes. Мы решили
исправить эту ситуацию и сравнить инструменты, которыми можно
управлять IaC на базе Windows. Статья будет полезна разработчикам,
которые работают с Windows-инфраструктурой и выбирают способы
управления, и тем, кто уже внедрил Powershell DSC или Ansible, но
сомневается в своем решении. Ниже поделимся опытом и расскажем:
- как устроен Powershell DSC и чем он отличается от Ansible при
управлении инфраструктурой на Windows;
- почему мы перешли на Ansible;
- с какими проблемами столкнулись и как их решали;
- как соотносятся ожидания и реальность после перехода на
Ansible;
- кому стоит выбрать Powershell DSC, а кому Ansible.
Почему изначально выбрали PowerShell DSC
В Mindbox развита культура DevOps/SRE, несмотря на преимущественно
Windows-инфраструктуру: Hyper-V, IIS, MS SQL Server. И хотя
компания постепенно переходит на Linux и Open Source, Windows пока
превалирует.
Чтобы управлять этой инфраструктурой, планировали использовать
инфраструктурный код: писать его, сохранять в репозиторий, а затем
с помощью какого-то инструмента превращать код в реальную
инфраструктуру. В то время как Ansible самый популярный инструмент
управления инфраструктурой через код, он все-таки традиционно
ассоциируется с Linux-миром. Нам хотелось что-то нативное и
заточенное под Windows, поэтому выбрали PowerShell DSC.
Как работает Powershell DSC
PowerShell Desired State Configuration (DSC) это сервис, который
уже есть в Windows из коробки и помогает управлять инфраструктурой
через конфигурационные файлы. На вход он принимает инфраструктурный
код на PowerShell, а внутри преобразует его в команды, которые
конфигурируют систему. Кроме тривиальных операций, например
установки Windows-компонентов, изменения ключей реестра, создания
файлов или конфигурирования служб, он умеет многое из того, что
обычно выполняется PowerShell-скриптами. Например,
полный
цикл настройки DNS или
высокодоступный инстанс MS SQL Server.
Полезные ссылки к схеме:
Пример простой конфигурации для документов по DSC
Как использовать датафайлы
Как использовать SQL Server-базу в качестве бэкэнда для Windows
Server 2019
Как настроить DSC pull server для работы с базой данных SQL для
версий раньше Windows Server 2019
Чем DSC отличается от Ansible
Проблемы, которые возникли с DSC
Ожидания от DSC оправдались не во всём. Кроме этого, во время
работы возникли новые потребности, которые не могли удовлетворить с
помощью DSC.
Разработчики не могут использовать инструмент самостоятельно без
помощи SRE. Хотя почти в каждой команде есть SRE, инструмент
IaC должен быть достаточно простым, чтобы разработчик мог им
пользоваться и тратить на это не больше получаса. DSC позволяет
использовать не только декларативный код, но и любые конструкции на
Powershell. Это значит, что высок шанс сделать код, который будет
сложно сопровождать или который приведет к инфраструктурной аварии.
Например, развертывание приложения с некорректными параметрами не в
той среде.
Невозможно пропустить конфигурацию в режиме dry run перед
прокаткой, чтобы увидеть, какие именно изменения будут
применены, а какие нет.
Для DSC трудно организовать синтаксические проверки и проверки
стиля кода. Инструментов для проверки мало, и они не
обновляются. Для Ansible мы это уже сделали.
В push-режиме DSC нет удобного способа отслеживать состояние
тасков. В случае если конфигурация применилась с ошибкой, для
диагностики следует совершать дополнительные действия: выполнять
команды, чтобы получить статус применения конфигурации, смотреть
журналы событий. Если ошибка произошла на нескольких серверах, то
это отнимает много времени.
Pull-режим так и не стал преимуществом. В нем конфигурация
применяется асинхронно узнать, когда точно закончено применение
новой конфигурации, невозможно без обвязок и костылей.
Избыточное использование двух отличных друг от друга
инструментов IaC, которые конфигурируют серверы. Ansible может
делать то же, что и DSC, а ведь мы уже используем Ansible для
конфигурирования Linux-хостов и сетевого оборудования.
Как планировали перейти с DSC на Ansible
Сначала задача казалась простой, приблизительно на месяц. Мы
выделили три этапа работ:
- научиться подключаться к Windows-хостам с помощью Ansible;
- переписать конфигурации DSC с помощью Ansible-модулей;
- удалить DSC pull server, его базу данных и прочие
артефакты.
Вот какой рабочий процесс был на DSC, и как планировали
организовать в Ansible:
Стандартная структура ролей в Ansible
На Ansible мы планировали отделить сложный код, который что-то
конфигурирует и устанавливает, в код ролей и разнести роли по
отдельным репозиториям. В главном репозитории Ansible должны были
остаться только вызовы ролей, переопределения параметров ролей и
списки серверов по группам. Так не только SRE, но и любой
разработчик мог бы развернуть роль на нужные серверы или подправить
параметр, не углубляясь в логику инфраструктурного кода. Исправить
же код роли разработчик сможет только после ревью SRE.
С какими сложностями столкнулись при переходе на Ansible и как
их решали
Когда работа началась, мы поняли, что ошиблись: задача оказалась
непростой. Проблем не возникло только с репозиторием, а в других
вопросах пришлось много исследовать и улучшать наработки.
WinRM или SSH
Первый сюрприз состоял в выборе типа подключения. В случае Windows
их два WinRM и SSH. Оказалось, что Ansible медленно работает через
WinRM. При этом
Ansible не рекомендует использовать OpenSSH из коробки для
Windows Server 2019. И мы нашли новое решение:
- Форкнули и переделали под себя
роль из Galaxy.
- Написали плейбук, в котором есть только вызов этой роли. Это
единственный плейбук, при котором идет подключение к хостам по
WinRM.
-
Стандартными средствами Prometheus Blackbox Exporter сделали
мониторинг порта 22/tcp и версии OpenSSH:
- alert: SSHPortDown
expr: probe_success{job=~".*-servers-ssh",instance!~".*domain..ru"}
== 0
for: 1d
annotations:
summary: "Cannot reach {{`{{ $labels.instance }}`}} with
SSH"
- Выбрали и настроили
LDAP-плагин для инвентаризации, чтобы не вписывать вручную все
Windows-серверы из домена в статическую инвентаризацию:
plugin: ldap_inventory
domain: 'ldaps://domain:636'
search_ou: "DC=domain,DC=ru"
ldap_filter:
"(&(objectCategory=computer)(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
validate_certs: False
exclude_hosts:
- db-
account_age: 15
fqdn_format: True
- Развернули везде OpenSSH с нужными ключами и убедились, что ни
одного алерта о недоступности Windows-серверов по SSH больше
нет.
- Чуть позже интегрировали установку OpenSSH в стандартный образ.
Наши образы готовятся с помощью Packer, который также умеет
вызывать Ansible:
"type": "shell-local",
"tempfile_extension": ".ps1",
"execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
"env_var_format": "$env:%s=\"%s\"; ",
"environment_vars": [
"packer_directory={{ pwd }}",
"ldap_machine_name={{user `ldap_machine_name`}}",
"ldap_username={{user `ldap_username`}}",
"ldap_password={{user `ldap_password`}}",
"ansible_playbooks={{user `ansible_playbooks`}}",
"github_token={{user `github_token`}}"
],
"script": "./scripts/run-ansiblewithdocker.ps1"
Рефакторинг
Когда мы переписывали код под Ansible, то периодически натыкались
на дублирование кода. Например, почти все конфигурации DSC
содержали установку
windows_exporter. Единственное, что отличалось это коллекторы,
которые экспортер должен был использовать:
Чтобы избавиться от дублированного кода, вынесли windows_exporter в
отдельную Ansible-роль, а параметры этой установки в переменные
групп хостов.
Second hop authentication
Наверное, second hop authentication самая распространенная
проблема, с которой сталкиваются те, кто начал использовать Ansible
под Windows:
- name: Custom modules loaded into module directory
win_copy:
src: '\\share\dsc\modules'
dest: 'C:\Program Files\WindowsPowerShell\Modules'
remote_src: yes
Такая конструкция вызывает ошибку Access Denied из-за того, что по
умолчанию делегировать учетные данные для авторизации на удаленном
ресурсе невозможно без дополнительных настроек. Обойти ошибку
помогает, например,
new_credentials. Но мы предпочли воспользоваться тем, что
Ansible умеет вызывать ресурсы DSC через модуль win_dsc. Вызываем
DSC-ресурс File, который по умолчанию выполняется под учетной
записью компьютера. Делегация Kerberos в этом случае не нужна:
- name: Custom modules loaded into module directory
win_dsc:
resource_name: File
SourcePath: '\\share\dsc\modules'
DestinationPath: 'C:\Program Files\WindowsPowerShell\Modules'
Type: Directory
Recurse: true
Force: true
MatchSource: true
При этом нет противоречия в том, чтобы отказываться от DSC, но
использовать его ресурсы, если они лучше решают задачу, чем модуль
Ansible. Главная цель прекратить использовать DSC-конфигурации,
потому что нас не устраивала именно экосистема DSC, а не сами
ресурсы. Например, если нужно создать виртуальный свитч Hyper-V, то
придется использовать ресурс DSC в Ansible пока нет средств по
управлению конфигурацией Hyper-V.
Сетевой дисконнект
Некоторые задачи вызывают отключение сети (дисконнект) на
конфигурируемых серверах. Например, создание виртуального свитча
Hyper-V из примера выше:
- name: External switch present
win_dsc:
resource_name: xVMSwitch
Ensure: 'Present'
Name: 'Virtual Network'
Type: 'External'
NetAdapterName: 'TEAM_LAN'
AllowManagementOS: True
Проблема в том, что в DSC такой вызов работает, а в Ansible
завершается с ошибкой, так как управляемый хост дисконнектнул. Это
происходит потому, что Windows всегда дисконнектит при создании
виртуального экстернал-свитча. Решение добавить к таску аргумент
async:
async: 10
Так Ansible отправляет таск на хост, ждет заданное время и только
потом запрашивает состояние.
Дрифт инфраструктуры
Когда мы стали переносить код, обнаружили дрифт конфигураций. Это
фактические различия между тем, что описано в коде, и реальной
конфигурацией сервера или ПО. Причина в том, что в некоторых
случаях DSC выполнял только часть работы, а остальное делали либо
скриптами, либо вручную по инструкции.
Чтобы облегчить работу с IaC, мы собрали все скрипты и документы и
сделали единые однозначные инструкции. Кроме этого, организовали
процесс так, чтобы никто не внес случайные изменения в Ansible. Мы
храним весь инфраструктурный код в GitHub, а задачи инженерам
ставим через GitHub Projects, поэтому у нас есть возможность
связывать изменения инфраструктурного кода (pull requests) с
задачами. Так мы можем посмотреть изменения по каждой выполненной
задаче. Если у задачи не будет изменений, то её не примут и вернут
на доработку.
Баги сбора фактов
В отличие от DSC, Ansible при запуске собирает
факты об управляемых хостах, чтобы у разработчика была
возможность определить поведение тасков в зависимости от состояния
хоста. При сборе фактов с Windows-хостов Ansible может выдавать
ошибку, из-за некорректного кода модуля. Чтобы её исправить,
нужно подключить коллекцию
ansible.windows.
[WARNING]: Error when collecting bios facts: New-Object :
Exception calling ".ctor" with "0" argument(s): "Index was out of
range. Must be non-negative and less than the size of the
collection. Parameter name: index" At line:2
char:21 + ... $bios = New-Object -TypeName
Пайплайн для Ansible перед запуском каждого плейбука проверяет
наличие файлов
requirements.yml со списком необходимых ролей и коллекций, а
затем устанавливает их. Туда мы и добавили коллекцию
ansible.windows.
Коллекции это новый концепт развития Ansible. Если раньше в
Galaxy распространялись только роли, то теперь там можно найти
подборки различных плагинов и модулей, плейбуков и ролей.
Тесты
Прежде чем передать IaC-инструментарий разработчикам, мы хотели
быть уверенными, что Ansible-код будет надежным и ничего не
сломает. В случае с DSC никаких специальных тестов не было, хотя
существует специальный
фреймворк
для этой задачи. Конфигурации обычно валидировались на
стейджинг-серверах, поломка которых не приводила к дефектам.
Для тестирования Ansible обычно используют инструмент
molecule
как обертку для запуска тестов. Это удобный инструмент для
Linux-ролей, но в случае с Windows есть проблема. Раньше molecule
умела поднимать инфраструктуру сама, но сейчас разработчики убрали
такую возможность. Теперь инфраструктура поднимается либо с помощью
molecule в Docker, либо вне molecule. Протестировать Windows-роли в
Docker чаще всего невозможно: Hyper-V и большинство других
Windows-фич в Docker-контейнере не установятся. Придется
разворачивать инфраструктуру под тесты вне molecule и использовать
delegated driver в molecule.
Эту задачу мы еще не решили, но нашли инструменты, которые
обнаружат самые очевидные ошибки:
Как устроена работа с Ansible
После того как мы внедрили Ansible и преодолели все сложности,
сформировался процесс действий инженеров и автоматических
запусков:
- Инженер пишет код роли и тесты к ней, если это роль для
Linux-серверов. Как только инженер решит, что роль готова, он
делает pull request в отдельный бранч в GitHub-репозитории,
созданном специально для роли.
- При создании pull request автоматически запускается воркфлоу
GitHub Actions, который выполняет синтаксическую проверку и линтинг
роли. Если это Linux-роль, то запускаются еще и тесты. Инженер
проверяет, что всё хорошо, и при необходимости исправляет.
- Другой инженер делает ревью кода из pull request. После того
как автор роли исправляет все замечания, код вливается в
мастер-бранч, а версия роли автоматически повышается.
- Теперь нужно развернуть новую версию роли. Версии перечислены в
специальных файлах requirements.yml, которые лежат в
GitHub-репозитории с плейбуками. Для каждого плейбука отдельный
такой файл. Автор роли изменяет версию в таком файле. Если нужно
развернуть роль на серверы, которых нет в инвентаризации Ansible,
автор дополняет инвентаризацию. Потом автор снова создает pull
request, но уже в репозиторий с плейбуками.
- После подтверждения pull request снова запускается GitHub
Actions, который создает новый релиз в Octopus Deploy. Роль готова
к развертыванию.
- Инженер заходит в Octopus Deploy и запускает развертывание.
Процесс развертывания позволяет инженеру ограничить теги и хосты, а
также переопределить переменные аналогично опциям команды
ansible-playbook: --tags, --limit и --extra-vars.
- Процесс развертывания сначала запускает режим проверки, который
показывает, какие изменения будут сделаны. Инженер оценивает
результат проверки и либо подтверждает развертывание кода на
целевую инфраструктуру, либо сначала устраняет обнаруженные
недостатки.
Организация работы с Ansible
Что выбрать: DSC или Ansible
Евгений Берендяев, SRE-инженер