Compartir a través de

创建和部署 VM 应用程序

VM 应用程序是Azure计算库中的资源类型,可简化虚拟机的应用程序和脚本的发布、部署、管理、共享和全局分发。 VM 应用程序支持各种方案,包括大规模部署、低延迟要求、故障复原、安全受信任的推出、车队范围的一致性、微服务体系结构和部署后管理。 详细了解 VM 应用程序

若要在 Azure VM 上创建和部署应用程序,请先打包应用程序并将其作为存储 Blob 上传到 Azure 存储帐户。 然后创建引用这些存储 Blob 的 Azure VM application 资源和 VM application version 资源。 Azure在区域托管存储帐户中存储和复制这些 Blob。 最后,通过在 applicationProfile 中传递应用程序引用,在任何 VM 或Virtual Machine Scale Sets上部署应用程序。

显示发布和部署 VM 应用程序所涉及的分步过程的关系图。

先决条件

  1. 创建 Azure 存储帐户存储容器。 此容器用于上传应用程序文件。
  2. 创建 用于存储和共享应用程序资源的 Azure 计算库
  3. 创建 用户分配的托管标识分配给 Azure 计算资源库

注释

创建禁用匿名访问的存储帐户以提高安全性。 使用分配给 Azure 计算库的 托管标识安全地发布 VM 应用程序,而无需使用 SAS 令牌。

打包应用程序

在 Azure 计算库中发布 VM 应用程序资源之前,请将应用程序文件打包为上传到 Azure Storage 帐户。 下图显示了用于组织应用程序文件的可能文件夹结构:

显示用于上传和创建 VM 应用程序的文件夹结构的屏幕截图。

1.打包应用程序文件

VM 应用程序需要单个文件作为应用程序包。

  • 如果应用程序安装需要单个可执行文件(.exe、.msi、.sh、.ps等),则可以将其用作应用程序包。
  • 如果应用程序需要多个文件(可执行文件、依赖项、脚本、元数据等),请将它们存档到单个文件(.zip.tar.tar.gz)中,并将该文件用作应用程序包。
  • 对于微服务应用程序,可以将每个微服务打包并发布为单独的 Azure VM 应用程序。 这种模块化有助于使用 orderapplicationProfile 中的属性实现应用程序可重用性、跨团队开发和微服务的顺序安装。

详细了解 为 Azure VM 应用程序创建应用程序包

2. (可选) 打包应用程序配置文件

  • 您可以使用defaultConfigurationLink在 VM 应用程序的publishingProfile中单独提供配置文件。 这减少了存档和取消存档应用程序包的开销。
  • 还可以在应用部署期间使用 configurationReference VM 中的 applicationProfile 属性传递配置文件,以便为每个 VM 启用自定义安装。
  • 如果设置了这两个属性,configurationReference 将覆盖通过 defaultConfigurationLink 传递的配置。

3.创建安装脚本

在 VM 上下载应用程序和配置 Blob 后,Azure 会执行提供的安装脚本来安装应用程序。 安装脚本以字符串形式提供 ,最大字符限制为 4,096 个字符。 如果应用程序包和配置文件位于当前目录中,则应编写安装命令。

