创建具有数据磁盘和多个 NIC 的 VM

本文为 Azure 使用者提供 ARM 模板最佳实践指南,本文通过对一个创建 VM 的实例的讲解,让您对 ARM 模板开发有一个快速的从实践到理论的认识。如果您对 ARM 模板开发并不熟悉,本文比较合适。请注意,文中 “Azure” 作用范围均指中国大陆。

需求

用户可以同过 ARM 模板在 Azure 中创建虚拟机 (VM)。对很多应用系统来说,数据和程序需要分开存放,数据磁盘用来专门存放数据。有多个 NIC 时,可跨各个 NIC 分隔不同的流量类型。同时,数据库对于应用系统来说,一般也是必须的。

这是一个比较基础的用户需求,我们将会基于这个需求设计一个基础模板,并对之进行讲解,力求使您以最快的速度掌握 ARM 模板的开发。

方案

本方案中,虚拟机部署在虚拟网络 (VNet) 中其自己的子网(Subnet)中,Nic有两个,一个用于数据流访问,另一个用于控制流访问,网络安全组 (NSG) 以控制哪些流量允许到达部署中的每个子网和 NIC。下图显示了此方案的基本体系结构:

              DemoVM

模板

本节以上述方案为基础,对实际的案例进行剖析,希望用最快的方式使您掌握 ARM 模板的开发,并能够较快地理解 ARM 模板相关的格式、用法和深入的理解。对于比较全面的 ARM 模板开发学习,请参考 ARM 文档。如下将针对ARM模板的json文件,逐段讲解 ARM 模板的开发。

在开始前,请首先准备一个 Azure 账号,下载 DemoVM ARM模板,并安装和配置 Azure PowerShell

1 模板参数、变量、函数和命名

1.1 ARM 模板了解和部署

请参考 ARM概述了解 Azure 资源管理器模板的结构和语法

ARM模板基于以资源为中心 Azure 云计算平台,一般来说,Azure 上的所有资源都可以用 ARM 模板来部署,同样,也可以用 PowerShell 或 Azure-cli命令来部署。 请在 PowerShell下用如下命令部署一下这个模板,并在 Azure 入口中查看资源组 mytestrg。被部署的 ARM 模板也可以从 Azure 入口中导出,具体请参考 导出 ARM 模板。如果您的订阅资源不够了,请参考 如何申请配额资源

Login-AzureRmAccount -EnvironmentName AzureChinaCloud
new-AzureRmResourceGroup -Name mytestrg -Location "China North"
New-AzureRmResourceGroupDeployment -ResourceGroupName mytestrg -TemplateFile .\DemoVM.json

ARM 模板部署完成后,请浏览器中查询部署是否成功, http://{VmName}-0.chinanorth.cloudapp.chinacloudapi.cn/

1.2 模板参数

请参考 模板参数

模板参数用于标识模板的输入,模板参数也可以写成单独的参数文件。

1.3 参数缺省值和资源函数

请参考 资源管理器模板函数 以及 用于 ARM 模板的资源函数

模板函数是 ARM 模板中经常用到的功能。resourceGroup() 用于获取资源组,是常用的函数,我们建议您使用这个函数而不是类似于“China North”这样的字符串。

1.4 Azure 资源命名约定

请参考 Azure 资源命名约定

Azure资源命名有很多限制,例如Linux的虚拟机名称长度限制为15个字符,比如存储账号不能用大写字符,请特别注意这一点。

1.5 模板变量

请参考 ARM 模板的变量

模板变量 variables 一般在parameters后定义,注意其引用的uniquestring() 函数,它可以生成 哈希值,建议使用这个函数,这样部署的资源名称不会有重复现象。

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",    //1.1 ARM模板
    "contentVersion": "1.0.0.0",
    "parameters": { //1.2 模板参数
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",  //1.3. 参数缺省值和资源函数
            "metadata": {
                "description": "Location for the resources."
            }
        },
        "vmName": {
            "type": "string",
            "defaultValue": "myvm", //1.4 Azure 资源命名约定
            "metadata": {
                "description": "Name of virtual machine"
            }
        },
        ……
    },

    "variables": {      //1.5 模板变量
        "publicIpNamePrefix":  "[concat('PublicIp-', uniquestring(resourceGroup().id))]",
        "nsgName": "[concat('NSG-', uniquestring(resourceGroup().id))]",
        ……
    }

