Настоящий админ может спать спокойно лишь тогда, когда у него всё бэкапится, мониторится и дублируется.
Так получилось, что я в своей работе использую в основном продукты Microsoft и могу сказать, что компания серьезно подходит к резервированию своих сервисов: Active Directory, Exchange DAG, SQL Always On, DFSR и т.д. Как и везде, здесь есть как весьма изящные и удачные реализации, так и явно неудобные и тяжелые. Для сервиса печати тоже есть решение, но для него необходима кластеризация на базе Hyper-V. А хотелось простого решения “из коробки”, не требующего дополнительных финансов. За основу была взята Windows 2012 R2, но скорее всего та же схема без проблем будет работать на любых серверных версиях, начиная с Windows 2008, и даже клиентских ОС от Vista и выше (привет любителям экономить бюджет!). Кому интересно — прошу под кат.
Немного теории
Кто не любит теорию и хочет быстрее поклацать мышью и клавиатурой, может сразу перейти к следующей части.
Как было сказано выше, официальная рекомендация на сегодняшний день — это решение с использованием кластеризации и виртуализации Hyper-V. Также ничто не мешает обеспечить отказоустойчивость сервиса печати на уровне системы виртуализации, причем не обязательно Hyper-V, но такие решения стоят денег.
Мне очень хотелось что-нибудь похожее на DHCP Failover, но для роли принт-сервера.
В интернете в целом и на хабре в частности ничего подходящего не нашлось — и пришлось изобретать самому.
Суть идеи в одном абзаце
Описанное ниже решение основано на использовании утилиты BrintBrm, входящей в стандартную поставку Windows и пришедшую на замену printmig.
Резервный сервер работает в standby-режиме и с заданной периодичностью синхронизирует настройки с основным сервером с помощью этой утилиты. Для клиентских машин в DNS создан CNAME с малым TTL, ссылающийся на основной сервер. В случае аварии основного сервера админ правит CNAME, переключая клиентов на резервный сервер. Вот, собственно, и всё.
Кто имеет достаточно опыта, тот пусть дальше не читает и делает всё сам. Либо побухтит в комментариях про зря потраченное время. Тех же, кому хочется познакомиться с уже набитыми мной шишками и путями обхода граблей, прошу следовать дальше.
Before you begin, или что нужно знать о PrintBrm
Итак, какова она, эта утилита PrintBrm, главное назначение которой — прислуживать серверу печати?
- Ухожена. Имеет GUI-воплощение, которое именуется Перенос принтеров (Print Migration) и может быть запущено из оснастки Управление печатью. GUI-вариант менее функционален и имеет проблемы с переносом портов.
- Внимательна. По умолчанию обрабатывает ACL принтеров принт-сервера. Другими словами, если вы разрешили печатать на принтере \\printserver\printer1 только сотрудникам, входящим в AD-группу Бухгалтерия, то это ограничение будет учтено импорте/экспорте. Или не будет, если поставить ключ -NOACL. При этом ACL самого сервера печати не обрабатывается независимо от ключа.
- Капризна. На момент импорта параметров из файла на целевом сервере должен быть хотя бы один расшаренный принтер, иначе получите ошибку.
- Нежна. Теряется, видя пробелы в пути файла. При виде кавычек, обрамляющих такой путь, огорчается и выдает ошибку 0x8007007b.
- Скромна. Если при попытке экспорта настроек указанный файл уже существует, перезаписать его не может, спросить стесняется и также завершается с ошибкой.
- Таинственна. Всегда возвращает exit-код, равный 0. Получается, идеальная программа.
- Склонна к раздумьям. Может подзависнуть на стадии 100% минут на 5, а иногда и больше. Но потом одумывается и завершает работу (если, конечно, у вас хватит терпения не нажать Ctrl+C).
- Внезапна и противоречива. Может устраивать вот такие сюрпризы.
- Умна. Может переназначать исходные драйверы на другие. Например, с помощью XML-файла можно указать, что все драйверы HP Universal Printing PCL 5 в сохраненном файле на целевом сервере надо переназначить на HP Universal Printing PCL 6. На практике не использовал, но для кого-то может пригодиться.
- Своенравна. Использовать ее для переноса настроек между доменами без доверия у меня не получилось, даже с ключом -NOACL. Либо не умеет в принципе, либо моя магия недостаточно сильна.
- Познакомиться поближе можно тут и здесь, а для тех отважных, кто не стесняется спросить напрямую, есть ключ /?
Допускаю, что какие-то черты я незаслуженно обошел вниманием. Возможно, в Windows 10/2016 она стала вести себя иначе. Если есть информация, прошу поделиться.
Подготовка среды
Предполагается, что у вас уже развернута Active Directory и вы знаете как минимум 3 способа вывести ее из строя и хотя бы 2 из них были опробованы на практике.
Также мне нравится, когда каждый сетевой принтер прописан во внутренней DNS-зоне. С этой задачей легко справится DHCP-сервер на базе Windows.
К примеру, имя принтера может быть формата msk-prn001 или sale-printer023, причем имена портов для этих принтеров на принт-сервере названы точно так же. Но это лично мои предпочтения, готов выслушать возражения в комментариях.
Будем исходить из того, что все принтеры сетевые и доступны для печати с основного и резервного принт-серверов. Пусть эти серверы называются prn-srv01 и prn-srv02 соответственно.
В качестве принт-серверов подойдут доменные машины на Windows Server не ниже 2008. В принципе подойдут и клиентские ОС, начиная с Vista, если уж очень хочется сэкономить. В примере используется Windows 2012 R2. Крайне желательно перед настройкой установить все необходимые обновления операционной системы как на серверы, так и на клиентские машины.
Вы и сами, конечно, понимаете, но кэп всё же требует обратить внимание: если принт-серверы будут виртуальными, то они обязательно должны быть разнесены по разным физическим серверам, иначе наш failover превратится просто в fail.
На prn-srv01 и prn-srv02 должна быть добавлена роль сервера печати. Мне удобнее для этого использовать командлет PowerShell:
Install-WindowsFeature Print-Services
Также на принт-серверах должен быть применен твик реестра, который исправляет ошибку 0?00000709 при обращении клиентских машин к принт-серверу по CNAME. Можно сделать это командой из статьи по ссылке выше:
reg add HKLM\SYSTEM\CurrentControlSet\Control\Print /v DnsOnWire /t REG_DWORD /d 1
После применения команды нужно перезапустить службу Диспетчер печати.
Рекомендую выделить для принт-серверов отдельный OU и раздавать эту настройку с помощью GPP.
Запускаем оснастку DNS на контроллере домена и включаем расширенное отображение:
Расширенное отображение нужно, чтобы иметь возможность задать TTL для создаваемых записей.
В DNS создаем CNAME-запись print, ссылающуюся на prn-srv01 с 5-минутным значением TTL:
Это имя должны использовать клиентские машины для подключения к принт-серверу. Т.е. клиент будет подключаться к адресам \\print\printer01, \\print\printer02 и т.д.
Чем меньше значение TTL, тем чаще клиенты будут обновлять запись и быстрее “поймут”, что надо переключиться на другой сервер печати. Мне достаточно 5 минут.
Задав слишком малое значение, вы плодите DNS-трафик в своей сети,
Альтернативный вариант добавления CNAME-записи с помощью PowerShell:
Import-Module DnsServer
Add-DnsServerResourceRecordCName -Name "print" -HostNameAlias "prn-srv01.lab.net" -ZoneName "lab.net" -TimeToLive 00:05:00
(Разумеется, lab.net меняем на ваш contoso.local или как там его)
Надо учесть, что если у вас несколько сайтов AD, то обновление DNS-записи во всех локациях займет больше времени за счет межсайтовой репликации. Форсировать процесс можно командой repadmin /syncall.
Средствами групповой политики разрешаем рядовым пользователям устанавливать драйверы с принт-сервера. О том, как это сделать, подробно написано тут.
Создаем служебную учетную запись в AD (я назвал ее svc-printsync) с неограниченным сроком действия пароля:
Согласно требованиям PrintBrm, эта учетная запись должна обладать полными правами на принт-сервере, поэтому добавляем ее в
Настраиваем первый сервер
Если все нужные принтеры на основном принтере уже добавлены, то можно сразу перейти к разделу о настройке второго сервера.
С помощью оснастки Управление печатью добавляем на сервер драйверы нужных принтеров:
Запустится мастер установки драйверов. Он интуитивно понятен, тут сами разберетесь. Обращу лишь внимание на момент с разрядностью.
Т.к. Windows 2012R2 поставляется только в x64-варианте, то драйверы тоже должны быть x64. Если же к серверу печати будут подключаться клиенты с x86-версиями Windows, не забудьте поставить соответствующий флажок:
Некоторые комплекты драйверов содержат общий inf-файл и для x86, и для x64-систем, в других же присутствует разделение.
Также в целях единообразия я по максимуму стремлюсь использовать Universal-вариант драйверов (есть практически у всех нормальных вендоров). Но с ним иногда могут быть проблемы. Так, однажды встретил баг в одной из версий HP Universal Printing PCL 6, при котором PDF-документ через EasyPrint в RDP-сеансе печатался зеркально слева направо.
Можно ещё посмотреть в сторону v4-драйверов.
Когда все необходимые драйверы добавлены, займемся портами и принтерами. Можно их добавить вручную из той же оснастки, но я рекомендую создать CSV-файл в Excel и скормить его PowerShell-скрипту. Разумеется, ничто не мешает вместо Excel использовать любой другой табличный редактор или вообще блокнот. Главное — чтобы разделитель и кодировка, указанные в скрипте, соответствовали разделителю и кодировке в CSV-файле.
Также обратите внимание, что имя драйвера в CSV-файле должно быть точно таким же, каким оно указано в оснастке Управление печатью.
Хоть я писал выше, что мне нравится, когда все принтеры имеют унифицированные сетевые имена, в примере (поле Адрес принтера) использован винегрет из IP-адресов и имен на случай, если порядок у вас в сети
Сохраним эту таблицу в CSV-формате:
Примечание. Несмотря на то, что в поле “Тип файла” в качестве разделителей указаны запятые, у меня Excel разделителем сделал точку с запятой. Наверно, чтобы было интереснее и веселее.
А вот сам скрипт:
#Откуда будем загружать данные
$InputFile = 'C:\Scripts\Printers.csv'
#Разделитель и кодировка должны соответствовать формату CSV-файла
$Printers = (Import-Csv $InputFile -Delimiter ";" -Encoding Default)
#Все указанные в файле драйверы должны присутствовать на целевом сервере
ForEach ($Printer in $Printers) {
#Текст должен соответствовать заголовкам столбцов в файле
$PrinterName = $Printer.'Имя принтера'
$ShareName = $Printer.'Имя общего ресурса'
$DriverName = $Printer.'Имя драйвера'
$PrinterAddr = $Printer.'Адрес принтера'
$Comment = $Printer.'Комментарии'
$Location = $Printer.'Размещение'
#Добавляем порт
Add-PrinterPort -Name $PrinterAddr -PrinterHostAddress $PrinterAddr -SNMP 1 -SNMPCommunity 'public'
#Добавляем принтер
Add-Printer -Name $PrinterName -DriverName $DriverName -PortName $PrinterAddr -Comment $Comment -Location $Location
#и расшариваем его
Set-Printer -Name $PrinterName -Shared $True -Published $False -ShareName $ShareName
}
Если в качестве разделителя в вашем CSV используется знак табуляции, то в скрипте надо выставить -Delimiter "`t"
Учтите, что если во время работы скрипта какой-нибудь принтер будет недоступен с сервера, то его добавление на принт-сервер займет больше времени (2-3 минуты вместо нескольких секунд)
Результат работы скрипта:
Чтобы убедиться, что на этом этапе всё работает, добавляем на любую из клиентских машин общий принтер с основного принт-сервера, используя ранее созданный CNAME (например, \\print\printer01), и пробуем распечатать на нем что-нибудь.
Настраиваем второй сервер
Un artista copia, un gran artista roba (Пабло Пикассо)
Наш prn-srv02 пока еще не дорос до уровня gran artista, поэтому ограничимся копированием.
Создаем и расшариваем хотя бы один принтер, иначе PrintBrm выдаст ошибку. Можно сделать фейковый, но при этом важно не выбрать неподходящий драйвер или порт. Например, принтер с драйвером Microsoft XPS Document Writer или портом FILE: расшарить не получится.
Создаём незатейливый скрипт синхронизации. Я предпочитаю PowerShell, но никто не запрещает сделать теплый ламповый батник.
#Путь к утилите PrintBrm
$ProgramPath = 'C:\Windows\System32\Spool\Tools\PrintBrm.exe'
#Основной и резервный серверы
$SourceServer = 'prn-srv01'
$DestServer = 'prn-srv02'
#Файл, куда выгружаем настройки. Путь не должен содержать пробелы, т.к. утилита PrintBrm не понимает кавычки в пути файла
$ConfigFilePath = 'C:\Scripts\prn-config.printerExport'
#Экспортируем принтеры в файл
$Arguments = "-s $SourceServer -f $ConfigFilePath -b"
Start-process $ProgramPath -ArgumentList $Arguments -Wait -PassThru
#Импортируем принтеры из файла
$Arguments = "-s $DestServer -f $ConfigFilePath -r -o force"
Start-process $ProgramPath -ArgumentList $Arguments -Wait -PassThru
#Прибираемся за собой
Del $ConfigFilePath
Кладем скрипт в укромное место (в примере это C:\Scripts) и создаем задачу в Планировщике.
Запускать будем из-под ранее созданной учетной записи svc-printsync с наивысшими правами:
Частоту выполнения определите для себя сами. Мне достаточно раз в сутки:
На вкладке Действия создаем новое действие запуска PowerShell:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
В качестве аргументов задаем путь к скрипту со следующими параметрами:
C:\Scripts\PrintSync.ps1 -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass
Остальные параметры задачи на вкладках Условия и Параметры оставляем по умолчанию.
При сохранении задачи будет запрошен пароль для учетной записи svc-printsync. Вы ведь его не забыли? Если уже забыли (статья-то длинная), то
В первый раз запускаем задание вручную и дожидаемся его завершения.
Для моего зоопарка, где около 50-ти принтеров разных видов, как вымирающих, так и недавно выведенных, процедура синхронизации занимает примерно 10 минут. Файл при этом весит почти 1ГБ.
Для ускорения процесса импорта/экспорта можно использовать ключ -NOBIN, который отвечает за копирование драйверов. Имеет смысл, когда парк принтеров состоит из одинаковых моделей и необходимые драйверы установлены на всех серверах.
После завершения запускаем оснастку Просмотр событий, переходим в раздел Журналы приложений и служб, открываем журнал Microsoft\Microsoft\PrintBRM\Администратор и анализируем его на предмет ошибок и предупреждений.
Мне попадались с кодами 20, 22, 80 и 81. Например,
Как ясно из текста, возникла проблема при переносе определенного драйвера. Просматривая журнал, составляем список проблемных драйверов и ставим их руками на резервный сервер, либо заменяем другими, которые не прочь попутешествовать. У меня были проблемы лишь с HP, Kyocera и Konica Minolta, для драйверов других производителей ошибок не выявилось (может потому, что они лучше, а может потому, что у нас их просто нет).
В итоге нужно добиться одинакового списка принтеров на основном и резервном серверах и отсутствия ошибок и предупреждений в логах.
Переключаемся на резерв
Через некоторое время (что вы там ставили в TTL?)
Возвращаемся обратно
Если за время восстановления основного сервера на резервном были изменения конфигурации, которые необходимо сохранить, запускаем синхронизацию в другую сторону. Для этого в указанном выше скрипте PrintSync.ps1 меняем местами значения переменных $SourceServer и $DestServer. После переноса изменений не забудьте вернуть эти значения обратно, иначе все изменения в конфигурации принтеров на prn-srv01 будут нещадно отметаться каждую ночь злой волей судьбы.
В оснастке DNS устанавливаем для CNAME-записи print значением конечного узла prn-srv01 — и всё возвращается на круги своя.
Что в итоге?
Бурные овации руководства, подкидывание админа на руках, повышение зарплаты (автору статьи — честные 10% от прибавки)…
Ну и несколько мыслей в сторону наведения дальнейшей красоты.
Чудес, к сожалению, на всех не хватает, и данное решение — не полноценный Failover. Если в момент крушения основного принт-сервера на нем будут непустые очереди печати, то их содержимое скорее всего канет в лету и кому-то придется повторять отправку на печать.
Зато очень удобно будет прозрачно для пользователей выполнять регламентное обслуживание серверов печати.
Фанаты автоматизации могут пойти дальше и создать скрипт, который на входе получает имена серверов с интервалом синхронизации и остальные настройки делает сам: создает сервисную учетную запись при необходимости, задание в планировщике и т.д.
Гуру мониторинга добавят наблюдение за выполнением задачи синхронизации и ошибками в логах.
Любители копать глубже могут продумать двухстороннюю синхронизацию в духе репликации AD с отслеживанием времени изменений по каждому принтеру. PrintBrm тут уже не поможет, но никто не отменял PowerShell!
Вишенкой на торте будет автоматическая установка принтеров на клиентских машинах с помощью GPP с нацеливанием на группу AD. Добавляем пользователя в группу — и ему прилетает нужный принтер. Правда, это уже другая история, выходящая за рамки статьи.
Надеюсь, для кого-то моя публикация окажется полезной. Желаю всем поменьше сбоев и жду вопросов и предложений в комментариях.
Комментарии (13)
NoOne
19.06.2018 07:01За утилиту PrintBrm спасибо.
У меня раньше была идея в создании failover кластера просто с виртуальным именем и IP-адресом, которые бы ездили между узлами, и добавлением службы Print Spooler как ресурс Generic Service в кластере. Но как раз был вопрос в синхронизации принтеров между двумя узлами. Ручной вариант (создать принтер и тут, и там) не устроил, поэтому этот тему приостановили.
А вот теперь с PrintBrm есть интересный вариант. Тем более, что можно также создать кластерное задание планировщика задач, которое выполняется на узле владельце роли (который будет определять направление репликации).
Таким образом будет и автоматический failover (упала служба/сервер) и автоматическая синхронизация принтеров.
Если бы потом ещё с очередью разобраться, было бы вообще хорошо :)
simplix
19.06.2018 09:58Конечно, у каждого админа своя магия, но для меня в своё время решением стало использование ScrewDrivers — клиент-серверной утилиты, которая пробрасывает локальные принтера пользователей к серверу терминалов. Это не только решает проблему печати на старых принтерах, драйверов для которых нет для 2008+, но и предотвращает конфликты драйверов на сервере, которые с ростом принтеров рано или поздно приводят к падениям службы печати. И принцип failover тоже соблюдается, так как клиенты могут печатать на своих принтерах сразу же, к какому серверу они бы не подключились.
perlestius Автор
19.06.2018 15:59А если есть задача собирать централизованно статистику по использованию сервиса печати (расход бумаги и тонера и и.д.), ваше решение это предусматривает?
simplix
19.06.2018 16:46Статистика мне не нужна, погуглил — ThinPrint такое умеет, и ещё посмотрите Simplify Printing от той же Tricerat, может быть там что-то такое есть.
andreyle
19.06.2018 13:50Не совсем понял для чего создавать сервисную учетную запись в домене и от этой учетки запускать задачу в планировщике. Можно же просто запускать от пользователя «Система». Не требуется сохранение пароль, правда на сетевую шару не запишешь, но тут это не требуется.
А за статью огромное спасибо, как раз искал материал по этой теме, буду пробовать у себя реализовывать.perlestius Автор
19.06.2018 13:55Учётная запись должна обладать административными правами на ОБОИХ серверах. Если сделать так, как Вы предлагаете, задание не сможет сделать бэкап, т.к. не будет иметь прав на prn-srv01
rionnagel
Спасибо за статью.
А можете более подробно написать про практическое использование? Я просто пытаюсь прикинуть когда у тебя мешанина старого, нового, сетевого, usb, конфликтующих дров и взаимодействия/проблем с терминальными серверами.
perlestius Автор
Не совсем понял вопрос. USB-принтеров, физически подключенных к принт-серверу, у меня нет. Есть много тех, что подключены к рабочим станциям (наследство времён былой роскоши), которые без крови у людей не заберёшь. Есть вариант и такие принтеры завести на принт-сервер, запретив пользователям печатать на них напрямую (включая "хозяина"), но надо ли оно?
С RDP-пробросом бывают проблемы, когда, например, драйвер принтера не совместим с TS Easy Print и нужно по-старинке ставить его (драйвер) на RDP-сервер. Или устаревший клиент на XP, которому надо .NET Framework обновить/переустановить. Или — что иногда разумнее — Винду поновее залить. It depends.
И в целом касательно мешанины и конфликтов — тут бесконечное множество комбинаций, и, как в песне поётся, "не разберёшь, пока не повернёшь".
ZlobniyShurik
На моей бывшей работе с локальными принтерами разобрались просто — начальство тупо издало приказ о переходе на станции печати (большие сетевые принтера по коридорам) и перестало выделять деньги на закупку картриджей к зоопарку локальных принтеров.
Так как картриджи в локальных принтерах кончались вразнобой, то юзеры не смогли объединиться и устроить протест ;)
P.S. Несколько локальных принтеров, которые реально были нужны, получили именную строчку в бюджете «покупка картриджа XXX для принтера YYY в отделе ZZZ».
rionnagel
Да я скорее хотел про проблемы услышать, которые вы встречаете в рамках данной темы)
С рдп, как написали ниже используем ScrewDrivers ещё с 2003, освобождает необходимость вообще дрова принтеров ставить на rdp (и кстати нехило сжимает размер отправленных на печать заданий)… но кто его знает, может в современной реализации серверной винды с этим хорошо… узнать хочется, а пробовать нет… ибо накушался)
perlestius Автор
Проблемы мелкие есть, но на моей практике все они благополучно решались. Поэтому в целом я доволен (говорю про 2012R2 и 2008R2 и клиентов не ниже Windows 7)
DenMMM
С терминальниками вопрос лучше решить радикально: выкинуть Easy Print и поставить ScrewDrivers/ThinPrint. Печатает все, от GDI принтеров Canon до больших копиров Kyocera. Даже домашние аппараты сотрудников пробрасываются.