安装脚本中可能需要执行少量操作。

  • (可选) 使用正确的命令解释器 Azure 使用的默认命令解释器位于 /bin/bash Linux OS 和 cmd.exe Windows OS 上。 如果计算机上安装了其他解释器(如 Chocolatey 或 PowerShell),可以使用其他解释器。 调用可执行文件并向其传递命令。 例如,powershell.exe -command '<powershell command>'。 如果使用的是 PowerShell,需要使用 3.11.0 版的 Az.Storage 模块。

  • (可选) 重命名应用程序 blob 和配置 blob Azure无法保留原始 Blob 名称和文件扩展名。 Azure下载应用程序文件,其中包含在Azure计算库上发布的 VM 应用程序资源的名称。 因此,如果 VM 应用的名称为“VMApp1”,则 VM 上下载的文件的名称为“VMApp1”,没有扩展名。 配置文件的默认名称为“VMApp1-config”,没有文件扩展名。

    您可以使用安装脚本将文件重命名,包括文件扩展名,或者您也可以在 VM 应用程序版本资源的 中传递名称到 属性中。 Azure下载文件时使用这些名称而不是默认名称。

  • 解除存档应用程序 Blob 对于已存档的应用程序包,在安装应用程序之前,需要将其解除存档。 使用 .zip 或.tar,因为大多数 OS 都内置支持取消存档这些格式。 对于其他格式,请确保客户操作系统已提供支持。

  • 将脚本转换为字符串 在 Azure VM 应用程序版本资源的publishingProfile中,安装脚本作为install属性的字符串进行传递。

  • 以静默和同步方式安装 在没有任何用户界面提示的情况下悄悄或无提示地安装应用程序。 由于安装在用户输入不可用的虚拟机(VM)上运行,您必须使用无提示或无人参与安装参数。 此外,如果应用程序安装程序生成后台或子进程或异步运行,则父脚本可能会在安装完成之前返回。 若要确保在Azure将操作标记为成功之前完成整个安装,请使用等待标志或阻止命令。 当安装涉及异步运行的多个步骤时,或者需要在后续应用程序 order 开始之前验证安装完成情况时,此方法会有所帮助。

    常见示例包括:

    • Windows .msistart /wait msiexec /i app.msi /quiet /qn
    • Windows .exestart /wait app.exe /silentstart /wait app.exe /quietstart /wait app.exe /S
    • PowerShell:Start-Process -FilePath '.\app.exe' -ArgumentList '/quiet' -Wait
    • Linux .sh./install.sh --silent (默认情况下,shell 脚本同步运行)
    • Linux .debsudo DEBIAN_FRONTEND=noninteractive dpkg -i app.deb (默认情况下同步运行)
    • Linux .rpmsudo rpm -ivh app.rpm (默认情况下同步运行)
  • (可选) 设置正确的执行策略和权限 取消存档后,可以重置文件权限。 在执行文件之前,最好设置正确的权限。

  • (可选) 设置详细执行和日志记录 VM 应用程序会自动捕获安装脚本的任何输出,并将其存储在 VM 上的 stdout 文件中。 使用此功能可以记录安装进度、调试失败并验证部署是否成功。

    • 启用详细模式 以记录应用程序安装程序日志。 Bash: set -x、PowerShell: $VerbosePreference='Continue'、Cmd: @echo on
    • 添加自定义消息 以跟踪脚本进度。 Bash: echo "Install started"、PowerShell: Write-Output "Install started"、Cmd: echo Install started

    可以使用 “运行命令” 或连接到 VM 来检索这些日志。 文件stdoutstderr文件位于以下路径

    • Linux:/var/lib/waagent/Microsoft.CPlat.Core.VMApplicationManagerLinux/<application name>/<application version>/
    • Windows: C:\Packages\Plugins\Microsoft.CPlat.Core.VMApplicationManagerWindows\1.0.9\Downloads\<application name>\<application version>/
  • (可选) 将应用程序和配置 blob 移动到适当的位置 Azure 将应用程序 Blob 和配置 Blob 下载到以下位置。 安装脚本必须在必要时将文件移动到适当的位置。

    Linux:/var/lib/waagent/Microsoft.CPlat.Core.VMApplicationManagerLinux/<application name>/<application version>

    Windows: C:\Packages\Plugins\Microsoft.CPlat.Core.VMApplicationManagerWindows\1.0.16\Downloads\<application name>\<application version>

下面是几个基于应用程序 Blob 文件扩展名的示例安装脚本。 如果在publishingProfile中以文件名和扩展名形式提供packageFileNameconfigFileName 属性,请删除以下脚本中用于重命名文件的代码。

