Зачем собирать образ с помощью Packer?


  1. Время создания инстанса из готового образа значительно меньше, чем время, которое нужно затратить с нуля на подготовку виртуальной машины к работе. Это достаточно критичный момент, так как порой очень важно ввести в работу новый инстанс нужного типа за кратчайшее время для того, чтобы начать пускать на него трафик.
  2. Помимо того что образ виртуальной машины для DEV, TEST, Staging окружений, он всегда будет соответствовать по набору ПО и его настройкам тому серверу, который используется в production. Важность этого момента трудно недооценить — крайне желательно, чтобы деплой нового кода на продакшн привел к тому, чтобы сайт продолжал корректную работу с новой функциональностью, а не упал из-за какой-то ошибки, связанной с недостающим модулем или отсутствующим ПО.
  3. Автоматизация сборки production- и development-окружений экономит время системного администратора. В глазах работодателя это также должно быть несомненным плюсом, так как это означает, что за то же время администратор сможет выполнить больший объем работы.
  4. Время для тестирования набора ПО, его версий, его настроек. Когда мы подготавливаем новый образ заранее, у нас есть возможность (и, что самое главное, время!) для того, чтобы спокойно и вдумчиво проанализировать различные ошибки, которые возникли при сборке образа, и исправить их. Также есть время для тестирования работы приложения на собранном образе и внесения каких-то настроек для оптимизации приложений. В случае же, если мы настраиваем инстанс, который нужно было ввести в работу еще вчера, все возникающие ошибки, как правило, исправляются по факту их возникновения уже на работающей системе — конечно же, это не совсем правильный подход.

Более подробно можете прочитать про packer здесь.


В процессе сборки запускается Ansible, который устанавливает обновления Windows. С помощью ansible гораздо проще управлять установкой приложений, настройкой Windows чем Powershell. Подробнее про использование ansible и windows здесь.


В посте описаны ошибки, которые вы можете получить, и их решение.


Готовый работе репозиторий: https://github.com/patsevanton/packer-windows-with-update-ansible-yandex-cloud


Установка необходимых пакетов


sudo apt update
sudo apt install git jq python3-pip -y

Устанавливаем ansible pywinrm


sudo pip3 install ansible pywinrm

Устанавливаем коллекцию ansible.windows


ansible-galaxy collection install ansible.windows

Установка Packer.


Необходимо установить Packer по инструкции с официального сайта https://learn.hashicorp.com/tutorials/packer/get-started-install-cli


Установка Yandex.Cloud CLI


https://cloud.yandex.com/en/docs/cli/quickstart#install


curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash

Загружаем обновленные настройки .bashrc без выхода из системы


source ~/.bashrc

Инициализация Yandex.Cloud CLI


yc init

Клонируем репозиторий с исходным кодом


git clone https://github.com/patsevanton/packer-windows-with-update-ansible-yandex-cloud.git
cd packer-windows-with-update-ansible-yandex-cloud

Скачиваем ConfigureRemotingForAnsible.ps1


wget https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1

Создайте сервисный аккаунт и передайте его идентификатор в переменную окружения, выполнив команды


yc iam service-account create --name <имя пользователя>
yc iam key create --service-account-name <имя пользователя> -o service-account.json
SERVICE_ACCOUNT_ID=$(yc iam service-account get --name <имя пользователя> --format json | jq -r .id)

В документации к Yandex Cloud параметр folder_id описывается как <имя_каталога>.


Получите folder_id из yc config list


Назначьте сервисному аккаунту роль admin в каталоге, где будут выполняться операции:


yc resource-manager folder add-access-binding <folder_id> --role admin --subject serviceAccount:$SERVICE_ACCOUNT_ID

Заполняем файл credentials.json.


    "folder_id": "<folder_id>",
    "password": "<Пароль для Windows>"

Заполняем credentials.json


Запускаем сборку образа


packer build -var-file credentials.json windows-ansible.json

Готовый образ можно будет найти в сервисе Compute Cloud на вкладке Образы.


Через Yandex Cloud CLI будет виден вот так.


yc compute image list
+----------------------+-------------------------------------+--------+----------------------+--------+
|          ID          |                NAME                 | FAMILY |     PRODUCT IDS      | STATUS |
+----------------------+-------------------------------------+--------+----------------------+--------+
| fd88ak25ivc8rn1e4bn7 | windows-server-2021-09-20t04-47-57z |        | f2e01pla7dr1fpbeihp9 | READY  |
+----------------------+-------------------------------------+--------+----------------------+--------+

Описание кода в user-data


При сборке образа сначала выполняется скрипты, описанные в user-data:
Установка пароля пользователя Administrator


net user Administrator {{user `password`}}

Удаление скриптов из директории C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\


ls \"C:\\Program Files\\Cloudbase Solutions\\Cloudbase-Init\\LocalScripts\" | rm

Удаляет доверенные хосты службы WS-Management по адресу \Localhost\listener\listener*


Remove-Item -Path WSMan:\\Localhost\\listener\\listener* -Recurse

Удаляет сертификаты с локального сервера


Remove-Item -Path Cert:\\LocalMachine\\My\\*

Получает DNS имя из службы метаданных


