История одного конфигурационного «зонтика»
В этой статье я расскажу об опыте поставки и конфигурирования многочисленных взаимосвязанных продуктов (непричастные называют это просто «созданием инсталляторов»).
Итак, представьте разбросанный по нескольким серверам набор приложений: 12 штук. Приложения разнородные, разработаны разными компаниями в разное время. Реализованы различные методы интеграции от примитивных экспортов/импортов через файловую систему, до продвинутой на базе web-сервисов. Приложения используют базы данных SQL Server и Oracle. Все эти приложения работают в сотнях разных окружений конечных заказчиков.
Установить и сконфигурировать все это стоит немалых трудов и ресурсов. Хорошо, установили. А обновления, которые для бухгалтерских систем должны выпускаться в соответствии с изменениями в законодательстве? Масштабирование приложений? А новый функционал? Что насчет новых клиентов с их «особенными» конфигурациями? Вручную — дорого и долго, если у вас, скажем, 150 клиентов и хотя бы 2 обновления такого «монстра» в год.
Наш ответ на все эти вопросы — создание инсталляционного «зонтика» — бутстрапера и причесывания всего, что под ним. Многие заказчики теперь сами могут установить или обновиться без нашей помощи и дополнительной оплаты услуг консультантов компании. Количество клиентов растет, а количество работы для поддержки уменьшается.
Тут, собственно, про наш «зонтик». Это «велосипед»? Да, в какой-то мере. Однако, явно с электроприводом и встроенной GPS навигацией. Хотя, может быть, не последней версии.
Это идеальное решение? Нет, но оно работает и приносит реальные плоды от своего внедрения. Текущие продукты невозможно эффективно поставлять без него.
А, простите, «зонтик». Тогда вот так:)
Зачем все это нужно?
Упаковкой всех этих приложений уже давно занималась выделенная команда, используя InstallShield. Нужно было поставлять инсталляционные пакеты в различной конфигурации, настраивая их под специфические требования и окружения клиентов, поддерживать различные варианты масштабирования.
В результате, бывало и так…
Приходит, значит, задание на упаковку. Первый вопрос тут же:
— А какой деадлайн для этой задачи?
Ответ:
— Позавчера!
Как-то сразу все архитектурные изыски испаряются из головы в такой ситуации. Нет? То есть все должно уже быть готово. Как у хирурга для срочной операции — инструменты на столе и в наличии. В таком случае виноват в срыве сроков оказывается «плохой» пекеджинг, как последнее звено. Иногда это прямо как граната без чеки — передай следующему.
Бизнес-ценность пекеджинга, так скажем, не на первом месте — «уж как-нибудь заверните, чтобы работало да и все». И не совсем очевидно, что из-за отсутствия какой-то новой фичи пострадает ну может быть пара клиентов, а без надлежащей упаковки не получат продукт все. Ну, а если инсталлятор попортит окружения у огромного количества клиентов?
В результате — каждый из проектов разработки приложений жил своей жизнью и нечто интегрированное вырастало только в процессе упаковки всего решения.
Что же нас не устраивало?
- Скорость и предсказуемость сроков поставки решений. Независимо от того насколько квалифицирована и велика команда, переписать половину приложения за неделю невозможно.
- Большой бюджет пекеджинга не устраивал заказчика.
- Необходимость в дорогостоящем лицензировании и специализации сотрудников в данной области.
- Процесс разработки инсталляторов не был достаточно прозрачным и понятным заказчику.
Что же получилось и как оно работает?
Само решение
С точки зрения пользователя это папка с исполняемыми файлами «движка» зонтика, несколькими XML «матрицами» в виде XML, утилитами для конфигурации и диагностики, среди них и знаменитый setup.exe.
Такова иерархия директорий поставляемого конечному пользователю продукта.
Взгляд со стороны пользователя
Пользователь запускает знаменитый setup.exe — наш продукт. Отображается пользовательский интерфейс в корпоративном стиле компании. Происходит выбор комплекта установки. А вот, собственно, где работает наша матрица — система сама просчитывает зависимости: в случае если для установки какого-то продукта требуется другой, она добавляет его как обязательный.
В соответствии с выбором продуктов пользователем предлагается ввести то или иное количество параметров установки в пользовательском интерфейсе. Причем, если группе пакетов требуется один и тот же параметр, все это автоматически группируется в один элемент UI. Если же пользователь не выбрал продукт для этой настройки — она также не отображается.
В процессе установки мы генерируем командный файл с параметризацией каждого пакета в ProgramData на входе, потом после инсталляции получается 2 лога — один от зонтика в XML, второй от каждого инсталлятора. Причем в первом есть ссылки для удобства диагностики. Есть и специальная утилита для просмотра.
А если пакет свалился? Copy-paste в отдельный .cmd из сгенерированного командного файла и комфортная отладка прямо на окружении без запуска «зонтика», но с его параметризацией. Очень удобно прицепиться к процессу .NET Custom Action для отладки. Рай в сравнении с VBS. Тем более что наш фреймворк при изменении системной переменной окружения все сообщения от Custom Actions MSI выдает в message box, цепляйся — не хочу. ;)
И сразу вопрос «Что насчет апгрейда?»
Мы пишем в реестр, причем в единую системную область для наших продуктов. Так и определяем параметры установленных приложений, мигрируем настройки оттуда, в случае если не поддерживается ручное изменение параметров. В ином случае читаем из конфигов или мержим их.
Используем MSI только в режиме Major Upgrade. Патчи не прижились, хотя такая возможность в системе существует. Однако сейчас размер пакета не стал столь критичен, а при грамотной его разработке и тестировании — полноценный upgrade также не вызовет проблем.
Со стороны разработчика
Сейчас практика такова, что мы стартуем пекеджинг как можно раньше и сразу же его автоматизируем. Даже если в новом приложении всего одна страница, его уже можно установить!
Процесс не слишком сложный, описан в нескольких пунктах ниже.
- Разработчик заходит на портал, скачивает инструментарий, по документации смотрит не забыл ли он что. Заполняет спецификацию по пекеджингу, если делает не сам.
- Убеждается что собрать проект может и все файлы на месте.
- Настраивает инструментарий в соответствии с требуемой конфигурацией (к примеру, в зависимости о типа приложения — клиент, веб сервер или просто база данных) и генерирует темплейт инсталлятор.
- Собирает инсталлятор, тестирует, автоматизирует в соответствии с примерами.
- Заходит на портал «зонтика», импортирует из пакета параметры командной строки и привязывает их к пользовательскому интерфейсу.
- Экспортирует матрицу для тестирования или релиза на носитель с движком нашей оболочки и зависимыми продуктами.
- Производит установку.
Есть возможность автоматической установки, делается через Silent режим. Запустить удаленно или по событию можно через Windows Scheduler или PowerShell. Теперь сразу понятно, где задерживается разработка: у команды приложения или пекеджинга :)
История создания
В начале очень важным было найти успешные примеры, на которые можно было ориентироваться. На тот момент их было немного, а гибкие решения фактически отсутствовали.
Посмотрели на бутстрапер VS, даже списались с Microsoft, он оказался недостаточно гибким. Да и область применения другая. Планов выхода на рынок с чем-то, ориентированным на клиента, у них не было. Немного удивило, что общих практик в этой области у них не оказалось, но мы решили, что у богатых свои причуды. Мы пойдем другим путем.
Также общались с Flexera (в то время называлась Acresso Software) — спрашивали, смогут ли они предложить что-то готовое в разумные сроки. Был начат большой thread в форуме с пожеланиями к продукту и состоялась продолжительная, представительная беседа с менеджментом компании. Там мы и описали свои требования к продукту и то как видим окончательное решение. Сроков от них так и не получили. Года через три появился Flexera InstallShield Suite, поставляемый сейчас с InstallShield Premier, в котором угадываются многие наши идеи :)
Прототипирование
В начале мы сделали пару прототипов, которые позволили понять бюджет и технические аспекты решения.
Очень удачной идей оказалось хранение матрицы зависимостей приложений в XML, который бы поставлялась с продуктом.
Простота и совместимость WIX Deployment Tools Foundation позволила без проблем интегрировать .NET код в MSI. Раньше, даже если и удавалось дернуть .NET метод из InstallShield, то проблема с ссылками на библиотеки портила жизнь. Собственно, то как использовать в процессе установки функционал библиотек не из Global Assembly Cache — загадка (есть несколько вариантов, но все они достаточно трудоемки), да и неудобно. В DTF же просто контейнер, куда все, на что ссылается библиотека попадает автоматически. Никакой «ручной» работы.
Критической для перехода с VBScript стала функциональность по переупаковке Silverlight XAP файла для изменения конфигурации внутри, что используется и по сей день. Дальше .NET Custom Actions стали использовать везде.
От пользовательского интерфейса MSI при установке комплекта приложений предложено было отказаться: одно отсутствие события на изменение содержимого текст бокса чего стоит. Воз, кстати, и поныне там.
Для раздельной установки приложения UI Windows Installer был сохранен, но жестко стандартизирован. В результате немного погорячились с пользовательским интерфейсом в одном из первых прототипов, ибо он тоже предполагался в XML. Мысль была прекрасная, через год реализована Microsoft в WPF, собственно посему признана в результате неудачной и заменена на него.
Договорились, что за образец возьмем установщик Microsoft Office. Именно команда Office, насколько помню, разработала Windows Installer — нет повода им не доверять. Дружелюбность его установки на самом высоком уровне — можно просто прощелкать по Next и получить работоспособный продукт. Того же хотели и мы.
Прототип 1
Просто бутстрапер, с конфигурируемыми параметрами для запуска MSI. InstallShield для инсталляторов.
Оказалось, недостаточно — от бардака с зависимостями не спасет, да и интерфейс под все случаи не написать.
Прототип 2
.NET, XML везде. WIX в качестве движка для инсталляторов.
Оказалось, слишком много кода (в частности, описывающего сами пакеты), достаточно сложная структура у XML проектов WIX. Движок XML GUI тоже был достаточно объемным. В процессе использования выяснилось, что диагностика ошибок и поддержка решения слишком трудоемка. Быстро источник проблемы не локализовать и не поправить.
Прототип 3
База данных и экспорт из DataSet в XML для бутстрапера, UI на WPF, собственный инструментарий для создания пакетов инсталляции.
Данное решение уже было жизнеспособным — на нем решено было остановиться.
Нереализованные идеи
Несколько идей так и не были опробованы:
- Сервис аналогичный Windows Installer для поддержки комплектов продуктов.
- Компиляция набора продуктов в большой MSI пакет в соответствии с выбором пользователя.
Редактирование матрицы зависимостей
Вернемся к нашим инсталляторам. С XML матрицей все было хорошо, за исключением того, что работать с данными нескольким пользователям одновременно практически нереально. Ошибки при вводе, валидация зависимостей — нужен редактор.
Пришли к идее хранить и редактировать матрицу в SQL Server базе и экспортировать ее в XML, который поставляется с инсталлятором.
По результатам — серьезно пострадала «читабельность» и «модифицируемость» XML файла, выросла зависимость от редактора. Однако, нашлись специалисты, которые умудрялись и это редактировать вручную, если возникнет соответствующая ситуация. Но туда теперь обычно «ручками» лезут только для экспериментов.
Какие появились варианты на современном рынке
Мы, конечно, смотрим, что новенького появляется на рынке. Если увидим решение, покрывающее наши требования, можем и переключиться на него.
Вот некоторое сравнение нашего решения с другими доступными на рынке.
Характеристика |
InstallShield Suite |
WIX Burn |
Наш продукт |
Проверка зависимостей продукта |
Нет |
Нет |
Есть |
Централизованный многопользовательский редактор |
Нет |
Нет |
Есть |
Хранение данных о параметрах установки компонентов |
XML |
XML |
SQL Server, XML |
Пользовательский интерфейс для разработчика |
InstallShield |
XML |
Silverlight Client |
Динамически изменяемый пользовательский интерфейс |
Нет |
Нет |
Да |
Наличие интерфейса для автоматизации |
Да, начиная с версии 2015 |
Нет, можно реализовать на базе XML |
Да, WCF |
Интерфейс для расширений |
C++ plugins |
.NET |
.NET |
Работа с БД приложений |
Нет |
Нет |
SQL Server |
Предполагаемый интегрируемый инсталлятор продукта |
InstallShield |
WIX |
InstallShield + WIX DTF |
Встроенная конфигурация Active Directory |
Нет |
Нет |
Есть |
Наше решение в деталях
Был принят на вооружение C#, MVVM и SQL Server — стандартный инструментарий, не то что VBScript.
InstallShield — редактор MSI проектов
Плюсы:
- Быстрая диагностика и исправление проблем, все сразу видно в полнофункциональном GUI.
- Возможность постепенного перехода.
- Заказчик уже широко использует данный пакет.
- Стабильность — можно успеть реализовать свой продукт на базе этого решения.
Минусы:
- Огромная стоимость лицензий: $2800 разово с ежегодным продлением для получения обновлений — еще $480 регулярно. Некоторая экономия достигнута на использовании Professional версии, так как Suite «зонтик» у нас свой. Насколько знаю, у Microsoft экономия достигается специальными предложениями, здесь же за много лет лояльности особых скидок добиться не удалось.
- Не весь функционал работает правильно, иногда работает намеренно неправильно для сохранения совместимости
C# — основной язык программирования («внутри» и снаружи MSI, в «зонтике»)
Плюсы:
- Повысилась скорость разработки и гибкость решения.
Минусы:
- Зависимость от .NET (в нашем случае это не критично, так как почти все приложения итак его используют).
WPF — UI бутстрапера
Плюсы:
- Решение проще, легче поддерживать.
- Автоматический binding, легко реализуется «гибкий» пользовательский интерфейс.
Минусы:
- Меньше возможностей для настройки. К примеру, отказались от хранения описания UI в БД.
SQL Server — хранилище матрицы зависимостей
Плюсы:
- Доступ к информации нескольким пользователям.
- Решение в перспективе позволяет заместить информационную систему заказчика на базе SharePoint.
- Более надежное хранение и валидация информации.
Минусы:
- Необходимость поддержки централизованного сервера.
- БОльшие затраты на разработку.
Silverlight — UI редактора продуктов и их зависимостей
Плюсы:
- Широкие возможности для настройки пользовательского интерфейса и контроля ввода пользователя.
- Многопользовательский режим.
Минусы:
- Зависимость от плагина, сложности с переводом приложения на более актуальные версии.
- Сейчас решение уже морально устарело в связи с прекращением поддержки Silverlight.
Все это, конечно же, постараемся рассмотреть в деталях, если будет такой интерес у читателей.
Инсталляторы — до и после зонтика
Собственно, все вышеперечисленное затевалось ради установки и конфигурирования конечных продуктов. Так что поговорим об инсталляторах продуктов.
Ошибка №1 которая была сделана — мы начали писать «зонтик» до того, как полностью устаканилась разработка пекеджей. Из-за этого появились избыточные требования к продукту. К примеру, было реализовано несколько типов зависимостей, из которых реально используется только два. Статья не пойдет этим порочным путем!
Не все любят MSI. И у нас всегда был MSI, и даже не всегда InstallShield, и даже не всегда WIX в остальных случаях. В общем порядочная солянка, наваристая такая…
Состав: WIX, InstallShield, Wise, InstallScript, VBScript, .NET
Cначала о том, почему это плохо — разношерстные пакеты. И почему это непременно вас выбесит, если начнете ставить это на поток.
Вот что может быть…
И все бы хорошо, но у каждого — свои стандарты и ограничения. Это выливается в полный хаос с точки зрения функциональности и параметризации. А если инсталлятор вообще не на базе MSI то и популярный редактор Orca из Windows SDK не поможет выискать тот самый заветный параметр. Кроме того, WISE, к примеру, больше не поддерживается.
Типичные варианты
- Инсталлятор «все в одном» на базе InstallScript, как вероятная интерпретация Wise или на базе любого другого скриптового движка. Работает только с GUI и пытается установить все связанные компоненты перед установкой продукта. Как правило куча кода внутри, совершенно непредсказуемая параметризация и поддержка «silent» режима. Удаление обычно грязное, реализуется по остаточному принципу. Апгрейд — точно так же. Это переписываем.
- MSI с вкраплениями InstallScript и VBS. Могут быть созданы различными средами разработки от InstallShield до WIX. Сильная сторона — не зависят от наличия .NET. Но у кого сейчас Windows без него? Из недостатков — сложность в разработке и поддержке. Неуклюжий debug. Параметризация хаотичная. Обычно silent режим как-то работает. Это оставляем, по возможности, как есть. Или переписываем, если что-то не устраивает.
- Хороший вариант. MSI на WIX или InstallShield с WIX DTF. Но бардак с параметрами может быть. Приводим к общему знаменателю.
В результате оставили InstallShield на базе нашего генерируемого темплейта и WIX инсталляторы. И то и другое использует WIX DTF с нашими обертками. От setup.exe контейнера отказались совсем, надо иметь возможность посмотреть, что внутри.
Параметризация
Если в .NET у нас настоящий ООП (интерфейсы, классы, которые мы можем переопределять, видоизменять и наследовать), то в MSI нет. Если нет, значит надо что-то придумать — у нас это префиксы и соглашения по именованию. Если сейчас в управляемых языках это считается признаком плохого тона (но грешим, грешим еще в пользовательском интерфейсе почему-то), то для Windows Installer — в самый раз.
То есть так мы называем свойства (CIF — сокращенно Common Installation Framework) MSI, в просторечии «property».
Если в пакете присутствуют наши стандартные свойства, то инсталлятор их обрабатывает и поддерживает, в данном случае, конфигурирование Active Directory:
CIF_AD_USER
CIF_AD_PASSWORD
Нестандартные свойства (например, если у приложения несколько пользователей), конфигурируются отдельными полями ввода, либо переопределяются вручную в расширенных настройках:
APP_AD_USER
APP_AD_PASSWORD
Почему верхний регистр? Для тех, что используются внутри MSI мы допускаем использование обоих регистров. Для всех внешних интерфейсов — только верхний. Почему? InstallShield не везде чувствителен к регистру (system search, например).
Избыточные параметры не вызовут проблем, если MSI их не использует — просто проигнорирует. Поэтому стандартный набор параметров всегда передается, что упрощает добавление нового продукта и минимизирует необходимое количество документации или сопутствующего общения.
Как передаем параметры инсталляторам? Рассматривалось 2 варианта:
- Через командную строку
- Через трансформы
Выбрали 1-ый вариант. Причина? Банально проще. Да и командный файл легче редактировать. Лучше использовать один, наиболее универсальный, подход. Ограничения по длине строки мы не достигли.
Custom actions, лог и обработка исключений
С входными параметрами разобрались. А что на счет логов? У InstallScript он вообще бинарный. Как понять, что установка не свалилась в процессе? Коды ошибок могут быть разные. Как понять, что пошло не так в процессе выполнения того самого VBS CA написанного 3 года назад?
Нужна стандартизация:
- Необходимость поддержки стандартного набора параметров для установки и обновления продукта.
- Диагностика и обработка ошибок в процессе установки.
- Постановка процесса упаковки на поток.
- Дополнение встроенных конфигурационных средств InstallShield по работе с XML, IIS и пр. Уход от «мигающих черных окошек».
Как правило хорошего тона — рекомендация использовать WIX Deployment Tools Foundation для InstallShield. Тогда это было не столь очевидно.
Чего нам в InstallShield Professional не хватало — так это всевозможных настроек IIS и порядка их применения во время апгрейда. Как InstallShield работает с XML нам тоже не нравилось (много приложений не работало из-за того, что криво закрывались элементы, изменялся их порядок) — выросла функциональность по переформатированию и работе с фрагментами XML. Сейчас вообще переходим на text file changes и собственный XML движок.
Такая вот архитектура получилась в результате.
На рисунке:
- Зеленые — реализованы на базе паттернов, реализованных как темплейты и сниппеты на базе VS SDK.
- Оранжевые — переиспользуемый код в рамках MSI.
- Красные — переиспользуемый код за пределами MSI.
Чем хорош WIX .NET custom action — интерфейсом MSI DLL. Тесть в результате сборки автоматически формируется zip контейнер со всеми связанными библиотеками: если переименовать в ZIP, можно увидеть содержимое, запакованные библиотечки и т.д.
Логика и зависимости при этом хранятся отдельно. Как результат — библиотека может быть переиспользована как из PowerShell, MS Build, так и из нашего конфигурационного «зонтика». Не говоря о том, что, если кто-то захочет сконфигурировать что-то прямо из приложения.
Как мы можем отладить инсталлятор? Очень просто — ставим CIF_DEBUG системную переменную окружения в 1 и каждый наш CA будет писать не в лог уже, а в Message Box. Тут куча вариантов подцепиться и посмотреть изнутри что же происходит.
Как это сделано? Все сервисные функции MSI у нас выполняет специальная Core библиотека, которая служит уровнем абстракции над Microsoft Deployment Tools Foundation. Несложно перекинуть вывод на GUI.
Еще достаточно просто сделать exe из custom action — разница только в конструкторе, и вывод уже идет в консоль! Это на случай «быстро проверить на окружении».
.NET Custom Actions
У нас много разных .NET Custom Actions, мы используем вот такое соглашение об именовании:
[Subject][Action][Execution Type]([Parametrization Type])
Пример:
XmlMergeNodesDef — deferred execution in system context custom action, мержит ноды в конфигурационных файлах
MsiParseStringImOut — immediate execution, генерирует несколько новых свойств на выходе, посему тип «out».
Что мы еще не решили полностью, так это не отказались от кучи действий, передающих параметры «deferred in system context» custom actions. Они еще есть и у них расширение »_SetCAProp»
Пример:
CIF_XmlMergeNodesDef_SetCAProp — передает параметры в одноименный deferred custom action.
Как мы решили проблему с передачей параметров? В темплейте всем заведует единый .NET CA TemplatePrepare. Он генерируется автоматически нашим инструментарием по заданным параметрам.
Мы используем using конструкцию которая объявляет начало и конец кода custom action после стандартной WIX обвязки. Открывающая скобка — автоматически постит приветствие в лог с версией библиотеки, закрывающая прощается. У каждой строки стандартный префикс с именем custom action и таймстампом, вот например:
СIF CA NetServicesStopIm 06:27:56.1707871: Starts with CIF Core 3.7.0.15 → Windows Installer Log, parameters in CustomActionData
CIF CA NetServicesStopIm 06:27:56.1707871: SplitData splitting property CustomActionData
CIF CA NetServicesStopIm 06:27:56.1707871: SplitData found 1 parameter values in » with delimiter »|»
CIF CA NetServicesStopIm 06:27:56.1863471: Searching for property references in «AppFabricEventCollectionService|AppFabricWorkflowManagementService|AppFabricCachingService|»…
CIF CA NetServicesStopIm 06:27:56.1863471: Searching for property references in »…
CIF CA NetServicesStopIm 06:27:56.1863471: No properties detected.
CIF CA NetServicesStopIm 06:27:56.1863471: Get property AppFabricEventCollectionService|AppFabricWorkflowManagementService|AppFabricCachingService
CIF CA NetServicesStopIm 06:27:56.1863471: Searching for property references in »8»…
CIF CA NetServicesStopIm 06:27:56.1863471: No properties detected.
CIF CA NetServicesStopIm 06:27:56.1863471: Get property InstanceId = 8
CIF CA NetServicesStopIm 06:27:56.1863471: {InstanceId} replaced with »8»
CIF CA NetServicesStopIm 06:27:56.2488510: Ends
Заметим, читать это очень просто — для скролла просто жмем на «Найти следующее вхождение». По таймстампу с миллисекундами можно посмотреть насколько то или иное действие отнимает много времени (бывает из-за неверного параметра обрабатывается что-то слишком объемное на уровне выше, к примеру).
Заключение, выводы, достоинства и недостатки решения
Как результат — сейчас люди на проектах разработки приложений занимаются этими проектами. А люди, занимающиеся конфигурацией, собственно, ей и занимаются.
Если что-то новое нужно сконфигурить, то имеется процесс от создания user story в бэклоге до получения результата в виде новой библиотеки, описанной в документации с примерами.
Минусы:
- Себестоимость разработки и поддержки решения
- Более длительный процесс поставки решения в сравнении с чисто облачными вариантами
Плюсы:
- Возможность продажи продуктов для установки на окружении заказчикам
- Полный контроль над конфигурацией приложения
- Возможность поставки различных вариантов «наборов» продукции
- Более качественное тестирование продукта
- Быстрое внесение изменений в продукт и диагностика причин неправильной конфигурации
- Уменьшение количества инцидентов, связанных с конфигурацией, как результат ниже затраты на их поддержку и выше уровень удовлетворённости продуктом
В целом, цель была достигнута, хоть решение созрело чуть позже, чем того хотелось бы. Так что желаем вам ясной погоды и не попадать под дождь, ну, а если случится, то быть готовым и с зонтиком! :)
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.