VMApp1 替换为应用程序包(VMApp1)和配置文件(VMApp1-config)中的 VM 应用名称。 将 app.tar 替换为所需的应用名称。 替换 config.yaml 为您所需的配置名称。

在不使用配置文件的情况下使用 bash 进行安装:

mv VMApp1 app.tar && tar -xf app.tar && chmod -R +xr . && bash ./install.sh
mv VMApp1 app.tar && tar -xf app.tar && chmod -R +xr . && sudo DEBIAN_FRONTEND=noninteractive dpkg -i ./app.deb

通过配置文件使用 bash 进行安装:

mv VMApp1 app.tar && mv VMApp1-config config.yaml && tar -xf app.tar && chmod -R +xr . && bash ./install.sh --config config.yaml

如果配置文件打包在 tar 文件中

mv VMApp1 app.tar && tar -xf app.tar && chmod -R +xr . && sudo debconf-set-selections < config.cfg && sudo DEBIAN_FRONTEND=noninteractive dpkg -i ./app.deb

4.创建删除脚本

通过该 remove 脚本,可以定义用于删除应用程序的操作。 该 remove 脚本以字符串形式提供,最大字符限制为 4,096 个字符。 编写假设应用程序包和配置文件位于当前目录中的命令。 在卸载作期间,Azure运行卸载脚本,然后从存储库中删除所有文件。

脚本必须执行的操作很少 remove

  • 卸载应用程序: 从 VM 正确卸载应用程序。 例如:

Windows上的 CMD:

start /wait uninstall.exe /quiet
start /wait python.exe /uninstall /quiet
start /wait msiexec /x app.msi /quiet /qn
  • 删除残差文件: 如果应用程序安装在文件系统的其他部分创建文件,请删除这些文件。

将应用程序文件上传到 Azure 存储帐户

重要

在上传文件或生成 SAS URL 之前,请将角色分配给对目标存储帐户或容器执行这些作的用户、服务主体或托管标识:

  • 存储 Blob 数据贡献者或存储 Blob 数据所有者:所需权限用于上传、修改或删除 Blob。
  • 若要使用 Azure AD 生成 SAS(例如,在 CLI/PowerShell 中使用 --auth-mode 登录名),请在存储帐户范围内分配存储 Blob 委派器。

1. 将您的应用程序和配置文件上传到位于Azure存储帐户中的容器

将打包的应用程序和可选配置文件作为 Blob 上传。 块 Blob 适用于大多数方案。

(可选)如果需要使用页 Blob,请在使用以下脚本之一上传之前对文件进行字节对齐:

inputFile="<the file you want to pad>"

# Get the file size
fileSize=$(stat -c %s "$inputFile")

# Calculate the remainder when divided by 512
remainder=$((fileSize % 512))

if [ "$remainder" -ne 0 ]; then
  # Calculate how many bytes to pad
  difference=$((512 - remainder))
  
  # Create padding (empty bytes)
  dd if=/dev/zero bs=1 count=$difference >> "$inputFile"
fi

2. 为应用程序包和配置文件生成 SAS 或 Blob URL

将应用程序和配置文件上传到存储帐户后,需要为这些 Blob 生成具有读取权限的 Blob URL 或 SAS URL 。 然后,在创建 VM 应用程序版本资源时提供这些 URL 作为参考。

注释

强烈建议将 Blob URL 与托管标识配合使用,以提高安全性和隐私性。

使用 Blob URL:

使用 SAS URL:

  • 如果存储帐户被禁用匿名访问,并且未将托管标识分配给 Azure 计算画廊。

Blob URL = https://${STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${CONTAINER_NAME}/${BLOB_NAME} SAS URL = https://${STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${CONTAINER_NAME}/${BLOB_NAME}?${SAS_TOKEN}

如果为 Azure Compute Gallery 分配了托管标识,请使用以下脚本。 此方法使用不使用 SAS 令牌的 Blob URL。

#!/bin/bash
set -euo pipefail

