概述
VM映像做为创建VM的必要条件,各类云环境映像市场均有提供最基础的映像。创建VM后,通常还需要根据组织或用户的需求,安装一些软件、修改配置后才能满足使用需求。这类需求通常可以手动部署或者借助一些配置管理工具,如ansible、puppet、saltstack等来实现,也可以考虑构建自定义的映像,集成软件部署和配置。
映像构建的过程
通常自定义映像比较繁琐,需要经历如下几个步骤,以Azure云为例:
1)创建构建映像使用的VM
2)部署软件/修改配置
3)通用化
4)关机
5)捕获映像
6)删除构建映像使用的VM
使用packer可以自动完成上述这些步骤
Packer构建映像
Packer是HashiCorp推出的一款自动构建VM/Docker映像的工具,支持各大主流云平台,可以结合puppet、ansible等配置管理工具高效定制映像。

packer官方文档:https://developer.hashicorp.com/packer/docs
Packer构建AVD映像
本文分享使用packer构建AVD映像的案例,AVD(Azure Virtual Desktop)是Azure推出的虚拟桌面服务,通过集成一些常用的客户端工具,为用户提供方便快捷的资源访问入口。基于Azure VMSS(Virtual Machine Scale Sets)提供会话主机,VMSS使用的映像需要集成各类客户端工具。本场景中我们使用packer+ansible来实现AVD映像的自动构建。有关AVD的介绍,请参考:https://docs.azure.cn/zh-cn/virtual-desktop/overview
环境准备
准备一台用来执行映像构建的VM,本案例使用的VM信息如下
操作系统:CentOS 7
VMSIZE:Standard_D2s_v3
安装packer、ansible
[localhost]# yum install epel-release -y
[localhost]# yum install ansible -y
[localhost]# wget -c
‘https://releases.hashicorp.com/packer/1.8.1/packer_1.8.1_linux_amd64.zip’
[localhost]# unzip packer_1.8.1_linux_amd64.zip -d /usr/bin/
[localhost]# pip install pywinrm
生成VM托管标识和授权
VM托管标识(Managed Identity)是用来在Azure上做身份认证和授权的,认证可选的方式还有Service Principal(SP)方式,需要在packer的模版文件中声明client_id、client_secret。此处更推荐使用托管标识的方式,无需在packer模版文件配置相关敏感信息,且托管标识方式跟Azure资源绑定后才能使用,更加安全。有关托管标识的说明,请参考:https://learn.microsoft.com/zh-cn/entra/identity/managed-identities-azure-resources/overview
安装azcli
导入azcli仓库key
[localhost]# rpm –import https://packages.microsoft.com/keys/microsoft.asc
添加azcli仓库
[localhost]# echo -e “[azure-cli]
name=Azure CLIbaseurl=https://packages.microsoft.com/yumrepos/azure-cli
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc” | sudo tee /etc/yum.repos.d/azure-cli.repo
安装azcli并登录
[localhost]# yum install -y azure-cli
[localhost]# az cloud set
-n AzureChinaCloud[localhost]# az login
创建VM托管标识
开启VM托管标识和授予订阅参与者权限
[localhost]$ az vm identity assign –identities [system] –role Contributor –scope /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx –ids /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM
packer模版文件编辑
packer模版语法支持HCL和JSON格式,并且可以从HCL转换到JSON格式,此处为了方便添加注释,使用HCL来描述模版。
[localhost]# cat avd-images.pkr.hclsource “azure-arm” “avd-images” { #avd-images为模版名称 # use Azure Managed Identity client_id = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx” #VM托管标识的objectID cloud_environment_name = “AzureChinaCloud” managed_image_resource_group_name = “resourcegroup” #映像所在的资源组 managed_image_name = “avd-images-test” #映像名称 os_type = “Windows” #映像操作系统类型和版本 image_publisher = “MicrosoftWindowsServer” image_offer = “WindowsServer” image_sku = “2019-Datacenter” build_resource_group_name = “resourcegroup” #构建映像时使用的资源组,默认会创建“pkr-Resource-Group-”命名格式的资源组 virtual_network_name = “virtualnetwork” #构建映像时使用虚拟网络,默认会创建”pkrvn“命名格式的虚拟网络 virtual_network_subnet_name = “subnetwork” #构建映像时使用的子网 virtual_network_resource_group_name = “networkresourcegroup” #虚拟网络所在的资源组 private_virtual_network_with_public_ip = false #构建映像时是否创建公网IP,默认为true vm_size = “Standard_DS2_v2” #构建映像时使用的vm型号 # config winrm communicator = “winrm” #winrm连接的相关配置,ansible通过winrm方式来管理windows机器 winrm_use_ssl = true winrm_insecure = true winrm_timeout = “5m” winrm_username = “packer” # define tags #配置一些tag信息 azure_tags = { key1 = “value1” key2 = “value2” }} build { sources = [“sources.azure-arm.avd-images”] # config winrm for ansible provisioner “powershell” { #下载和执行powershell脚本,配置winrm inline = [ “$wirmsrc = ‘https://xxxxxxxxxxx/ConfigureRemotingForAnsible.ps1′”, “$wirmdes = ‘C:\\ConfigureRemotingForAnsible.ps1′”, “Invoke-WebRequest -uri $wirmsrc -OutFile $wirmdes”, “Unblock-File $wirmdes”, “powershell.exe -ExecutionPolicy Unrestricted -File ‘C:\\ConfigureRemotingForAnsible.ps1′”, “rm ‘C:\\ConfigureRemotingForAnsible.ps1′” ] } # exec ansile playbook provisioner “ansible” { #调用ansible playbook,执行软件安装配置 playbook_file = “./playbook.yml” user = “packer” use_proxy = false extra_arguments = [ “-e”, “ansible_winrm_server_cert_validation=ignore”, “-vvv” ] } # generalized image provisioner “powershell” { #执行通用化 inline = [ “while ((Get-Service RdAgent).Status -ne ‘Running’) { Start-Sleep -s 5 }”, “while ((Get-Service WindowsAzureGuestAgent).Status -ne ‘Running’) { Start-Sleep -s 5 }”, “& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit”, “while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne ‘IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE’) { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }” ] }}
ansible playbook预览
[localhost]# cat playbook.yml —- hosts: default gather_facts: false roles: – init – winrar – winscp – storageexplorer – vscode – chrome – putty……
ansible-role预览
[localhost]# tree -L 1 roles/roles/├── chrome├── config_env_variables├── config_proxy├── edge├── fslogix……
ansible task预览
[localhost]# cat roles/winrar/tasks/main.yml —# tasks file for winrar – name: Download winrar win_get_url: url: ‘https://xxxx/winrar-x64-602.exe’ dest: C:\TempAvd\ – name: Install winrar win_command: ‘C:\TempAvd\winrar-x64-602.exe -S’ args: creates: ‘C:\Program Files\WinRAR\WinRAR.exe’
Note
Icon
使用ansible或脚本方式在windows上部署软件时,需要使用静默安装的方式,不同安装包支持的参数可能存在差异,可以在命令行使用”/?”来查看帮助,如xxx.exe /?,xxx.msi /?
构建映像
[localhost]# packer build avd-images.pkr.hcl 看到如下输出表示构建完成OSType: WindowsManagedImageResourceGroupName: resourcegroupManagedImageName: avd-images-testManagedImageId: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/resourcegroup/providers/Microsoft.Compute/images/avd-images-testManagedImageLocation: chinaeast2
packer构建映像会执行如下几个步骤:
1)检查或创建资源组、创建VM、创建keyvault
2)配置winrm、调用ansible playbook部署软件
3)执行通用化
4)捕获映像
5)删除构建映像时由packer创建的临时资源
使用构建后的映像部署到AVD的VMSS,可以看到playbook中定义的软件都已安装

随后AVD发布应用效果如下

小结
1)packer使用模版描述构建映像时所需的步骤,自动完成映像构建和临时资源清理工作
2)packer支持集成系统shell和调用配置管理工具实现软件部署、配置管理