На одной из встреч петербургского .
Net-сообщества разработчиков SpbDotNet Community мы пошли на эксперимент и решили рассказать о том, как можно применять подходы, давно ставшие стандартными в мире Linux, для автоматизации инфраструктуры Windows. Но чтобы не доводить все до голословного размахивания флагом Ansible, было решено показать это на примере развертывания приложения ASP.Net. Спикером вызвался Алексей Чернов, старший разработчик команды, разрабатывающей библиотеки UI-компонентов для наших проектов.
И да, вы так не думали: разработчик JavaScript отправился выступить перед аудиторией .
Net. Всем, кому интересен исход такого эксперимента, добро пожаловать под кат на расшифровку.
Привет) Тут уже немного нагадили и сказали, что я фронтенд-разработчик, так что можете уже уходить =) Меня зовут Алексей, я довольно давно занимаюсь всякими вещами в области веб-разработки.
время.
Я начал с Perl, потом был PHP, немного RoR, немного того, немного этого.
А потом в мою жизнь ворвался JavaScript, и с тех пор это практически все, чем я занимался.
Помимо JS, в последнее время я пишу довольно много автотестов (и использую тот же JS), в связи с чем мне приходится автоматизировать развертывание тестовых стендов и инфраструктуры для них.
Фон Два года назад я попал в Veeam, где разрабатывают продукты для Windows. В тот момент я очень удивился, но оказалось, что так бывает =).
Но больше всего меня удивил непривычно низкий уровень автоматизации всего, что касается развертывания, развертывания приложений, тестирования и т. д. Мы, те, кто занимается разработкой под Linux, давно привыкли, что все должно быть в Docker, есть Kubernetes и все можно развернуть в один клик.
И когда я оказался в среде, где всего этого не хватало, это было шокирующе.
И когда я начал делать автотесты, я понял, что это только 20% успеха, а остальное — подготовка к ним инфраструктуры.
Мои ощущения в начале Текущие условия Расскажу немного о том, как у нас все работает, что нам приходится автоматизировать и что мы делаем.
У нас есть куча разных продуктов, большинство для Windows, несколько для Linux и даже кое-что для Solaris. Каждый день собирается довольно много билдов для всех продуктов.
Соответственно, все это нужно развертывать в тестовых лабораториях, как для QA, так и для самих разработчиков, чтобы они могли проверить интеграцию приложений.
Все это требует огромной инфраструктуры из множества аппаратных серверов и виртуальных машин.
А иногда мы проводим тестирование производительности, когда нам нужно установить сразу тысячу виртуальных машин и посмотреть, насколько быстро будут работать наши приложения.
Проблемы Конечно, на первых этапах (читай, давно), при попытке автоматизировать все в лоб, использовался PowerShell. Инструмент мощный, но сценарии развертывания оказываются крайне сложными.
Другой проблемой было отсутствие централизованного управления этим процессом.
Какие-то скрипты запускались разработчиками локально, какие-то на виртуальных машинах, созданных ещё в эпоху мамонтов и т.д. Как итог: сложно было получить единый результат и понять, что работает, а что нет. Вы приходите на работу, открываете браузер — сервер недоступен.
Почему он был недоступен, что случилось, где сломался – совершенно непонятно.
Единой точки входа не было, и приходилось искать правду в рабочих чатах, и хорошо, если кто-то отвечал.
Другая проблема, не столь очевидная, — это новички.
Им было трудно.
Первую неделю работы они просто вникали в происходящее.
Мы по привычке жили с этим, успокаивая себя тем, что жизнь — штука сложная и с этим надо смириться.
Понять и простить, так сказать.
Но в какой-то момент мы нашли в себе внутренние силы преодолеть это и осмотреться.
Наверное, с этим можно как-то справиться.
Первый шаг к решению проблемы – ее принятие Выбор решения Когда вы не знаете, что делать, посмотрите, что делают другие.
И для начала мы составили свой список требований к тому, что мы хотим получить в итоге.
- Единая кодовая база.
Все сценарии развертывания должны находиться в одном месте.
Если вы хотите что-то развернуть или посмотреть, как это разворачивается: вот репозиторий, идите туда.
- Все знают, как это работает. Вопросы типа «Я не понимаю, как это развернуть, поэтому уже второй день не могу закрыть баг» должны исчезнуть.
- Возможность запуска с кнопки.
Нам нужно иметь возможность контролировать развертывание.
Например, какой-то веб-интерфейс, куда заходишь, нажимаешь кнопку, и нужный продукт разворачивается на нужном хосте.
Традиционно первым шагом было решение проблем с помощью лобовой атаки.
Много ли у нас скриптов PowerShell? Итак, давайте объединим их в один репозиторий.
Но проблема не в том, что скриптов было слишком много, а в том, что разные команды делали одно и то же с разными скриптами.
Я посещал разные команды, выслушивал их требования, собирал одинаковые скрипты, пытался их как-то более-менее причесать и параметризовать, а затем помещать в единый репозиторий.
Неудача: Попытка не удалась.
Во-первых, мы стали много спорить о том, почему мы делаем то, а не то.
Почему был использован именно этот метод, а не какой-то другой и т. д. И в результате нашлось много желающих переделать все «как надо», по принципу «сейчас форкну и все за тебя перепишу».
И, конечно же, при таком подходе объединить ветки не получится.
Попытка номер два: мы должны были взять наш CI-сервер (TeamCity), сделать на нем несколько шаблонов и с помощью наследования решить основную проблему с первой попытки.
Но, как вы могли сразу догадаться, был еще Неудача: Вы можете использовать только последнюю версию шаблона, а значит, требуемого версионирования мы не достигнем.
А в результате большого количества команд шаблонов стало очень много, управлять ими становилось все сложнее, а на горизонте отчетливо проглядывало новое болото.
Устав падать лицом на землю каждый раз, когда я пытался взлететь, было решено снова сесть и хорошенько подумать.
Итак, у нас есть большая куча PS-скриптов с одной стороны и огромное количество виртуальных машин с другой.
Но мы ошибались, потому что.
Корень проблемы был вовсе не в этом.
Проблема была в том, что между этими вещами всегда находился человек.
Неважно, разработчик ли это, тестировщик или кто-то другой, в моей голове всегда происходила примерно такая логическая цепочка:
- Итак, мне нужна виртуальная машина для тестов
- Да, вот у нас есть пул хостов
- Но вот скрипт мне нужен, я его сейчас запущу и все произойдет
Оказалось, что вся наша боль из-за отсутствия единого описания нашей инфраструктуры.
Оно было в головах людей, создавших его; они сидели в разных ведомствах, никак не стремились это документировать, и вообще каждый жил в своем отдельном государстве.
В этот момент мы пришли к выводу, что ответом на все наши проблемы является: Инфраструктура как код Это именно то, что вся наша инфраструктура должна быть описана в коде и храниться в репозитории.
Все виртуальные машины, все их параметры, все, что там установлено — все должно быть описано в коде.
Возникает закономерный вопрос – почему? Отвечаем: такой подход даст нам возможность применить лучшие практики из мира разработки, к которым мы все так привыкли:
- Контроль версий.
Мы всегда сможем понять, что и когда изменилось.
Больше никаких хостов, появляющихся из ниоткуда или исчезающих в никуда.
Всегда будет понятно, кто внес изменения.
- Обзор кода.
Мы сможем контролировать процессы развертывания, чтобы одни не ущемляли другие.
- Непрерывная интеграция.
Мы выбрали Ansible, потому что он содержит набор необходимых нам функций.
В первую очередь мы хотим от системы автоматизации, чтобы не запускались какие-то инсталляторы, что-то куда-то мигрировало и т.п.
В первую очередь мы хотим от такой системы, чтобы после нажатия одной кнопки мы видели UI приложения.
нам нужно.
Поэтому ключевым признаком для нас является идемпотентность.
Ansible не волнует, что случилось с системой «до».
После запуска нужного playbook мы всегда получаем один и тот же результат. Это очень важно, когда вы говорите не «Установите IIS», а «IIS должен быть здесь», и вам не приходится думать о том, был он там раньше или нет. С помощью скриптов этого добиться очень сложно, но плейбуки Ansible делают это возможным.
Следует также отметить, что Ansible не имеет агентов.
Большинство систем автоматизации работают через агентов.
Это имеет много преимуществ — например, лучшую производительность — но для нас было важно, чтобы не было агента, чтобы систему не нужно было как-то подготавливать.
PowerShell:
Анзибль:$url = " http://buildserver/build.msi " $output = "$PSSscriptRoot\build.msi" Invoke-WebRequest -Uri $url -OutFile $output
name: Download build
hosts: all
tasks:
name: Download installer
win_get_url:
url: " http://buildserver/build.msi "
dest: "build.msi"
force: no
Здесь мы видим, что в базовом примере скрипт ps будет даже более кратким, чем плейбук Ansible. 3 строки сценария вместо 7 строк плейбука для загрузки файла.
Но, Петька, есть нюанс (с).
Как только мы хотим соблюсти принцип идемпотентности и, например, быть уверены, что файл на сервере не изменился и его не нужно скачивать, нам придется реализовать в скрипте HEAD-запрос, который добавляет около 200 строк.
А в плейбуке - один.
Модуль Ansible win_get_url, который выполняет всю проверку за вас, содержит 257 строк кода, которые вам не нужно вставлять в каждый скрипт. И это только один пример очень простой задачи.
А если вдуматься, то идемпотентность нужна везде:
- Проверьте существование виртуальной машины.
В случае со скриптами мы рискуем либо создать их бесконечное количество, либо скрипт упадет в самом начале.
- Какие пакеты MSI установлены на машине? В лучшем случае здесь ничего не упадет; в худшем случае машина перестанет работать адекватно.
- Нужно ли мне снова загружать артефакты сборки? Хорошо, если ваши сборки весят десятки мегабайт. Что делать тем, у кого есть пара гигабайт?
Помимо прочего, что важно для нас, Ansible не использует агентов для управления вашими хостами и машинами.
В Linux, естественно, он запускается по ssh, а в Windows используется WinRM. Отсюда очевидное следствие: Ansible является кроссплатформенным.
Он поддерживает фантастическое количество платформ, вплоть до сетевого оборудования.
И последнее, но не менее важное: формат YAML для записи конфигов.
Все привыкли, читается легко, и не сложно разобраться, что там происходит. Но не все так сладко, есть и проблемы:
- Проблема сомнительная: вам все равно понадобится машина с Linux для запуска плейбуков, даже если вся ваша инфраструктура исключительно Windows. Хотя в современном мире это не такая уж большая проблема, потому что… В Windows 10 теперь есть WSL, где можно запустить Ubuntu, под которой можно запускать плейбуки.
- Иногда плейбуки действительно сложно отладить.
Ansible написан на Python, и последнее, что вы хотите увидеть, — это лист трассировки стека Python для пяти экранов.
А виновником может быть опечатка в названии модуля
Из него будут запускаться плейбуки, и на нем происходит вся магия.
На эту машину нам понадобится:
- Python и менеджер пакетов Python pip. Во многих дистрибутивах он есть «из коробки», поэтому никаких сюрпризов здесь нет.
- Устанавливаем Ansible через pip, как самый универсальный способ: pip install ansible
- Добавьте модуль winrm для запуска на компьютерах с Windows: pip install pywinrm[credssp]
- А на машинах, которыми мы хотим управлять, нам нужно включить winrm, потому что.
по умолчанию он отключен.
Есть много способов сделать это, все они описаны в документации Ansible. Но самый простой — взять готовый скрипт из репозитория Ansible и запустить его с необходимой опцией авторизации: ConfigurationRemotingForAnsible.ps1 -EnableCredSSP
YAML-файл (в нашем случае), который описывает нашу инфраструктуру и куда вы всегда можете посмотреть, чтобы понять, где что развернуто.
И, конечно же, сами книги.
В дальнейшем работа выглядит как запуск плейбука с необходимым файлом инвентаря и дополнительными параметрами.
all:
children:
webservers:
hosts:
spbdotnet-test-host.dev.local:
dbservers:
hosts:
spbdotnet-test-host.dev.local:
vars:
ansible_connection: winrm
ansible_winrm_transport: credssp
ansible_winrm_server_cert_validation: ignore
ansible_user: administrator
ansible_password: 123qweASD
Здесь все просто: корневая группа — это all и две подгруппы, webserves и dbservers. Все остальное интуитивно понятно, но я лишь обращу ваше внимание на то, что Ansible по умолчанию считает, что Linux есть везде, поэтому для Windows необходимо указать winrm и тип авторизации.
Конечно, нет необходимости хранить пароль в плейбуке в открытом виде, вот лишь пример.
Хранить пароли можно, например, в Ansible-Vault. Для этого мы используем TeamCity, который передает секреты через переменные окружения и ничего не сжигает. Модули Все, что делает Ansible, он делает с использованием модулей.
Модули для Linux пишутся на Python, для Windows — на PowerShell. И дань идемпотентности: результат работы модуля всегда приходит в виде json-файла, в котором указано, были ли изменения на хосте или нет. В общем, мы запустим конструкцию, подобную списку модулей файла инвентаризации группы хостов ansible:
Учебники Playbook — это описание того, как и где мы будем выполнять модули Ansible. - name: Install AWS CLI
hosts: all
vars:
aws_cli_download_dir: c:\downloads
aws_cli_msi_url: https://s3.amazonaws.com/aws-cli/AWSCLI32PY3.msi
tasks:
- name: Ensure target directory exists
win_file:
path: "{{ aws_cli_download_dir }}"
state: directory
- name: Download installer
win_get_url:
url: "{{ aws_cli_msi_url }}"
dest: "{{ aws_cli_download_dir }}\\awscli.msi"
force: no
- name: Install AWS CLI
win_package:
path: "{{ aws_cli_download_dir }}\\awscli.msi"
state: present
В этом примере у нас есть три задачи.
Каждая задача представляет собой вызов модуля.
В этом плейбуке мы сначала создаем каталог (проверяем его существование), затем загружаем туда AWS CLI и устанавливаем его с помощью модуля win_packge. Запустив этот плейбук, мы получим следующий результат.
В отчете показано, что четыре задачи были успешно выполнены, а в трех из четырех внесены некоторые изменения на хосте.
Но что произойдет, если вы запустите эту книгу еще раз? Нигде не написано, что мы должны создать каталог, скачать файл установщика и запустить его.
Мы просто проверяем наличие каждого товара и пропускаем, если он доступен.
Это та самая идемпотентность, которой мы не смогли добиться с помощью PowerShell. Упражняться Это немного упрощенный пример, но в принципе именно это мы и делаем каждый день.
Мы развернем приложение, состоящее из службы Windows и веб-приложения под IIS. - name: Setup App
hosts: webservers
tasks:
- name: Install IIS
win_feature:
name:
- Web-Server
- Web-Common-Http
include_sub_features: True
include_management_tools: True
state: present
register: win_feature
- name: reboot if installing Web-Server feature requires it
win_reboot:
when: win_feature.reboot_required
Для начала нам нужно посмотреть, есть ли вообще на хосте IIS, и установить его, если нет. И было бы неплохо сразу добавить туда инструменты управления и все зависимые возможности.
И очень хорошо, если при необходимости хост будет перезагружен.
Первую проблему мы решаем с помощью модуля win_feature, который управляет функциями Windows. И здесь у нас впервые есть переменные среды Ansible, в параграфе реестра.
Помните, я говорил, что задачи всегда возвращают объект json? Теперь, после выполнения задачи Install IIS, переменная win_feature содержит выходные данные модуля win_feature (простите за тавтологию).
В следующей задаче мы вызываем модуль win_reboot. Но нам не нужно каждый раз перезагружать наш сервер.
Мы перезагрузим его только в том случае, если модуль win_feature вернет нам это требование в виде переменной.
Следующий шаг — установка SQL. Для этого уже придумано миллион способов.
Здесь я использую модуль win_chocolatey. Это менеджер пакетов для Windows. Да, именно то, к чему мы так привыкли в Linux. Модули поддерживаются сообществом, и сейчас их уже более шести тысяч.
Я очень рекомендую попробовать.
- name: SQL Server
hosts: dbservers
tasks:
- name: Install MS SQL Server 2014
win_chocolatey:
name: mssqlserver2014express
state: present
Итак, хост для запуска приложения мы подготовили, приступим к его развертыванию! - name: Deploy binaries
hosts: webservers
vars:
myapp_artifacts: files/MyAppService.zip
myapp_workdir: C:\myapp
tasks:
- name: Remove Service if exists
win_service:
name: MyAppService
state: absent
path: "{{ myapp_workdir }}\\MyAppService.exe"
На всякий случай первым делом удаляем существующий сервис.
- name: Delete old files
win_file:
path: "{{ myapp_workdir }}\\"
state: absent
- name: Copy artifacts to remote machine
win_copy:
src: "{{ myapp_artifacts }}"
dest: "{{ myapp_workdir }}\\"
- name: Unzip build artifacts
win_unzip:
src: "{{ myapp_workdir }}\\MyAppService.zip"
dest: "{{ myapp_workdir }}"
Следующий шаг — загрузка новых артефактов на хост. Этот плейбук предполагает, что он запущен на билд-сервере, все архивы расположены в известной папке, а путь к ним мы указываем с помощью переменных.
После копирования (win_copy) архивы распаковываются (win_unzip).
Дальше просто регистрируем сервис, говорим путь к exe и что его надо запустить.
- name: Register and start the service
win_service:
name: ReporterService
start_mode: auto
state: started
path: "{{ myapp_workdir }}\\MyAppService.exe"
Готовый!? Кажется, наш сервис готов к работе и обороне, однако есть одно «но» — мы не соблюдали принцип идемпотентности.
Мы всегда удаляем существующий код, а затем развертываем новый код. И в этом проблема.
Если мы удалили старый развернутый сервис, а потом произошла какая-то ошибка и плейбук не завершил свою работу, мы получим битый хост. Или, например, мы разворачиваем несколько приложений одновременно, одно из которых не изменилось, тогда его нам тоже развертывать не нужно.
Что может быть сделано? Альтернативно, вы можете проверить контрольную сумму наших артефактов и сравнить их с теми, что находятся на сервере.
- name: Get arifacts checksum
stat:
path: "{{ myapp_artifacts }}"
delegate_to: localhost
register: myapp_artifacts_stat
- name: Get remote artifacts checksum
win_stat:
path: "{{ myapp_workdir }}\\MyAppService.zip"
register: myapp_remote_artifacts_stat
Мы используем модуль stat, который предоставляет всю информацию о файлах, включая контрольную сумму.
Далее, используя уже знакомую директиву Register, записываем результат в переменную.
Что интересно: Deleate_to указывает, что это необходимо сделать на локальной машине, где запущен плейбук.
- name: Stop play if checksums match
meta: end_play
when:
- myapp_artifacts_stat.stat.checksum is defined
- myapp_remote_artifacts_stat.stat.checksum is defined
- myapp_artifacts_stat.stat.checksum == myapp_remote_artifacts_stat.stat.checksum
И с помощью метамодуля говорим, что нам нужно завершить выполнение плейбука, если контрольные суммы артефактов на локальной и удаленной машинах совпадают. Так мы соблюдаем принцип идемпотентности.
- name: Ensure that the WebApp application exists
win_iis_webapplication:
name: WebApp
physical_path: c:\webapp
site: Default Web Site
state: present
Теперь давайте посмотрим на наше веб-приложение.
Давайте пропустим часть о копировании файлов и перейдем сразу к делу.
Наш билд-сервер опубликовал его, все незакрепленные файлы загружаем на хост и используем встроенный модуль для работы с IIS-приложениями.
Он создаст приложение и запустит его.
Повторное использование кода Одной из задач, которые мы поставили, было дать возможность любому инженеру в компании легко запустить развертывание.
Он пишет свой playbook из готовых модулей, говорит, что ему нужно запустить такой-то продукт на таком-то хосте.
В Ansible для этого есть роли.
По сути, это соглашение.
Создаем на сервере папку /roles/ и помещаем в нее свои роли.
Каждая роль представляет собой набор файлов конфигурации: описание наших задач, переменных, служебных файлов и т. д. Обычно роль состоит из какой-то изолированной сущности.
Установка IIS — отличный пример, если нам нужно его не только установить, но и как-то дополнительно настроить или проверить дополнительными задачами.
Мы создаем отдельную роль и таким образом изолируем все книги игр, связанные с IIS, в папке ролей.
В дальнейшем мы просто вызываем эту роль с помощью директивы include_role %role_name%.
Естественно, мы создали роли для всех приложений, оставив инженерам возможность как-то настроить процесс с помощью параметров конфигурации.
- name: Run App
hosts: webservers
tasks:
- name: "Install IIS"
include_role:
name: IIS
- name: Run My App
include_role:
name: MyAppService
vars:
myapp_artifacts: .
/buld.zip
В этом примере роль «Запустить мое приложение» имеет возможность передавать артефактам часть собственного пути.
Здесь нужно сказать пару слов об Ansible Galaxy — репозитории общедоступных стандартных решений.
Как принято в приличном обществе, многие вопросы уже решены до нас.
И если у вас возникнет ощущение, что сейчас мы собираемся начать изобретать велосипед, то сначала вам нужно посмотреть список встроенных модулей, а затем уже углубляться в Ansible Galaxy. Вполне вероятно, что нужная вам книга уже создана кем-то другим.
Там огромное количество модулей, на все случаи жизни.
Больше гибкости Что делать, если в Галактике нет ни встроенного модуля, ни подходящей роли? Тут два варианта: либо мы что-то делаем не так, либо перед нами действительно уникальная задача.
В случае второго варианта мы всегда можем написать свой модуль.
Как я показывал вначале, Ansible позволяет написать простой модуль буквально за 10 минут, а когда вы углубитесь, вам на помощь приходит довольно подробная документация, освещающая многие вопросы.
КИ В нашем отделе мы очень любим TeamCity, но на ваш выбор может быть любой другой CI-сервер.
Почему нам нужно использовать их вместе? Во-первых, мы всегда можем проверить корректность синтаксиса наших плейбуков.
Хотя YAML считает табуляцию синтаксической ошибкой, это очень полезная функция.
Мы также запускаем ansible-lint на CI-сервере.
Это статический анализатор конфигов Ansible, выдающий список рекомендаций.
Например, здесь он говорит, что у нас в конце строки есть лишний пробел, и для одной задачи не дано имя.
Это важно, поскольку имя модуля может встречаться несколько раз в одном плейбуке, и все задачи должны быть названы.
Конечно, вы также можете писать тесты для плейбуков.
Мы можем себе позволить этого не делать, потому что.
Мы развертываемся на тестовой среде, и ничего критического не произойдет. Но если будете деплоить в продакшен, то лучше все проверить.
К счастью, ansible позволяет тестировать не только плейбуки, но и отдельные модули.
Так что обязательно обратите на него внимание.
И вторая основная причина использования CI-сервера — запуск плейбуков.
Это волшебная кнопка «Делать хорошо», которую дает нам TeamCity. Мы просто создаем несколько простых конфигураций для разных продуктов, где говорим: ansible-playbook reporter_vm.yml -i Inventory.yml -vvvv и получаем кнопку «Развернуть».
Бонусное удобство: на билдах можно строить зависимости.
Как только что-то пойдет не так, TeamCity запускает процесс передислокации, после чего мы можем посмотреть логи только в том случае, если вдруг что-то сломается.
Общий
- Мы заменили запутанные и разбросанные скрипты PowerShell на конфиги YAML.
- Мы заменили разные реализации одних и тех же задач общими ролями, которые можно использовать повторно.
Создан репозиторий, в котором расположены роли.
Если роль вам подходит, вы просто ее используете.
Если вас это не устраивает, вы просто отправляете pull request и вас это устраивает =)
- Теперь вы можете проверить успешность развертывания в одном месте.
- Где искать логи знают все
- Проблемы со связью также были решены за счет общего репозитория и TeamCity. Все заинтересованные люди знают, где находятся плейбуки и как они работают.
Теги: #программирование #ИТ-инфраструктура #Windows #Системное администрирование #.
NET #ansible #veeam
-
Лига Наций
19 Oct, 24 -
Сделайте Fallout Снова Великим
19 Oct, 24 -
"Я Не Понимаю"
19 Oct, 24 -
Мопед Не Мой, Я Просто Разместил Объявление
19 Oct, 24 -
Openoffice.org 2.2.0
19 Oct, 24