# === CONFIGURATION ===
STORAGE_ACCOUNT="your-storage-account-name"
CONTAINER_NAME="your-container-name"
APP_FILE="./path/to/your-app-file"           # Path to your application payload file        
CONFIG_FILE="./path/to/your-config-file"     # Path to your configuration file (optional)   

# === LOGIN (if not already logged in) ===
az cloud set -n AzureChinaCloud
az login --only-show-errors

# === CREATE CONTAINER IF NOT EXISTS ===
az storage container create \
  --name "$CONTAINER_NAME" \
  --account-name "$STORAGE_ACCOUNT" \
  --auth-mode login \
  --only-show-errors

# === UPLOAD APPLICATION FILE ===
APP_BLOB_NAME=$(basename "$APP_FILE")
az storage blob upload \
  --account-name "$STORAGE_ACCOUNT" \
  --container-name "$CONTAINER_NAME" \
  --name "$APP_BLOB_NAME" \
  --file "$APP_FILE" \
  --auth-mode login \
  --overwrite \
  --only-show-errors

# === UPLOAD CONFIG FILE (optional) ===
if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
  CONFIG_BLOB_NAME=$(basename "$CONFIG_FILE")
  az storage blob upload \
    --account-name "$STORAGE_ACCOUNT" \
    --container-name "$CONTAINER_NAME" \
    --name "$CONFIG_BLOB_NAME" \
    --file "$CONFIG_FILE" \
    --auth-mode login \
    --overwrite \
    --only-show-errors
fi

# === GENERATE BLOB URLs ===
echo "Generating Blob URLs..."

APP_BLOB_URL="https://${STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${CONTAINER_NAME}/${APP_BLOB_NAME}"
echo "Application file: $APP_BLOB_URL"

if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
  CONFIG_BLOB_URL="https://${STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${CONTAINER_NAME}/${CONFIG_BLOB_NAME}"
  echo "Configuration file: $CONFIG_BLOB_URL"
fi

创建 VM 应用程序

若要创建 VM 应用程序,请先创建描述应用程序的 VM 应用程序 资源。 然后在其中创建 VM 应用程序版本 资源,其中包含用于安装、更新和删除应用程序的 VM 应用程序有效负载和脚本。 负载内容使用 Blob URL 或 SAS URL 提供给 Azure 存储帐户中的 blob 容器。

请参阅 VM 应用程序和 VM 应用程序版本资源的架构 ,详细了解每个属性。

使用以下 ARM 模板创建 Azure Compute Gallery、VM 应用程序和 VM 应用程序版本。 此模板演示用于发布应用程序包的关键属性和配置选项。