$DnsName = Invoke-RestMethod -Headers @{\"Metadata-Flavor\"=\"Google\"} \"http://169.254.169.254/computeMetadata/v1/instance/hostname\"

Получает hostname имя из службы метаданных


$HostName = Invoke-RestMethod -Headers @{\"Metadata-Flavor\"=\"Google\"} \"http://169.254.169.254/computeMetadata/v1/instance/name\"

Получает сертификат из службы метаданных


$Certificate = New-SelfSignedCertificate -CertStoreLocation Cert:\\LocalMachine\\My -DnsName $DnsName -Subject $HostName

Создает новое значение HTTP в службе WS-Management по адресу \LocalHost\Listener c параметром Address *


New-Item -Path WSMan:\\LocalHost\\Listener -Transport HTTP -Address * -Force

Создает новое значение HTTPS в службе WS-Management по адресу \LocalHost\Listener c параметром Address *, HostName и Certificate


New-Item -Path WSMan:\\LocalHost\\Listener -Transport HTTPS -Address * -Force -HostName $HostName -CertificateThumbPrint $Certificate.Thumbprint

Добавляет в firewall правило разрешающее трафик на порт 5986


netsh advfirewall firewall add rule name=\"WINRM-HTTPS-In-TCP\" protocol=TCP dir=in localport=5986 action=allow profile=any

Тестирование и отладка


Для отладки запускаем packer с опцией debug


packer build -debug -var-file credentials.json windows-ansible.json

Но так как опция -debug требует потверждения на каждый шаг, для отладки добавляем конструкцию после шага, который нужно отладить, например, после запуска ansible.


{
    "type": "shell",
    "inline": [
        "sleep 9999999"
    ]
},

Отредактируем файл ansible/inventory. Проверим win_ping.


ansible windows -i ansible/test-inventory -m win_ping

Тестируем установку обновлений Windows без Packer


ansible-playbook -i ansible/test-inventory ansible/playbook.yml

Ошибки


Если не запустить ConfigureRemotingForAnsible.ps1 на Windows, то будет такая ошибка


basic: the specified credentials were rejected by the server

Если забыли установить "use_proxy" в false, то будет такая ошибка


basic: HTTPSConnectionPool(host=''127.0.0.1'', port=5986): Max retries exceeded with url: /wsman (Caused by NewConnectionError(''<urllib3.connection.VerifiedHTTPSConnection object at 0x7f555c2d07c0>: Failed to establish a new connection: [Errno 111] Connection refused''))

Если в таске Ensure the local user Administrator has the password specified for TEST\Administrator будет ошибка


requests.exceptions.HTTPError: 401 Client Error:  for url: https://xxx:5986/wsman
...
winrm.exceptions.InvalidCredentialsError: the specified credentials were rejected by the server

То вы забыли синхронизировать пароли в переменных pdc_administrator_password и ansible_password


Временный ansible inventory с именем /tmp/packer-provisioner-ansiblexxxx будет иметь вот такой вид:


default ansible_host=xxx.xxx.xxx.xxx ansible_connection=winrm ansible_winrm_transport=basic ansible_shell_type=powershell ansible_user=Administrator ansible_port=5986

Тестирование сборки Windows c Active Directory


Так же тестировал сборку Windows c Active Directory, используя Galaxy роль justin_p.pdc, но при запуске нового instance из собранного образа получал ошибку.
В комментариях написали что Running Sysprep on a domain controller is not supported


2021-09-18 19:34:31, Error SYSPRP ActionPlatform::LaunchModule: Failure occurred while executing 'CryptoSysPrep_Specialize' from C:\Windows\system32\capisp.dll; dwRet = 0x32
2021-09-18 19:34:31, Error SYSPRP SysprepSession::ExecuteAction: Failed during sysprepModule operation; dwRet = 0x32
2021-09-18 19:34:31, Error SYSPRP SysprepSession::ExecuteInternal: Error in executing action for Microsoft-Windows-Cryptography; dwRet = 0x32
2021-09-18 19:34:31, Error SYSPRP SysprepSession::Execute: Error in executing actions from C:\Windows\System32\Sysprep\ActionFiles\Specialize. xml; dwRet = 0x32
2021-09-18 19:34:31, Error SYSPRP RunPlatformActions:Failed while executing Sysprep session actions; dwRet = 0x32
2021-09-18 19:34:31, Error [0x060435] IBS Callback_Specialize: An error occurred while either deciding if we need to specialize or while specializing; dwRet = 0x32


Немного рекламы: На платформе https://rotoro.cloud/ вы можете найти курсы с практическими занятиями:


Комментарии (4)


  1. AlexGluck
    20.09.2021 20:10

    В новых современных версиях винды есть ссш, не пробовали заменить винрм на него?

    Так же есть letsencrypt, возможно он будет лучше самоподписаных сертификатов?


    1. chemtech Автор
      20.09.2021 20:12

      Не пробовал.


  1. mvv-rus
    20.09.2021 22:16
    +1

    Так же тестировал сборку Windows c Active Directory, используя Galaxy роль justin_p.pdc, но при запуске нового instance из собранного образа получал ошибку.

    Расскажите подробнее, что вы этим хотели добиться — может быть я вам смогу помочь.
    А код ошибки 0x32 означает ERROR_NOT_SUPPORTED
    Что совершенно логично: Running Sysprep on a domain controller is not supported
    PS Ну и выбор остальных средств вызывает у меня некоторые вопросы. К примеру, когда вы сравниваете ansible с Powershell, то вы, как я понимаю, имеете в виду самописные скрипты, а не Desired state configuration — которая тоже Powershell и которая является неким аналогом ansible в мире Windows. Впрочем, если у вас там в основном Linux, а Windows Server — «для больших оказий»(с), то вполне разумно выбирать то, с чем обычно работаешь.


    1. chemtech Автор
      21.09.2021 12:45

      Спасибо за интересный комментарий.

      Выбор ansible обусловлен тем что с ansible я знаком, а с Desired state configuration и Powershell не знаком.