教學課程:使用 Ansible 更新 Azure 虛擬機器擴展集的自訂映射

重要

本文中需要 Ansible 2.8 (或更新版本)才能執行範例劇本。

Azure 虛擬機器擴展集 是一項 Azure 功能,可讓您設定一組相同的負載平衡 VM。 擴展集不需要額外的成本,而且是從虛擬機器建置的。 您只需支付基礎計算資源的費用,例如 VM 實例、負載平衡器或受控磁片儲存體。 使用擴展集,可提供管理和自動化層級,以執行和調整您的應用程式。 您可以改為手動建立和管理個別 VM。 不過,使用擴展集有兩個主要優點。 它們內建于 Azure 中,並會自動調整虛擬機器以符合應用程式需求。

部署 VM 之後,您可以使用應用程式所需的軟體來設定 VM。 您可以建立自訂映射,而不是為每個 VM 執行此設定工作。 自訂映射是現有 VM 的快照集,其中包含任何已安裝的軟體。 當您 設定擴展集時,您會指定要用於該擴展集 VM 的映射。 藉由使用自訂映射,每個 VM 實例都會針對您的應用程式進行相同的設定。 有時候,您可能需要更新擴展集的自訂映射。 這項工作是本教學課程的重點。

在本文中,您將學會如何:

  • 使用 HTTPD 設定兩個 VM
  • 從現有的 VM 建立自訂映射
  • 從映射建立擴展集
  • 更新自訂映射

必要條件

  • Azure 訂用帳戶:如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶

設定兩部 VM

本節中的劇本程式碼會建立兩部已安裝 HTTPD 的虛擬機器。

index.html每個 VM 的頁面會顯示測試字串:

  • 第一個 VM 會顯示值 Image A
  • 第二個 VM 會顯示值 Image B

此字串旨在模擬使用不同的軟體來設定每個 VM。

有兩種方式可取得範例劇本:

  • 下載劇本 ,並將它儲存至 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 inteface 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 命令執行劇本,並將 myrg 取代為您的資源組名:

ansible-playbook create-vms.yml --extra-vars "resource_group=myrg"

debug由於劇本的區段, ansible-playbook 命令會列印每個 VM 的 IP 位址。 複製這些 IP 位址以供稍後使用。

Virtual machine IP addresses

連線至兩部 VM

在本節中,您會連線到每個 VM。 如上一節所述,字串 Image AImage B 模擬具有兩個不同組態的 VM。

使用上一節中的 IP 位址,連線到這兩部 VM:

Screenshot from virtual machine A

Screenshot from virtual machine B

從每個 VM 建立映射

此時,您有兩個 VM 具有稍微不同的組態(其 index.html 檔案)。

本節中的劇本程式碼會為每個 VM 建立自訂映射:

  • image_vmforimageA - 針對在其首頁上顯示的 Image A VM 所建立的自訂映射。
  • image_vmforimageB - 針對在其首頁上顯示的 Image B VM 所建立的自訂映射。

有兩種方式可取得範例劇本:

  • 下載劇本 ,並將它儲存至 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 命令執行劇本,並將 myrg 取代為您的資源組名:

ansible-playbook capture-images.yml --extra-vars "resource_group=myrg"

使用映射 A 建立擴展集

本節會使用劇本來設定下列 Azure 資源:

  • 公用 IP 位址
  • 負載平衡器
  • 參考的擴展集 image_vmforimageA

有兩種方式可取得範例劇本:

  • 下載劇本 ,並將它儲存至 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 命令執行劇本,並將 myrg 取代為您的資源組名:

ansible-playbook create-vmss.yml --extra-vars "resource_group=myrg"

debug由於劇本的 區段, ansible-playbook 命令會列印擴展集的 IP 位址。 複製此 IP 位址以供稍後使用。

Public IP Address

連線擴展集

在本節中,您會連線到擴展集。

使用上一節中的 IP 位址,連線到擴展集。

如上一節所述,字串 Image AImage B 模擬具有兩個不同組態的 VM。

擴展集會參考名為 的 image_vmforimageA 自訂映射。 自訂映射 image_vmforimageA 是從其首頁顯示 Image A 的 VM 建立的。

因此,您會看到顯示 的首頁 Image A

The scale set is associated with the first VM.

繼續下一節時,讓瀏覽器視窗保持開啟狀態。

變更擴展集和升級實例中的自訂映射

本節中的劇本程式碼會將擴展集的映射從 image_vmforimageA 變更為 image_vmforimageB 。 此外,擴展集所部署的所有目前虛擬機器都會更新。

有兩種方式可取得範例劇本:

  • 下載劇本 ,並將它儲存至 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 命令執行劇本,並將 myrg 取代為您的資源組名:

ansible-playbook update-vmss-image.yml --extra-vars "resource_group=myrg"

返回瀏覽器並重新整理頁面。

您會看到虛擬機器的基礎自訂映射已更新。

The scale set is associated with the second VM

清除資源

  1. 將下列程式碼儲存為 delete_rg.yml

    ---
    - hosts: localhost
      tasks:
        - name: Deleting resource group - "{{ name }}"
          azure_rm_resourcegroup:
            name: "{{ name }}"
            state: absent
          register: rg
        - debug:
            var: rg
    
  2. 使用 ansible-playbook 命令執行劇本 。 將預留位置取代為要刪除的資源群組名稱。 將會刪除資源群組中的所有資源。

    ansible-playbook delete_rg.yml --extra-vars "name=<resource_group>"
    

    重點︰

    • register由於劇本的變數和 debug 區段,因此命令完成時會顯示結果。

下一步