{
  "$schema": "https://schema.management.azure.com/schemas/2020-06-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "galleryName": {
      "type": "string"
    },
    "applicationName": {
      "type": "string"
    },
    "versionName": {
      "type": "string",
      "metadata": {
        "description": "Must follow the format: major.minor.patch (Example: 1.0.0)"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "China North 2"
    },
    "supportedOSType": {
      "type": "string",
      "allowedValues": ["Windows", "Linux"]
    },
    "endOfLifeDate": {
      "type": "string",
      "metadata": {
        "description": "Optional. This property is for information only and doesn't block app deployment."
      }
    },
    "description": {
      "type": "string",
      "defaultValue": "Description of the application"
    },
    "eula": {
      "type": "string",
      "defaultValue": ""
    },
    "privacyStatementUri": {
      "type": "string",
      "defaultValue": ""
    },
    "releaseNoteUri": {
      "type": "string",
      "defaultValue": ""
    },
    "mediaLink": {
      "type": "string"
    },
    "configLink": {
      "type": "string"
    },
    "appConfigFileName": {
      "type": "string"
    },
    "appPackageFileName": {
      "type": "string"
    },
    "replicaRegion1": {
      "type": "string",
      "defaultValue": "China North"
    },
    "replicaRegion2": {
      "type": "string",
      "defaultValue": "China North"
    },
    "installScript": {
      "type": "string",
      "metadata": {
        "description": "Optional. Script to run to install the application. Example: echo 'Installing application...'"
      }
    },
    "updateScript": {
      "type": "string",
      "metadata": {
        "description": "Optional. Script to run to update the application. Example: echo 'Updating application...'"
      }
    },
    "removeScript": {
      "type": "string",
      "metadata": {
        "description": "Optional. Script to run to delete the application. Example: echo 'Deleting application...'"
      }
    },
    "storageAccountType": {
      "type": "string",
      "allowedValues": ["PremiumV2_LRS", "Premium_LRS", "Standard_LRS", "Standard_ZRS"],
      "defaultValue": "Standard_LRS"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Compute/galleries",
      "apiVersion": "2024-03-03",
      "name": "[parameters('galleryName')]",
      "location": "[parameters('location')]",
      "properties": {
        "identifier": {}
      }
    },
    {
      "type": "Microsoft.Compute/galleries/applications",
      "apiVersion": "2024-03-03",
      "name": "[format('{0}/{1}', parameters('galleryName'), parameters('applicationName'))]",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Compute/galleries', parameters('galleryName'))]"
      ],
      "properties": {
        "supportedOSType": "[parameters('supportedOSType')]",
        "endOfLifeDate": "[parameters('endOfLifeDate')]",
        "description": "[parameters('description')]",
        "eula": "[if(equals(parameters('eula'), ''), json('null'), parameters('eula'))]",
        "privacyStatementUri": "[if(equals(parameters('privacyStatementUri'), ''), json('null'), parameters('privacyStatementUri'))]",
        "releaseNoteUri": "[if(equals(parameters('releaseNoteUri'), ''), json('null'), parameters('releaseNoteUri'))]"
      }
    },
    {
      "type": "Microsoft.Compute/galleries/applications/versions",
      "apiVersion": "2024-03-03",
      "name": "[format('{0}/{1}/{2}', parameters('galleryName'), parameters('applicationName'), parameters('versionName'))]",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('applicationName'))]"
      ],
      "properties": {
        "publishingProfile": {
          "source": {
            "mediaLink": "[parameters('mediaLink')]",
            "defaultConfigurationLink": "[parameters('configLink')]"
          },
          "manageActions": {
            "install": "[parameters('installScript')]",
            "remove": "[parameters('removeScript')]",
            "update": "[parameters('updateScript')]"
          },
          "settings": {
            "scriptBehaviorAfterReboot": "Rerun",
            "configFileName": "[parameters('appConfigFileName')]",
            "packageFileName": "[parameters('appPackageFileName')]"
          },
          "targetRegions": [
            {
              "name": "[parameters('location')]",
              "regionalReplicaCount": 3,
              "storageAccountType": "[parameters('storageAccountType')]"
            },
            {
              "name": "[parameters('replicaRegion1')]",
              "regionalReplicaCount": 1,
              "storageAccountType": "[parameters('storageAccountType')]"
            },
            {
              "name": "[parameters('replicaRegion2')]"
            },
          ],
          "excludeFromLatest": false,
          "replicaCount": 2,
          "storageAccountType": "[parameters('storageAccountType')]"
        },
        "safetyProfile": {
          "allowDeletionOfReplicatedLocations": true
        },
        "endOfLifeDate": "[parameters('endOfLifeDate')]"
      }
    }
  ]
}

部署 VM 应用

可以在 Azure 虚拟机或 Azure 虚拟机规模集的 applicationProfile 中引用一个或多个 VM 应用程序。 然后,Azure 会拉取 VM 应用程序的有效负载,并使用提供的安装脚本在每个 VM 上安装它。 该 order 属性定义 VM 应用程序在 VM 上安装的顺序。

请参阅VM / Virtual Machine Scale Sets 的 applicationProfile 的 schema以了解每个属性的详情。

