VM 应用程序是 Azure 计算库中的资源类型,可简化虚拟机应用程序的管理、共享和全局分发。
详细了解 VM 应用程序
若要在 Azure VM 上创建和部署应用程序,请先打包应用程序并将其作为存储 Blob 上传到 Azure 存储帐户。 然后创建 Azure VM application
引用这些存储 Blob 的资源和资源 VM application version
。 最后,通过传入应用程序引用 applicationProfile
,在任何 VM 或虚拟机规模集上部署应用程序。
先决条件
- 创建 Azure 存储帐户 和 存储容器。 此容器用于上传应用程序文件。 建议使用禁用匿名访问的存储帐户,以增强安全性。
- 创建 用于存储和共享应用程序资源的 Azure 计算库。
打包应用程序
1.打包应用程序文件
- 如果应用程序安装需要单个文件(.exe、.msi、.sh、.ps等),则可以按原样使用它。
- 如果应用程序安装需要多个文件(包含配置文件、依赖项、清单文件、脚本等的可执行文件),则必须将其(使用 .zip、.tar、.tar.gz等)存档到单个文件中。
- 对于微服务应用程序,可以将每个微服务打包并发布为单独的 Azure VM 应用程序。 这有助于使用
order
applicationProfile 中的属性实现应用程序可重用性、跨团队开发和微服务的顺序安装。
2. (可选) 打包应用程序配置文件
- 可以选择单独提供配置文件。 这减少了存档和取消存档应用程序包的开销。 还可以在应用部署期间传递配置文件,以便为每个 VM 启用自定义安装。
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 无法保留原始文件名和文件扩展名。 因此,下载的应用程序文件和配置文件的默认名称为“MyVMApp”和“MyVMApp-config”,没有文件扩展名。 可以使用安装脚本将文件重命名为文件扩展名,也可以传递 packageFileName
configFileName
publishingProfile
然后,Azure 会在下载文件时使用这些名称,而不是默认名称。
(可选) 将应用程序和配置 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 对于存档的应用程序包,在安装应用程序之前,需要将其取消存档。 建议使用 .zip 或.tar,因为大多数 OS 都内置支持取消存档这些格式。 对于其他格式,请确保来宾 OS 提供支持。
(可选) 设置正确的执行策略和权限 取消存档后,可以重置文件权限。 在执行文件之前,最好设置正确的权限。
将脚本转换为字符串安装脚本作为 Azure VM 应用程序版本资源中的install
属性的字符串publishingProfile
传递。
下面是基于应用程序 Blob 文件扩展名的示例安装脚本
#!/bin/bash
# Rename blobs
mv MyVMApp app.tar
mv MyVMApp-config app-config.yaml
# Unarchive application
mkdir -p app
tar -xf app.tar -C app
# Set permissions
chmod -R +x app
chmod -R +r app
# Install the script (example: install.sh with config)
bash ./app/install.sh --config app-config.yaml
# OR Install the .deb package (example: install.deb without config)
# sudo dpkg -i ./app/install.deb
# OR Install the .rpm package (example: install.rpm without config)
# sudo rpm -ivh ./app/install.rpm
以字符串形式编写脚本:
"#!/bin/bash\nmv MyVMApp app.tar\nmv MyVMApp-config app-config.yaml\nmkdir -p app\ntar -xf app.tar -C app\nchmod -R +x app\nchmod -R +r app\nbash ./app/install.sh --config app-config.yaml\n# sudo dpkg -i ./app/install.deb\n# sudo rpm -ivh ./app/install.rpm"
:: Rename blobs
rename MyVMApp app.zip
rename MyVMApp-config app-config.json
:: Unzip using built-in tar (available on Windows 10+)
mkdir app
tar -xf app.zip -C app
:: Install .exe application (example: setup.exe with config)
app\setup.exe /config app-config.json
:: install .msi application (example: setup.exe without config)
:: msiexec /i app\setup.msi /qn /l*v install.log
:: Install JavaScript (example: setup.js with config)
:: cscript //nologo app\setup.js app-config.json
:: Install python script (example: install.py with config) - Needs python pre-installed
:: python app\install.py app-config.json
:: Install ruby application (example: install.rb with config) - Needs Ruby pre-installed
:: ruby app\install.rb app-config.json
以字符串形式编写脚本:
"rename MyVMApp app.zip\r\nrename MyVMApp-config app-config.json\r\nmkdir app\r\ntar -xf app.zip -C app\r\napp\\setup.exe /config app-config.json\r\n:: msiexec /i app\\setup.msi /qn /l*v install.log\r\n:: cscript //nologo app\\setup.js app-config.json\r\n:: python app\\install.py app-config.json\r\n:: ruby app\\install.rb app-config.json"
powershell.exe -command "
# Rename blobs
Rename-Item -Path '.\MyVMApp' -NewName 'app.zip'
Rename-Item -Path '.\MyVMApp-config' -NewName 'app-config.json'
# Unzip application package
Expand-Archive -Path '.\app.zip' -DestinationPath '.\app'
# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
# Install the .exe application (example: setup.exe with config)
Start-Process -FilePath '.\app\setup.exe' -ArgumentList '/config app-config.json' -Wait
# Install PowerShell script (example: setup.ps1 with config)
# powershell.exe -ExecutionPolicy Bypass -File '.\app\setup.ps1' -ConfigFile 'app-config.json'
# Install .msi application (example: setup.msi without config)
# Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i .\app\setup.msi /qn /l*v install.log' -Wait
"
以字符串形式编写脚本:
"powershell.exe -command \"Rename-Item -Path '.\\MyVMApp' -NewName 'app.zip'; Rename-Item -Path '.\\MyVMApp-config' -NewName 'app-config.json'; Expand-Archive -Path '.\\app.zip' -DestinationPath '.\\app'; Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force; Start-Process -FilePath '.\\app\\setup.exe' -ArgumentList '/config app-config.json' -Wait; # powershell.exe -ExecutionPolicy Bypass -File '.\\app\\setup.ps1' -ConfigFile 'app-config.json'; # Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i .\\app\\setup.msi /qn /l*v install.log' -Wait\""
:: Rename blobs
rename MyVMApp app.exe
rename MyVMApp-config app-config.json
:: Run the installer with config
app.exe /config app-config.json
以字符串形式编写脚本:
"rename MyVMApp app.exe\r\nrename MyVMApp-config app-config.json\r\napp.exe /config app-config.json"
powershell.exe -command "
# Rename blobs
Rename-Item -Path '.\MyVMApp' -NewName 'app.exe'
Rename-Item -Path '.\MyVMApp-config' -NewName 'app-config.json'
# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
# Install the .exe application (example: setup.exe with config)
Start-Process -FilePath '.\app.exe' -ArgumentList '/config app-config.json' -Wait
"
以字符串形式编写脚本:
"powershell.exe -command \"Rename-Item -Path '.\\MyVMApp' -NewName 'app.exe'; Rename-Item -Path '.\\MyVMApp-config' -NewName 'app-config.json'; Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force; Start-Process -FilePath '.\\app.exe' -ArgumentList '/config app-config.json' -Wait\""
:: Rename blobs
rename MyVMApp app.msi
rename MyVMApp-config app-config.json
:: install .msi application (example: setup.exe without config)
msiexec /i app.msi /qn /l*v install.log
以字符串形式编写脚本:
"rename MyVMApp app.msi\r\nrename MyVMApp-config app-config.json\r\nmsiexec /i app.msi /qn /l*v install.log"
powershell.exe -command "
# Rename blobs
Rename-Item -Path '.\MyVMApp' -NewName 'app.zip'
Rename-Item -Path '.\MyVMApp-config' -NewName 'app-config.json'
# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
# Install .msi application (example: setup.msi without config)
Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i .\app\setup.msi /qn /l*v install.log' -Wait
"
以字符串形式编写脚本:
"powershell.exe -command \"Rename-Item -Path '.\\MyVMApp' -NewName 'app.zip'; Rename-Item -Path '.\\MyVMApp-config' -NewName 'app-config.json'; Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force; Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i .\\app\\setup.msi /qn /l*v install.log' -Wait\""
#!/bin/bash
# Rename blobs
mv MyVMApp app.deb
mv MyVMApp-config app-config.yaml
# Set permissions
chmod -R +x app.deb
chmod -R +r app.deb
# Install .deb package (example: install.deb without config)
# sudo dpkg -i ./app.deb
以字符串形式编写脚本:
"#!/bin/bash\nmv MyVMApp app.deb\nmv MyVMApp-config app-config.yaml\nchmod -R +x app.deb\nchmod -R +r app.deb\n# sudo dpkg -i ./app.deb"
#!/bin/bash
# Rename blobs
mv MyVMApp app.rpm
mv MyVMApp-config app-config.yaml
# Set permissions
chmod -R +x app.rpm
chmod -R +r app.rpm
# Install .rpm package (example: install.rpm without config)
sudo rpm -ivh ./app.rpm
以字符串形式编写脚本:
"#!/bin/bash\nmv MyVMApp app.rpm\nmv MyVMApp-config app-config.yaml\nchmod -R +x app.rpm\nchmod -R +r app.rpm\nsudo rpm -ivh ./app.rpm"
#!/bin/bash
# Rename blobs
mv MyVMApp app.sh
mv MyVMApp-config app-config.yaml
# Set permissions
chmod -R +x app.sh
chmod -R +r app.sh
# Install the script (example: install.sh with config)
bash ./app.sh --config app-config.yaml
以字符串形式编写脚本:
"#!/bin/bash\nmv MyVMApp app.sh\nmv MyVMApp-config app-config.yaml\nchmod -R +x app.sh\nchmod -R +r app.sh\nbash ./app.sh --config app-config.yaml"
powershell.exe -command "
# Rename blobs
Rename-Item -Path '.\MyVMApp' -NewName 'app.ps1'
Rename-Item -Path '.\MyVMApp-config' -NewName 'app-config.json'
# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
# Install PowerShell script (example: setup.ps1 with config)
powershell.exe -ExecutionPolicy Bypass -File '.\app.ps1' -ConfigFile 'app-config.json'
"
以字符串形式编写脚本:
"powershell.exe -command \"Rename-Item -Path '.\\MyVMApp' -NewName 'app.ps1'; Rename-Item -Path '.\\MyVMApp-config' -NewName 'app-config.json'; Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force; powershell.exe -ExecutionPolicy Bypass -File '.\\app.ps1' -ConfigFile 'app-config.json'\""
4.创建删除脚本
使用删除脚本可以定义应用程序的删除作。 删除脚本以字符串形式提供,最大字符限制为 4,096 个字符。 编写删除命令,假设应用程序包和配置文件位于当前目录中。
删除脚本必须执行的作可能很少。
卸载应用程序: 从 VM 正确卸载应用程序。 例如,在 Windows 或 uninstall.exe
Linux 上执行sudo apt remove app
。
删除残差文件: 从 VM 中删除剩余的应用程序文件。 例如,在 Windows 或 Remove-Item -Path "$PWD\*" -Recurse -Force -ErrorAction SilentlyContinue
Linux 上执行sudo rm -rf ./* ./.??*
。
将应用程序文件上传到 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
$inputFile = <the file you want to pad>
$fileInfo = Get-Item -Path $inputFile
$remainder = $fileInfo.Length % 512
if ($remainder -ne 0){
$difference = 512 - $remainder
$bytesToPad = [System.Byte[]]::CreateInstance([System.Byte], $difference)
Add-Content -Path $inputFile -Value $bytesToPad -Encoding Byte
}
2. 为应用程序包和配置文件生成 SAS URL
将应用程序和配置文件上传到存储帐户后,需要为这些 Blob 生成具有读取权限的 SAS URL 。 然后,在创建 VM 应用程序版本资源时提供这些 SAS URL 作为参考。 对于启用了匿名访问的存储帐户,也可以使用 Blob URL。 但是,建议使用 SAS URL 来提高安全性。 如果你没有 SAS URI,可以使用存储资源管理器快速创建一个。
#!/bin/bash
# === CONFIGURATION ===
STORAGE_ACCOUNT="yourstorageaccount"
CONTAINER_NAME="yourcontainer"
LOCAL_FOLDER="./your-local-folder"
SAS_EXPIRY_HOURS=24
# === 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 FILES ===
az storage blob upload-batch \
--account-name $STORAGE_ACCOUNT \
--destination $CONTAINER_NAME \
--source $LOCAL_FOLDER \
--auth-mode login \
--only-show-errors
# === GENERATE SAS URLs ===
echo "Generating SAS URLs..."
FILES=$(find $LOCAL_FOLDER -type f)
for FILE in $FILES; do
BLOB_NAME="${FILE#$LOCAL_FOLDER/}"
EXPIRY=$(date -u -d "+$SAS_EXPIRY_HOURS hours" '+%Y-%m-%dT%H:%MZ')
SAS_TOKEN=$(az storage blob generate-sas \
--account-name $STORAGE_ACCOUNT \
--container-name $CONTAINER_NAME \
--name "$BLOB_NAME" \
--permissions r \
--expiry $EXPIRY \
--auth-mode login \
-o tsv)
SAS_URL="https://${STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${CONTAINER_NAME}/${BLOB_NAME}?${SAS_TOKEN}"
echo "$BLOB_NAME: $SAS_URL"
done
# === CONFIGURATION ===
$storageAccount = "yourstorageaccount"
$containerName = "yourcontainer"
$localFolder = "C:\path\to\your\local\folder"
$sasExpiryHours = 24
# === LOGIN (if not already logged in) ===
az cloud set -n AzureChinaCloud
az login | Out-Null
# === CREATE CONTAINER IF NOT EXISTS ===
az storage container create `
--name $containerName `
--account-name $storageAccount `
--auth-mode login `
--only-show-errors | Out-Null
# === UPLOAD FILES ===
az storage blob upload-batch `
--account-name $storageAccount `
--destination $containerName `
--source $localFolder `
--auth-mode login `
--only-show-errors
# === GENERATE SAS URLs ===
Write-Host "`nGenerating SAS URLs..."
$files = Get-ChildItem -Recurse -File -Path $localFolder
foreach ($file in $files) {
$relativePath = $file.FullName.Substring($localFolder.Length + 1).Replace("\", "/")
$expiry = (Get-Date).ToUniversalTime().AddHours($sasExpiryHours).ToString("yyyy-MM-ddTHH:mmZ")
$sasToken = az storage blob generate-sas `
--account-name $storageAccount `
--container-name $containerName `
--name $relativePath `
--permissions r `
--expiry $expiry `
--auth-mode login `
-o tsv
$sasUrl = "https://$storageAccount.blob.core.chinacloudapi.cn/$containerName/$relativePath`?$sasToken"
Write-Host "$relativePath:`n$sasUrl`n"
}
创建 VM 应用程序
若要创建 VM 应用程序,请先创建描述应用程序的 VM 应用程序资源。 然后在其中创建 VM 应用程序版本资源,其中包含用于安装、更新和删除应用程序的 VM 应用程序有效负载和脚本。 有效负载使用 SAS URL 提供给 Azure 存储帐户中的 Blob 容器。
请参阅 VM 应用程序和 VM 应用程序版本资源的架构 ,详细了解每个属性。
使用“创建画廊应用程序 API”创建 VM 应用程序定义
PUT
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/galleries/{galleryName}/applications/{applicationName}?api-version=2024-03-03
{
"location": "China North 2",
"name": "myApp",
"properties": {
"supportedOSType": "Windows | Linux",
"endOfLifeDate": "2020-01-01",
"description": "Description of the App",
"eula": "Link to End-User License Agreement (EULA)",
"privacyStatementUri": "Link to privacy statement for the application",
"releaseNoteUri": "Link to release notes for the application"
}
}
使用 “创建库应用程序版本 API”创建 VM 应用程序版本。
PUT
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/galleries/{galleryName}/applications/{applicationName}/versions/{versionName}?api-version=2024-03-03
{
"location": "$location",
"properties": {
"publishingProfile": {
"source": {
"mediaLink": "$mediaLink",
"defaultConfigurationLink": "$configLink"
},
"manageActions": {
"install": "echo installed",
"remove": "echo removed",
"update": "echo update"
},
"targetRegions": [
{
"name": "China North",
"regionalReplicaCount": 1
},
{
"name": "China North"
}
]
"endofLifeDate": "datetime",
"replicaCount": 1,
"excludeFromLatest": false,
"storageAccountType": "PremiumV2_LRS | Premium_LRS | Standard_LRS | Standard_ZRS"
"safetyProfile": {
"allowDeletionOfReplicatedLocations": false
}
"settings": {
"scriptBehaviorAfterReboot": "None | Rerun",
"configFileName": "$appConfigFileName",
"packageFileName": "$appPackageFileName"
}
}
}
VM 应用程序需要 Azure CLI 版本 2.30.0 或更高版本。
使用 “az sig gallery-application create”创建 VM 应用程序定义。 在此示例中,我们将为基于 Linux 的 VM 创建名为 myApp 的 VM 应用程序定义。
az sig gallery-application create \
--application-name myApp \
--gallery-name myGallery \
--resource-group myResourceGroup \
--os-type Linux \
--location "China North 2"
使用 “az sig gallery-application version create”创建 VM 应用程序版本。 允许用于版本的字符为数字和句点。 数字必须在 32 位整数范围内。 格式:MajorVersion.MinorVersion.Patch。
将参数的值替换为你自己的值。
az sig gallery-application version create \
--version-name 1.0.0 \
--application-name myApp \
--gallery-name myGallery \
--location "China North 2" \
--resource-group myResourceGroup \
--package-file-link "https://<storage account name>.blob.core.chinacloudapi.cn/<container name>/<filename>" \
--install-command "mv myApp .\myApp\myApp" \
--remove-command "rm .\myApp\myApp" \
--update-command "mv myApp .\myApp\myApp" \
--default-configuration-file-link "https://<storage account name>.blob.core.chinacloudapi.cn/<container name>/<filename>"\
使用 New-AzGalleryApplication
.. 创建 VM 应用程序定义。 在此示例中,我们将在 myGallery Azure 计算库和 myGallery 资源组中创建名为 myApp 的 Linux 应用。 根据需要替换变量的值。
$galleryName = "myGallery"
$rgName = "myResourceGroup"
$applicationName = "myApp"
$description = "Backend Linux application for finance."
New-AzGalleryApplication `
-ResourceGroupName $rgName `
-GalleryName $galleryName `
-Location "China North 2" `
-Name $applicationName `
-SupportedOSType Linux `
-Description $description
使用 New-AzGalleryApplicationVersion
创建您的 VM 应用程序版本。 允许用于版本的字符为数字和句点。 数字必须在 32 位整数范围内。 格式:MajorVersion.MinorVersion.Patch。
在此示例中,我们将创建版本号 1.0.0。 请根据需要替换变量的值。
$galleryName = "myGallery"
$rgName = "myResourceGroup"
$applicationName = "myApp"
$version = "1.0.0"
New-AzGalleryApplicationVersion `
-ResourceGroupName $rgName `
-GalleryName $galleryName `
-GalleryApplicationName $applicationName `
-Name $version `
-PackageFileLink "https://<storage account name>.blob.core.chinacloudapi.cn/<container name>/<filename>" `
-DefaultConfigFileLink "https://<storage account name>.blob.core.chinacloudapi.cn/<container name>/<filename>" `
-Location "China North 2" `
-Install "mv myApp .\myApp\myApp" `
-Remove "rm .\myApp\myApp" `
- 转到 Azure 门户,然后搜索并选择“Azure Compute Gallery”。
- 从列表中选择需要使用的库。
- 在库页面上,选择页面顶部的“添加”,然后从下拉列表中选择“VM 应用程序定义”。 此时会打开“创建 VM 应用程序定义”页。
- 在“基本信息”选项卡中输入应用程序的名称,然后选择该应用程序是用于运行 Linux 还是 Windows 的 VM。
- 如果要为 VM 应用程序定义指定以下任一可选设置,请选择“ 发布选项 ”选项卡:
- VM 应用程序定义的说明。
- 生命周期终结日期
- 指向最终用户许可协议(EULA)的链接
- 隐私声明的 URI
- 发行说明的 URI
- 完成操作后,选择“查看 + 创建”。
- 验证完成后,选择“创建”以部署定义。
- 部署完成后,选择“转到资源”。
- 在应用程序页面上,选择“创建 VM 应用程序版本”。 此时会打开“创建 VM 应用程序版本”页。
- 输入版本号,例如 1.0.0。
- 选择上传应用程序包的区域。
- 在“源应用程序包”下,选择“浏览”。 选择存储帐户,然后选择包所在的容器。 从列表中选择包,完成后再选择选择。 或者,如果需要,可以将 SAS URI 粘贴到此字段中。
- 提供“安装脚本”。 还可以提供“卸载脚本”和“更新脚本”。 有关如何创建脚本的信息,请参阅概述。
- 如果已将默认配置文件上传到存储帐户,可以在“默认配置”中选择它。
- 如果在创建 VM 时不希望该版本显示为最新版本,请选择“从最新版本中排除”。
- 对于“生命周期结束日期”,选择一个将来的日期来跟踪该版本应该何时停用。 它不会自动删除或移除,仅用于你自己的跟踪。
- 若要将此版本复制到其他区域,请选择“ 复制 ”选项卡,添加更多区域,并更改每个区域的副本数。 创建版本的原始区域必须在列表中,并且不能删除。
- 完成更改后,在页面底部选择“审阅并创建”。
- 当验证结果显示为通过时,选择“创建”来部署你的 VM 应用程序版本。
name: Deploy Azure VM Application
on:
push:
branches:
- main
env:
# Customize your app and config filenames here
APP_FILE: app.exe
CONFIG_FILE: app-config.json
AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }}
AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }}
AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }}
AZURE_CONTAINER_NAME: ${{ secrets.AZURE_CONTAINER_NAME }}
GALLERY_NAME: ${{ secrets.GALLERY_NAME }}
APPLICATION_NAME: ${{ secrets.AZURE_VM_APPLICATION_NAME }}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
# Step 1: Checkout repo
- name: Checkout
uses: actions/checkout@v4
# Step 2: Login to Azure using OIDC
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Step 3: Upload app and config files to Azure Blob
- name: Upload files to Azure Blob Storage
run: |
set -euo pipefail
echo "Creating container if missing..."
az storage container create \
--name "$AZURE_CONTAINER_NAME" \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--auth-mode login \
--only-show-errors
echo "Uploading files..."
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--file "$APP_FILE" \
--auth-mode login \
--only-show-errors
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--file "$CONFIG_FILE" \
--auth-mode login \
--only-show-errors
# Step 4: Create VM Application (if missing)
- name: Create VM Application if missing
run: |
set -euo pipefail
echo "Checking for existing VM Application..."
if ! az sig gallery-application show \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--name "$APPLICATION_NAME" &>/dev/null; then
az sig gallery-application create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--name "$APPLICATION_NAME" \
--location "$AZURE_LOCATION" \
--os-type Windows
fi
# Step 5: Generate SAS URLs
- name: Generate SAS URLs
id: sas
run: |
set -euo pipefail
echo "Generating SAS URLs valid for 24 hours..."
EXPIRY=$(date -u -d "+1 day" '+%Y-%m-%dT%H:%MZ')
APP_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
CONFIG_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
echo "APP_SAS=$APP_SAS" >> $GITHUB_ENV
echo "CONFIG_SAS=$CONFIG_SAS" >> $GITHUB_ENV
# Step 6: Create Application Version using semantic versioning
- name: Create VM Application Version
run: |
set -euo pipefail
# Generate a unique version name
MAJOR=1
MINOR=0
PATCH=$(date +%Y%m%d)
VERSION="$MAJOR.$MINOR.$PATCH"
# Load install/uninstall commands from .txt files as strings
INSTALL_CMD=$(jq -Rs '.' < install-script-as-string.txt)
REMOVE_CMD=$(jq -Rs '.' < uninstall-script-as-string.txt)
PACKAGE_URL="https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${APP_FILE}?${APP_SAS}"
CONFIG_URL="https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${CONFIG_FILE}?${CONFIG_SAS}"
# Create the version
az sig gallery-application version create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--gallery-application-name "$APPLICATION_NAME" \
--gallery-application-version-name "$VERSION" \
--version-name "$VERSION" \
--location "$AZURE_LOCATION" \
--package-file-link "$PACKAGE_URL" \
--default-file-link "$CONFIG_URL" \
--install-command "$INSTALL_CMD" \
--remove-command "$REMOVE_CMD" \
--only-show-errors
stages:
- deploy
variables:
# Customize your filenames here
APP_FILE: "app.exe"
CONFIG_FILE: "app-config.json"
AZURE_RESOURCE_GROUP: "$AZURE_RESOURCE_GROUP"
AZURE_LOCATION: "$AZURE_LOCATION"
AZURE_STORAGE_ACCOUNT: "$AZURE_STORAGE_ACCOUNT"
AZURE_CONTAINER_NAME: "$AZURE_CONTAINER_NAME"
GALLERY_NAME: "$GALLERY_NAME"
APPLICATION_NAME: "$AZURE_VM_APPLICATION_NAME"
deploy_vm_app:
image: mcr.microsoft.com/azure-cli
stage: deploy
only:
- main
script:
# Login to Azure using service principal
- az login --service-principal -u "$AZURE_CLIENT_ID" -p "$AZURE_CLIENT_SECRET" --tenant "$AZURE_TENANT_ID"
- az account set --subscription "$AZURE_SUBSCRIPTION_ID"
# Step 1: Upload app and config files to Blob Storage
- |
echo "Uploading $APP_FILE and $CONFIG_FILE to blob..."
az storage container create \
--name "$AZURE_CONTAINER_NAME" \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--auth-mode login \
--only-show-errors
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--file "$APP_FILE" \
--auth-mode login \
--only-show-errors
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--file "$CONFIG_FILE" \
--auth-mode login \
--only-show-errors
# Step 2: Create VM Application Definition if missing
- |
echo "Checking for existing VM Application..."
if ! az sig gallery-application show \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" &>/dev/null; then
echo "Creating VM Application definition..."
az sig gallery-application create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" \
--location "$AZURE_LOCATION" \
--os-type Windows \
--only-show-errors
else
echo "VM Application already exists."
fi
# Step 3: Generate SAS URLs
- |
EXPIRY=$(date -u -d "+1 day" '+%Y-%m-%dT%H:%MZ')
APP_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
CONFIG_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
# Step 4: Create VM Application Version (semantic version: 1.0.YYYYMMDD)
- |
MAJOR=1
MINOR=0
PATCH=$(date +%Y%m%d)
VERSION="$MAJOR.$MINOR.$PATCH"
echo "Creating VM Application Version: $VERSION"
INSTALL_CMD=$(jq -Rs '.' < install-script-as-string.txt)
REMOVE_CMD=$(jq -Rs '.' < uninstall-script-as-string.txt)
az sig gallery-application version create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" \
--gallery-application-version-name "$VERSION" \
--version-name "$VERSION" \
--location "$AZURE_LOCATION" \
--package-file-link "https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${APP_FILE}?${APP_SAS}" \
--default-configuration-file-link "https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${CONFIG_FILE}?${CONFIG_SAS}" \
--install-command "$INSTALL_CMD" \
--remove-command "$REMOVE_CMD" \
--only-show-errors
pipeline {
agent any
environment {
APP_FILE = 'app.exe'
CONFIG_FILE = 'app-config.json'
AZURE_RESOURCE_GROUP = credentials('AZURE_RESOURCE_GROUP')
AZURE_LOCATION = credentials('AZURE_LOCATION')
AZURE_STORAGE_ACCOUNT = credentials('AZURE_STORAGE_ACCOUNT')
AZURE_CONTAINER_NAME = credentials('AZURE_CONTAINER_NAME')
GALLERY_NAME = credentials('GALLERY_NAME')
APPLICATION_NAME = credentials('AZURE_VM_APPLICATION_NAME')
AZURE_CLIENT_ID = credentials('AZURE_CLIENT_ID')
AZURE_CLIENT_SECRET = credentials('AZURE_CLIENT_SECRET')
AZURE_TENANT_ID = credentials('AZURE_TENANT_ID')
AZURE_SUBSCRIPTION_ID = credentials('AZURE_SUBSCRIPTION_ID')
}
stages {
stage('Login to Azure') {
steps {
sh '''
az login --service-principal \
--username "$AZURE_CLIENT_ID" \
--password "$AZURE_CLIENT_SECRET" \
--tenant "$AZURE_TENANT_ID"
az account set --subscription "$AZURE_SUBSCRIPTION_ID"
'''
}
}
stage('Upload to Blob Storage') {
steps {
sh '''
az storage container create \
--name "$AZURE_CONTAINER_NAME" \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--auth-mode login --only-show-errors
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--file "$APP_FILE" \
--auth-mode login --only-show-errors
az storage blob upload \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--file "$CONFIG_FILE" \
--auth-mode login --only-show-errors
'''
}
}
stage('Create VM Application if Needed') {
steps {
sh '''
if ! az sig gallery-application show \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" &>/dev/null; then
az sig gallery-application create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" \
--location "$AZURE_LOCATION" \
--os-type Windows
fi
'''
}
}
stage('Generate SAS URLs') {
steps {
sh '''
export EXPIRY=$(date -u -d "+1 day" '+%Y-%m-%dT%H:%MZ')
export APP_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$APP_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
export CONFIG_SAS=$(az storage blob generate-sas \
--account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$AZURE_CONTAINER_NAME" \
--name "$CONFIG_FILE" \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login -o tsv)
echo "APP_SAS=$APP_SAS" > sas.env
echo "CONFIG_SAS=$CONFIG_SAS" >> sas.env
'''
}
}
stage('Create Application Version') {
steps {
sh '''
source sas.env
MAJOR=1
MINOR=0
PATCH=$(date +%Y%m%d)
VERSION="$MAJOR.$MINOR.$PATCH"
INSTALL_CMD=$(jq -Rs '.' < install-script-as-string.txt)
REMOVE_CMD=$(jq -Rs '.' < uninstall-script-as-string.txt)
az sig gallery-application version create \
--resource-group "$AZURE_RESOURCE_GROUP" \
--gallery-name "$GALLERY_NAME" \
--application-name "$APPLICATION_NAME" \
--gallery-application-version-name "$VERSION" \
--version-name "$VERSION" \
--location "$AZURE_LOCATION" \
--package-file-link "https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${APP_FILE}?$APP_SAS" \
--default-configuration-file-link "https://${AZURE_STORAGE_ACCOUNT}.blob.core.chinacloudapi.cn/${AZURE_CONTAINER_NAME}/${CONFIG_FILE}?$CONFIG_SAS" \
--install-command "$INSTALL_CMD" \
--remove-command "$REMOVE_CMD" \
--only-show-errors
'''
}
}
}
}
部署 VM 应用
现在可以在 applicationProfile
Azure VM 或 Azure 虚拟机规模集中引用一个或多个 VM 应用程序。 然后,Azure 会拉取 VM 应用程序的有效负载,并使用提供的安装脚本在每个 VM 上安装它。 该 order
属性定义 VM 应用程序在 VM 上安装的顺序。
请参阅 VM/虚拟机规模集 applicationProfile 的架构 ,详细了解每个属性。
若要将 VM 应用程序版本添加到某个 VM,请对该 VM 执行 PUT。
PUT
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{VMName}?api-version=2024-03-03
{
"properties": {
"applicationProfile": {
"galleryApplications": [
{
"order": 1,
"packageReferenceId": "/subscriptions/{subscriptionId}/resourceGroups/<resource group>/providers/Microsoft.Compute/galleries/{gallery name}/applications/{application name}/versions/{version | latest}",
"configurationReference": "{path to configuration storage blob}",
"treatFailureAsDeploymentFailure": false
}
]
}
},
"name": "{vm name}",
"id": "/subscriptions/{subscriptionId}/resourceGroups/{resource group}/providers/Microsoft.Compute/virtualMachines/{vm name}",
"location": "{vm location}"
}
若要将 VM 应用程序应用于统一规模集,请运行以下代码:
PUT
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{VMSSName}?api-version=2024-03-03
{
"properties": {
"virtualMachineProfile": {
"applicationProfile": {
"galleryApplications": [
{
"order": 1,
"packageReferenceId": "/subscriptions/{subscriptionId}/resourceGroups/<resource group>/providers/Microsoft.Compute/galleries/{gallery name}/applications/{application name}/versions/{version | latest}",
"configurationReference": "{path to configuration storage blob}",
"treatFailureAsDeploymentFailure": false
}
]
}
}
},
"name": "{vm name}",
"id": "/subscriptions/{subscriptionId}/resourceGroups/{resource group}/providers/Microsoft.Compute/virtualMachines/{vm name}",
"location": "{vm location}"
}
响应包括完整的 VM 模型。 下面是相关部分。
{
"name": "{vm name}",
"id": "{vm id}",
"type": "Microsoft.Compute/virtualMachines",
"location": "{vm location}",
"properties": {
"applicationProfile": {
"galleryApplications": ""
},
"provisioningState": "Updating"
},
"resources": [
{
"name": "VMAppExtension",
"id": "{extension id}",
"type": "Microsoft.Compute/virtualMachines/extensions",
"location": "chinanorth2",
"properties": "@{autoUpgradeMinorVersion=True; forceUpdateTag=7c4223fc-f4ea-4179-ada8-c8a85a1399f5; provisioningState=Creating; publisher=Microsoft.CPlat.Core; type=VMApplicationManagerLinux; typeHandlerVersion=1.0; settings=}"
}
]
}
使用 “az vm application set” 将 VM 应用程序设置为现有 VM,并将参数的值替换为自己的值。
az vm application set \
--resource-group myResourceGroup \
--name myVM \
--app-version-ids /subscriptions/{subID}/resourceGroups/MyResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp/versions/1.0.0 \
--treat-deployment-as-failure true
若要在 VM 上设置多个应用程序,请执行以下操作:
az vm application set \
--resource-group myResourceGroup \
--name myVM \
--app-version-ids /subscriptions/{subId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp/versions/1.0.0 /subscriptions/{subId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp2/versions/1.0.1 \
--treat-deployment-as-failure true true
若要将应用程序添加到虚拟机规模集,请使用 “az vmss application set”:
az vmss application set \
--resource-group myResourceGroup \
--name myVmss \
--app-version-ids /subscriptions/{subId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp/versions/1.0.0 \
--treat-deployment-as-failure true
将多个应用程序添加到虚拟机规模集:
az vmss application set \
--resource-group myResourceGroup \
--name myVmss
--app-version-ids /subscriptions/{subId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp/versions/1.0.0 /subscriptions/{subId}/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/myGallery/applications/myApp2/versions/1.0.0 \
--treat-deployment-as-failure true
若要将应用程序添加到现有 VM,请获取应用程序版本并使用它来获取 VM 应用程序版本 ID。 使用该 ID 将应用程序添加到 VM 配置。
$galleryName = "myGallery"
$rgName = "myResourceGroup"
$applicationName = "myApp"
$version = "1.0.0"
$vmName = "myVM"
$vm = Get-AzVM -ResourceGroupName $rgname -Name $vmName
$appVersion = Get-AzGalleryApplicationVersion `
-GalleryApplicationName $applicationName `
-GalleryName $galleryName `
-Name $version `
-ResourceGroupName $rgName
$packageId = $appVersion.Id
$app = New-AzVmGalleryApplication -PackageReferenceId $packageId
Add-AzVmGalleryApplication -VM $vm -GalleryApplication $app -TreatFailureAsDeploymentFailure true
Update-AzVM -ResourceGroupName $rgName -VM $vm
将应用程序添加到虚拟机规模集群:
$vmss = Get-AzVmss -ResourceGroupName $rgname -Name $vmssName
$appVersion = Get-AzGalleryApplicationVersion `
-GalleryApplicationName $applicationName `
-GalleryName $galleryName `
-Name $version `
-ResourceGroupName $rgName
$packageId = $appVersion.Id
$app = New-AzVmssGalleryApplication -PackageReferenceId $packageId
Add-AzVmssGalleryApplication -VirtualMachineScaleSetVM $vmss.VirtualMachineProfile -GalleryApplication $app
Update-AzVmss -ResourceGroupName $rgName -VirtualMachineScaleSet $vmss -VMScaleSetName $vmssName
现在,可以使用门户创建一个 VM 并将 VM 应用程序部署到其中。 只需像往常一样创建 VM,然后在“高级”选项卡下,选择“选择要安装的 VM 应用程序” 。
从列表中选择 VM 应用程序,然后选择页面底部的 “保存 ”。
如果要安装多个 VM 应用程序,可以返回“高级”选项卡并设置每个 VM 应用程序的安装顺序。
还可以将 VM 应用程序部署到当前正在运行的 VM。 在门户中查看 VM 详细信息时,在左侧菜单中的“设置”下选择“扩展 + 应用程序”选项。
选择“VM 应用程序”,然后选择“添加应用程序”以添加 VM 应用程序。
从列表中选择 VM 应用程序,然后选择页面底部的 “保存 ”。
后续步骤
详细了解 Azure VM 应用程序。
了解如何 管理、更新或删除 Azure VM 应用程序。