2. ARM 资源基本用法

2.1 ARM Resources 资源

请参考 Azure ARM 模板的 Resources 介绍,以及具体的 Azure 资源列表

ARM 模板开发的主要部分都位于 resources 之内,因此了解这一部分的基本概念以及用法就非常重要。请注意 Azure 资源列表 链接,这里面详细介绍了您可以部署的资源的用法、属性等。另外请注意这个“资源列表”来自于Global,Azure 中国的可能稍有不同,具体请参考 资源提供程序和类型 并通过 PowerShell (PowerShell指令列表)获取实时信息。

2.2 API Version

可参考2.1中的 Azure 资源列表,并通过 PowerShell 获取实时信息,如果需要单纯获取 API Version 信息,可以在 Azure 入口 中的“所有服务->资源浏览器->提供程序”中查看。

请注意不同的 API Version 可能资源的语法格式略有不同,如果您在 Visual Studio中开发,会有相应的提示。

2.3 资源类型

可参考2.1中的 Azure 资源列表

Azure 中的资源类型通过“type”来表示。本例中,“Microsoft.Network”是provider,“publicIPAddresses”是resource type,如何使用公共IP地址资源,可参考 Azure 资源列表中的 Public IP Address

2.4 部署多个资源实例

请参考 copyIndex 部署多个资源实例

ARM 模板可以用copyIndex()方法部署多个资源实例,本例中为2个实例。

"resources": [      //2.1 ARM Resources 资源。
    {
        "apiVersion": "2018-02-01",     //2.2 API Version
        "type": "Microsoft.Network/publicIPAddresses",  //2.3 资源类型
        "name": "[concat(variables('publicIpNamePrefix'), '-', copyIndex())]",  //2.4 部署多个资源实例
        "location": "[parameters('location')]",
        "scale": null,
        ……
        "copy": {
            "name": "publicipcopy",
            "count": 2
        }
    },
    {
        "apiVersion": "2018-02-01",
        "type": "Microsoft.Network/virtualNetworks",
        ……
    }
]

3. 磁盘和存储

3.1 数据磁盘

很多应用需要 VM 附加数据磁盘,这在 ARM 模板中很容易实现,但要注意的是,磁盘虽然可以部署成功,但您需要手动在磁盘上创建文件系统并挂载到操作系统。下面展示了本例中如何在 Linux 中创建文件系统并挂载到操作系统的流程。如果您想自动实现文件系统的创建和挂载,请参考 4.5 采用自定义脚本实现。

    登录到 部署在 Azure上的 Linux VM 上。
    sudo fdisk -l   ;查看所有磁盘
    sudo df -h      ;查看已经挂载到 Linux 的磁盘,找到数据磁盘 /dev/sdc
    sudo mkfs.ext4 /dev/sdc     ;创建数据磁盘的文件系统
    sudo mkdir sdc              ;设置挂载点
    sudo mount /dev/sdc ./sdc   ;挂载数据磁盘的文件系统

     Fdisk

3.2 存储账号

请参考 azure存储简介关于存储账户 以及 存储的服务级别协议(SLA)。关于属性“accountType”,请参考 Azure资源中的"sku object" 以及 存储类型选择

存储账号是 Azure 中一个非常重要的概念和资源,所有的磁盘都必须位于存储账号下,请详细阅读存储账号的相关文档。

{
  "apiVersion": "2018-04-01",
  "type": "Microsoft.Compute/disks", //3.1 数据磁盘
  "copy": {
    "name": "diskCopy",
    "count": 1
  },
  "location": "[parameters('location')]",
  "name": "[variables('vmDataDiskId')]",
  "properties": {
    "creationData": {
      "createOption": "Empty"
    },
    "diskSizeGB": 50
  }
},
{
  "apiVersion": "2015-06-15",
  "type": "Microsoft.Storage/storageAccounts",//3.2 存储账号
  "name": "[parameters('storageAccountName')]",
  "location": "[parameters('location')]",
  "properties": {
    "accountType": "[parameters('storageAccountType')]"
  }
},