使用以下 ARM 模板在 Azure VM 或Azure Virtual Machine Scale Sets上部署 VM 应用程序。 此模板演示用于部署 VM 应用程序的关键属性和配置选项。

在虚拟机规模集中部署虚拟机应用程序

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "vmssName": {
      "type": "string"
    },
    "location": {
      "type": "string"
    },
    "subscriptionId": {
      "type": "string"
    },
    "resourceGroupName": {
      "type": "string"
    },
    "galleryName": {
      "type": "string"
    },
    "applicationName1": {
      "type": "string"
    },
    "applicationVersion1": {
      "type": "string",
      "defaultValue": "latest"
    },
    "configurationReference1": {
      "type": "string",
      "metadata": {
        "description": "Optional path to configuration file from Storage Account. Overrides default configuration file."
      }
    },
    "applicationName2": {
      "type": "string"
    },
    "applicationVersion2": {
      "type": "string",
      "defaultValue": "latest"
    }
  },
  "variables": {
    "packageReferenceId1": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/galleries/{2}/applications/{3}/versions/{4}', parameters('subscriptionId'), parameters('resourceGroupName'), parameters('galleryName'), parameters('applicationName1'), parameters('applicationVersion1'))]",
    "packageReferenceId2": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/galleries/{2}/applications/{3}/versions/{4}', parameters('subscriptionId'), parameters('resourceGroupName'), parameters('galleryName'), parameters('applicationName2'), parameters('applicationVersion2'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Compute/virtualMachineScaleSets",
      "apiVersion": "2024-03-03",
      "name": "[parameters('vmssName')]",
      "location": "[parameters('location')]",
      "properties": {
        "virtualMachineProfile": {
          "applicationProfile": {
            "galleryApplications": [
              {
                "order": 1,
                "packageReferenceId": "[variables('packageReferenceId1')]",
                "configurationReference": "[parameters('configurationReference1')]",
                "treatFailureAsDeploymentFailure": true
              },
              {
                "order": 2,
                "packageReferenceId": "[variables('packageReferenceId2')]",
                "treatFailureAsDeploymentFailure": false
              }
            ]
          }
        }
      }
    }
  ]
}

在 Azure VM 上部署 VM 应用程序

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "vmName": {
      "type": "string"
    },
    "location": {
      "type": "string"
    },
    "subscriptionId": {
      "type": "string"
    },
    "resourceGroupName": {
      "type": "string"
    },
    "galleryName": {
      "type": "string"
    },
    "applicationName1": {
      "type": "string"
    },
    "applicationVersion1": {
      "type": "string",
      "defaultValue": "latest"
    },
    "configurationReference1": {
      "type": "string",
      "metadata": {
        "description": "Optional path to configuration blob for application 1"
      }
    },
    "applicationName2": {
      "type": "string"
    },
    "applicationVersion2": {
      "type": "string",
      "defaultValue": "latest"
    }
  },
  "variables": {
    "packageReferenceId1": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/galleries/{2}/applications/{3}/versions/{4}', parameters('subscriptionId'), parameters('resourceGroupName'), parameters('galleryName'), parameters('applicationName1'), parameters('applicationVersion1'))]",
    "packageReferenceId2": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/galleries/{2}/applications/{3}/versions/{4}', parameters('subscriptionId'), parameters('resourceGroupName'), parameters('galleryName'), parameters('applicationName2'), parameters('applicationVersion2'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2024-07-01",
      "name": "[parameters('vmName')]",
      "location": "[parameters('location')]",
      "properties": {
        "applicationProfile": {
          "galleryApplications": [
            {
              "order": 1,
              "packageReferenceId": "[variables('packageReferenceId1')]",
              "configurationReference": "[parameters('configurationReference1')]",
              "treatFailureAsDeploymentFailure": true
            },
            {
              "order": 2,
              "packageReferenceId": "[variables('packageReferenceId2')]",
              "treatFailureAsDeploymentFailure": false
            }
          ]
        }
      }
    }
  ]
}

后续步骤