教程:使用 Ansible 更新 Azure 虚拟机规模集的自定义映像
重要
运行本文中的示例 playbook 需要 Ansible 2.8(或更高版本)。
Azure 虚拟机规模集是一项 Azure 功能,可让你配置一组相同的、负载均衡的 VM。 规模集不需要额外的成本,它们是从虚拟机构建的。 只需为基础的计算资源(例如 VM 实例、负载均衡器或托管磁盘存储)付费。 使用规模集时,会提供管理层和自动层来运行和缩放应用程序。 你可以改为手动创建和管理各个 VM。 但是,使用规模集有两个主要优点。 它们内置于 Azure 中,并自动缩放虚拟机以满足应用程序需求。
部署 VM 后,可以使用应用所需的软件配置 VM。 可创建自定义映像,而不是为每个 VM 执行此配置任务。 自定义映像是包含任何已安装软件的现有 VM 快照。 在配置规模集时,可以指定要用于该规模集的 VM 映像。 通过使用自定义映像,每个 VM 实例都为应用进行相同的配置。 有时,可能需要更新规模集的自定义映像。 该任务是本教程的重点。
在本文中,学习如何:
- 使用 HTTPD 配置两个 VM
- 从现有 VM 创建自定义映像
- 从映像创建规模集
- 更新自定义映像
先决条件
- Azure 订阅:如果没有 Azure 订阅,请在开始之前创建一个免费帐户。
安装 Ansible - 执行以下任一选项:
- 在 Linux 虚拟机上安装和配置 Ansible
- 配置 Azure Cloud Shell,另外,如果无法访问 Linux 虚拟机,请使用 Ansible 创建虚拟机。
配置两个 VM
本部分中的 playbook 代码创建两个虚拟机并在这两个虚拟机上都安装了 HTTPD。
每个 VM 的 index.html
页面显示一个测试字符串:
- 第一个 VM 显示值
Image A
- 第二个 VM 显示值
Image B
此字符串用于模拟使用不同的软件配置每个 VM。
可通过两种方式获取示例 playbook:
下载 playbook 并将其保存到
create_vms.yml
。创建名为
create_vms.yml
的新文件。 将以下代码插入新文件。
- name: Create two VMs (A and B) with HTTPS
hosts: localhost
connection: local
vars:
vm_name: vmforimage
admin_username: testuser
admin_password: Pass123$$$abx!
location: eastus
tasks:
- name: Create a resource group
azure_rm_resourcegroup:
name: "{{ resource_group }}"
location: "{{ location }}"
- name: Create virtual network
azure_rm_virtualnetwork:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
address_prefixes: "10.0.0.0/16"
- name: Create subnets for VM A and B
azure_rm_subnet:
resource_group: "{{ resource_group }}"
virtual_network: "{{ vm_name }}"
name: "{{ vm_name }}"
address_prefix: "10.0.1.0/24"
- name: Create Network Security Group that allows HTTP
azure_rm_securitygroup:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
rules:
- name: HTTP
protocol: Tcp
destination_port_range: 80
access: Allow
priority: 1002
direction: Inbound
- name: Create public IP addresses for VM A and B
azure_rm_publicipaddress:
resource_group: "{{ resource_group }}"
allocation_method: Static
name: "{{ vm_name }}_{{ item }}"
loop:
- A
- B
register: pip_output
- name: Create virtual network interface cards for VM A and B
azure_rm_networkinterface:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}_{{ item }}"
virtual_network: "{{ vm_name }}"
subnet: "{{ vm_name }}"
public_ip_name: "{{ vm_name }}_{{ item }}"
security_group: "{{ vm_name }}"
loop:
- A
- B
- name: Create VM A and B
azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}{{ item }}"
admin_username: "{{ admin_username }}"
admin_password: "{{ admin_password }}"
vm_size: Standard_B1ms
network_interfaces: "{{ vm_name }}_{{ item }}"
image:
offer: UbuntuServer
publisher: Canonical
sku: 16.04-LTS
version: latest
loop:
- A
- B
- name: Create VM Extension
azure_rm_virtualmachineextension:
resource_group: "{{ resource_group }}"
name: testVMExtension
virtual_machine_name: "{{ vm_name }}{{ item }}"
publisher: Microsoft.Azure.Extensions
virtual_machine_extension_type: CustomScript
type_handler_version: 2.0
auto_upgrade_minor_version: true
settings: {"commandToExecute": "sudo apt-get -y install apache2"}
loop:
- A
- B
- name: Create VM Extension
azure_rm_virtualmachineextension:
resource_group: "{{ resource_group }}"
name: testVMExtension
virtual_machine_name: "{{ vm_name }}{{ item }}"
publisher: Microsoft.Azure.Extensions
virtual_machine_extension_type: CustomScript
type_handler_version: 2.0
auto_upgrade_minor_version: true
settings: {"commandToExecute": "printf '<html><body><h1>Image {{ item }}</h1></body></html>' >> index.html; sudo cp index.html /var/www/html/"}
loop:
- A
- B
- debug:
msg: "Public IP Address A: {{ pip_output.results[0].state.ip_address }}"
- debug:
msg: "Public IP Address B: {{ pip_output.results[1].state.ip_address }}"
使用 ansible-playbook
命令运行 playbook,用资源组名称替换 myrg
:
ansible-playbook create-vms.yml --extra-vars "resource_group=myrg"
由于 playbook 的 debug
部分,ansible-playbook
命令将打印每个 VM 的 IP 地址。 请复制这些 IP 地址供稍后使用。
连接到两个 VM
在本部分中,你将连接到每个 VM。 如前一部分所述,字符串 Image A
和 Image B
模拟具有不同配置的两个不同 VM。
使用上一节中的 IP 地址,打开浏览器并连接到每个 VM。
从每个 VM 创建映像
此时,你有两个 VM,其配置(它们的 index.html
文件)略有不同。
本部分中的 playbook 代码为每个 VM 创建自定义映像:
image_vmforimageA
- 为在其主页上显示Image A
的 VM 创建的自定义映像。image_vmforimageB
- 为在其主页上显示Image B
的 VM 创建的自定义映像。
可通过两种方式获取示例 playbook:
下载 playbook 并将其保存到
capture-images.yml
。创建名为
capture-images.yml
的新文件。 将以下代码插入新文件:
- name: Capture VM Images
hosts: localhost
connection: local
vars:
vm_name: vmforimage
tasks:
- name: Stop and generalize VMs
azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}{{ item }}"
generalized: yes
loop:
- A
- B
- name: Create an images from a VMs
azure_rm_image:
resource_group: "{{ resource_group }}"
name: "image_{{ vm_name }}{{ item }}"
source: "{{ vm_name }}{{ item }}"
loop:
- A
- B
使用 ansible-playbook
命令运行 playbook,用资源组名称替换 myrg
:
ansible-playbook capture-images.yml --extra-vars "resource_group=myrg"
使用映像 A 创建规模集
在本部分中,使用了 playbook 配置以下 Azure 资源:
- 公共 IP 地址
- 负载均衡器
- 规模集引用了
image_vmforimageA
可通过两种方式获取示例 playbook:
下载 playbook 并将其保存到
create-vmss.yml
。创建名为
create-vmss.yml
的新文件。 将以下代码插入新文件:
---
- hosts: localhost
vars:
vmss_name: vmsstest
location: eastus
admin_username: vmssadmin
admin_password: User123!!!abc
vm_name: vmforimage
image_name: "image_vmforimageA"
tasks:
- name: Create public IP address
azure_rm_publicipaddress:
resource_group: "{{ resource_group }}"
allocation_method: Static
name: "{{ vmss_name }}"
register: pip_output
- name: Create a load balancer
azure_rm_loadbalancer:
name: "{{ vmss_name }}lb"
location: "{{ location }}"
resource_group: "{{ resource_group }}"
public_ip: "{{ vmss_name }}"
probe_protocol: Tcp
probe_port: 80
probe_interval: 10
probe_fail_count: 3
protocol: Tcp
load_distribution: Default
frontend_port: 80
backend_port: 80
idle_timeout: 4
natpool_frontend_port_start: 50000
natpool_frontend_port_end: 50040
natpool_backend_port: 22
natpool_protocol: Tcp
- name: Create a scale set
azure_rm_virtualmachinescaleset:
resource_group: "{{ resource_group }}"
name: "{{ vmss_name }}"
vm_size: Standard_DS1_v2
admin_username: "{{ admin_username }}"
admin_password: "{{ admin_password }}"
ssh_password_enabled: true
capacity: 2
virtual_network_name: "{{ vm_name }}"
subnet_name: "{{ vm_name }}"
upgrade_policy: Manual
tier: Standard
managed_disk_type: Standard_LRS
os_disk_caching: ReadWrite
image:
name: "{{ image_name }}"
resource_group: "{{ resource_group }}"
load_balancer: "{{ vmss_name }}lb"
- debug:
msg: "Scale set public IP address: {{ pip_output.state.ip_address }}"
使用 ansible-playbook
命令运行 playbook,用资源组名称替换 myrg
:
ansible-playbook create-vmss.yml --extra-vars "resource_group=myrg"
由于 playbook 的 debug
部分,ansible-playbook
命令将输出规模集的 IP 地址。 请复制此 IP 地址供稍后使用。
连接到规模集
使用上一节中的 IP 地址,连接到规模集。
如前一部分所述,字符串 Image A
和 Image B
模拟具有不同配置的两个不同 VM。
规模集引用名为 image_vmforimageA
的自定义映像。 自定义映像 image_vmforimageA
是从主页显示 Image A
的 VM 中创建的。
因此,你会看到一个显示 Image A
的主页。
在继续进行下一部分时,请保持浏览器窗口处于打开状态。
更改规模集和升级实例中的自定义映像
本部分中的 playbook 代码会更改规模集的映像 - 从 image_vmforimageA
更改为 image_vmforimageB
。 此外,还会更新由规模集部署的所有当前虚拟机。
可通过两种方式获取示例 playbook:
下载 playbook 并将其保存到
update-vmss-image.yml
。创建名为
update-vmss-image.yml
的新文件。 将以下代码插入新文件:
- name: Update scale set image reference
hosts: localhost
connection: local
vars:
vmss_name: vmsstest
image_name: image_vmforimageB
admin_username: vmssadmin
admin_password: User123!!!abc
tasks:
- name: Update scale set - second image
azure_rm_virtualmachinescaleset:
resource_group: "{{ resource_group }}"
name: "{{ vmss_name }}"
vm_size: Standard_DS1_v2
admin_username: "{{ admin_username }}"
admin_password: "{{ admin_password }}"
ssh_password_enabled: true
capacity: 3
virtual_network_name: "{{ vmss_name }}"
subnet_name: "{{ vmss_name }}"
upgrade_policy: Manual
tier: Standard
managed_disk_type: Standard_LRS
os_disk_caching: ReadWrite
image:
name: "{{ image_name }}"
resource_group: "{{ resource_group }}"
load_balancer: "{{ vmss_name }}lb"
- name: List all of the instances
azure_rm_virtualmachinescalesetinstance_facts:
resource_group: "{{ resource_group }}"
vmss_name: "{{ vmss_name }}"
register: instances
- debug:
var: instances
- name: manually upgrade all the instances
azure_rm_virtualmachinescalesetinstance:
resource_group: "{{ resource_group }}"
vmss_name: "{{ vmss_name }}"
instance_id: "{{ item.instance_id }}"
latest_model: yes
with_items: "{{ instances.instances }}"
使用 ansible-playbook
命令运行 playbook,用资源组名称替换 myrg
:
ansible-playbook update-vmss-image.yml --extra-vars "resource_group=myrg"
返回到浏览器并刷新页面,查看虚拟机的基础自定义映像是否已更新。
清理资源
将以下代码另存为
delete_rg.yml
。--- - hosts: localhost tasks: - name: Deleting resource group - "{{ name }}" azure_rm_resourcegroup: name: "{{ name }}" state: absent register: rg - debug: var: rg
使用 ansible-playbook 命令运行 playbook。 将占位符替换为要删除的资源组的名称。 将删除资源组内的所有资源。
ansible-playbook delete_rg.yml --extra-vars "name=<resource_group>"
要点:
- 由于 playbook 的
register
变量和debug
部分,因此在命令完成时,将显示结果。
- 由于 playbook 的