PowerShell 3.0 и Workflows: параллельное развёртывание патчей на 20 серверах без велосипедов
PowerShell 3.0 в Windows 8 и Server 2012 приносит Workflows и ForEach -Parallel. Пишем первый workflow для одновременного патчинга группы серверов и смотрим, что изменилось.
PowerShell 3.0 включён в Windows 8 и Windows Server 2012 RC: Workflows на базе WF4, Scheduled Jobs, улучшенный ISE с IntelliSense и автодополнением параметров
PowerShell 3.0 не нужно скачивать отдельно - он входит в Windows 8 и Windows Server 2012 RC, который мы уже тестируем на стендах. Новая версия бросается в глаза сразу: ISE с IntelliSense, автодополнение параметров прямо в консоли, нормальная справка через Update-Help. Но главное, что нас зацепило - Workflows.
Что такое Workflow и чем он отличается от обычного скрипта
Workflow в PowerShell 3.0 - это блок workflow { ... } вместо function { ... }, под капотом которого работает Windows Workflow Foundation 4. Это не просто синтаксический сахар. Из практически важного:
- Checkpoint'ы и возобновление. Workflow можно приостановить, сервер перезагрузить и продолжить с точки
Checkpoint-Workflow. Для патчинга с рестартами это не абстрактная фича. - Параллельное выполнение.
ForEach -Parallelи блокParallel { ... }позволяют гонять операции одновременно на нескольких целях без возни с runspace и job-ами вручную. - Встроенная поддержка PSRemoting. Параметр
-PSComputerNameпрямо в сигнатуре workflow - и он сам разворачивает выполнение по списку хостов.
Scheduled Jobs - тоже удобное добавление: задания через Register-ScheduledJob интегрированы с планировщиком Windows и возвращают нормальные объекты с историей запусков. Мы раньше лепили это через сочетание schtasks.exe и скриптов-обёрток - теперь проще.
Задача: патчинг группы серверов
У одного из клиентов - парк из двух десятков Windows-серверов разного возраста. WSUS есть, одобрение обновлений налажено, но сам процесс установки патчей до сих пор шёл последовательно: скрипт на PowerShell 2.0 обходил сервер за сервером, дожидался завершения установки, перезагрузки, проверки - и шёл к следующему. Окно обслуживания на 20 машин занимало около двух часов, притом что большую часть времени скрипт просто ждал.
Первая же мысль при взгляде на ForEach -Parallel: а что если запустить всё одновременно?
Workflow для параллельного патчинга
workflow Install-PatchesParallel {
param(
[string[]]$ComputerList,
[int]$ThrottleLimit = 5
)
ForEach -Parallel -ThrottleLimit $ThrottleLimit ($Computer in $ComputerList) {
sequence {
InlineScript {
$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$result = $searcher.Search("IsInstalled=0 and Type='Software'")
if ($result.Updates.Count -eq 0) {
Write-Output "[$using:Computer] Нет ожидающих обновлений"
return
}
$downloader = $session.CreateUpdateDownloader()
$downloader.Updates = $result.Updates
$downloader.Download()
$installer = $session.CreateUpdateInstaller()
$installer.Updates = $result.Updates
$installResult = $installer.Install()
Write-Output "[$using:Computer] Установлено: $($result.Updates.Count), код: $($installResult.ResultCode)"
} -PSComputerName $Computer -PSCredential $using:Credential
Checkpoint-Workflow
}
}
}
Несколько замечаний по тому, что получилось:
$using:Variable. В InlineScript переменные из внешнего scope передаются именно так - это специфика Workflows, к ней надо привыкнуть. Первые двадцать минут мы удивлялись, почему $Computer внутри InlineScript всегда пустой.
ThrottleLimit. Без ограничения параллельность упирается в возможности сети и удалённых хостов. 5 одновременных сессий на клиентском стенде показали себя адекватно, на 20 серверах пиков не создавали. Значение подбирается под конкретную инфраструктуру.
Checkpoint-Workflow после каждого хоста. Если что-то пошло не так на середине - workflow можно возобновить, не начиная всё заново. На практике пока не пригодилось, но это как ремень безопасности.
sequence внутри ForEach -Parallel. Шаги для каждого конкретного сервера должны идти последовательно - сначала скачать, потом установить. Без блока sequence Workflow может переставить операции, и это не баг, а намеренное поведение.
Что получилось по времени
Запустили на тестовом стенде из 12 виртуалок (все без обновлений за последние два месяца). С последовательным скриптом - около 80 минут. С ForEach -Parallel при ThrottleLimit = 5 - порядка 22 минут. Разница ощутимая, особенно с учётом того, что кода написали примерно столько же, просто в другом синтаксисе.
На реальных 20 серверах результат будет зависеть от объёма патчей и ширины канала до хостов. Но логика простая: если серверы патчатся примерно одинаково долго, параллельность даёт выигрыш пропорционально степени параллелизма - в разумных пределах ThrottleLimit.
ISE с IntelliSense
Отдельно стоит упомянуть новый ISE. Автодополнение параметров командлетов, подсказки по типам, подсветка переменных - это не революция, но работать приятнее. Раньше мы держали под рукой справочник по параметрам; теперь ISE подсказывает прямо во время набора. Workflow-синтаксис тоже подсвечивается корректно.
Что дальше
Workflow с параметром -PSComputerName на уровне самого workflow - это ещё один вариант, который мы не тестировали: передаёшь весь список сразу, и PowerShell сам занимается параллельным выполнением. Разберём в следующий раз.
Для сопровождения серверной инфраструктуры PowerShell 3.0 - ощутимый шаг вперёд. Не нужно изобретать параллельный патчинг через Start-Job и Wait-Job с колхозной синхронизацией - теперь есть штатный инструмент. Пока Workflows ведут себя предсказуемо на тестовом стенде; насколько они устойчивы в продакшне на Server 2012 GA - увидим после релиза.