4. 虚拟机

4.1 虚拟机大小

请参考 Azure 中 Linux 虚拟机的大小Azure 中 Windows 虚拟机的大小 以及 虚拟机价格

Azure 中国目前在中国大陆有四个云计算中心,请注意并不是每个云计算中心在任何时刻都有所有类型的虚拟机。

4.2 虚拟机镜像

请参考 ARM制作指南 中的“VHD 镜像引用”部分和 Azure镜像

Azure 中国目前有数百个虚拟机镜像,如果您想引用这些镜像,请先查询所有的 Azure镜像,请查看镜像中 artifacts 中的 uri字段,其中的 imageReference 包含了publisher、offer 和 sku。您也可以通过 powershell 查看所有的镜像,比如 Get-AzureRmVMImagePublisher/Get-AzureRmVMImageOffer/Get-AzureRmVMImageSku 等命令。

4.3 资源依赖关系

请参考 ARM 模板中部署资源的顺序

请注意,由于 ARM 模板中的资源之间有先后依赖关系,因此在设计整个架构的时候,就需要仔细定义这种依赖关系。

4.4 ARM 资源迭代

Azure ARM 模板中部署资源或属性的多个实例 中的“子资源的迭代”模块。

资源下面可以包含资源,本案中”extensions”就是典型案例。当然这个“extensions”也可以不作为"Microsoft.Compute/virtualMachines"的子资源存在,但是其type需要修改为“Microsoft.Compute/virtualMachines/extensions”。

4.5 自定义脚本

仔细阅读 在 Linux 虚拟机上使用 Azure 自定义脚本扩展版本 2 中的“扩展架构”部分,也可以参考 通过azure cli动态执行 VM 扩展

虚机启动后可以执行指定脚本,可以灵活配置或部署用户的应用。我们建议将 “fileUris” 指向的脚本存储到Azure 存储中,这样网络状况会更好一些,注意 “fileUris” 必须是可访问的。

本案例中的自定义脚本部署了 LAMP 架构,但并没有 实际的应用实例访问 Mysql 数据库,如果您需要安装应用并配置应用程序的数据库参数,请到 Azure 入口 资源组中查询部署的 Mysql 数据库的参数。

{
  "apiVersion": "2017-12-01",
  "type": "Microsoft.Compute/virtualMachines",
  "name": "[parameters('vmName')]",
  "location": "[parameters('location')]",
  "tags": { },
  "scale": null,
  "properties": {
    "hardwareProfile": {
      "vmSize": "[parameters('vmSize')]" //4.1 虚拟机大小
    },
    "storageProfile": {
      "imageReference": {   //4.2 虚拟机镜像
        "publisher": "Canonical",
        "offer": "UbuntuServer",
        "sku": "[parameters('ubuntuOSVersion')]",
        "version": "latest"
      },
      ……
    },
    ……
  },
  "dependsOn": [        //4.3 资源依赖关系
    "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
    "[resourceId('Microsoft.Compute/disks',variables('vmDataDiskId'))]",
    ……
  ],
  "resources": [        //4.4 ARM 资源迭代
    {
      "type": "extensions", //4.5 自定义脚本     
      "name": "[concat(parameters('vmName'), '_configScript')]",
          ……
      "properties": {
        ……
        "settings": {
          "fileUris": [
            "[concat(parameters('_artifactsLocation'), '/scripts/prepwebserver.sh', parameters('_artifactsLocationSasToken'))]"
          ],
          "commandToExecute": "[concat('sh prepwebserver.sh', ' ', 'True', ' ', variables('singleQuote'), variables('testPageMarkup'), variables('singleQuote'), ' ', 'index.html', ' ', variables('singleQuote'), parameters('ubuntuOSVersion'), variables('singleQuote'))]"
        },
        "protectedSettings": { }
      }
    }
  ]
}