빠른 시작: Bicep을 사용하여 Windows Virtual Machine Scale Set 만들기

Virtual Machine Scale Set를 사용하면 자동 크기 조정 가상 머신 세트를 배포하고 관리할 수 있습니다. Virtual Machine Scale Set의 VM 수를 수동으로 조정하거나 리소스 사용량(예: CPU, 메모리 요구량 또는 네트워크 트래픽)에 따라 자동으로 크기를 조정하는 규칙을 정의할 수 있습니다. 그러면 Azure 부하 분산 장치에서 Virtual Machine Scale Set의 VM 인스턴스에 트래픽을 분산합니다. 이 빠른 시작에서는 Bicep을 사용하여 Virtual Machine Scale Set를 만들고 샘플 애플리케이션을 배포합니다.

Bicep은 선언적 구문을 사용하여 Azure 리소스를 배포하는 DSL(도메인 특정 언어)입니다. 간결한 구문, 신뢰할 수 있는 형식 안전성 및 코드 다시 사용에 대한 지원을 제공합니다. Bicep은 Azure에서 코드형 인프라 솔루션에 대한 최고의 제작 환경을 제공합니다.

필수 조건

Azure 구독이 없는 경우 시작하기 전에 체험 계정을 만듭니다.

Bicep 파일 검토

이 빠른 시작에서 사용되는 Bicep 파일은 Azure 빠른 시작 템플릿에서 나온 것입니다.

@description('String used as a base for naming resources. Must be 3-61 characters in length and globally unique across Azure. A hash is prepended to this string for some resources, and resource-specific information is appended.')
@minLength(3)
@maxLength(61)
param vmssName string

@description('Size of VMs in the VM Scale Set.')
param vmSku string = 'Standard_D2s_v3'

@description('The Windows version for the VM. This will pick a fully patched image of this given Windows version. Allowed values: 2008-R2-SP1, 2012-Datacenter, 2012-R2-Datacenter & 2016-Datacenter, 2019-Datacenter.')
@allowed([
  '2019-DataCenter-GenSecond'
  '2016-DataCenter-GenSecond'
  '2022-datacenter-azure-edition'
])
param windowsOSVersion string = '2022-datacenter-azure-edition'

@description('Security Type of the Virtual Machine.')
@allowed([
  'Standard'
  'TrustedLaunch'
])
param securityType string = 'TrustedLaunch'

@description('Number of VM instances (100 or less).')
@minValue(1)
@maxValue(100)
param instanceCount int = 3

@description('When true this limits the scale set to a single placement group, of max size 100 virtual machines. NOTE: If singlePlacementGroup is true, it may be modified to false. However, if singlePlacementGroup is false, it may not be modified to true.')
param singlePlacementGroup bool = true

@description('Admin username on all VMs.')
param adminUsername string = 'vmssadmin'

@description('Admin password on all VMs.')
@secure()
param adminPassword string

@description('The base URI where artifacts required by this template are located. For example, if stored on a public GitHub repo, you\'d use the following URI: https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/201-vmss-windows-webapp-dsc-autoscale/.')
param _artifactsLocation string = deployment().properties.templateLink.uri

@description('The sasToken required to access _artifactsLocation.  If your artifacts are stored on a public repo or public storage account you can leave this blank.')
@secure()
param _artifactsLocationSasToken string = ''

@description('Location of the PowerShell DSC zip file relative to the URI specified in the _artifactsLocation, i.e. DSC/IISInstall.ps1.zip')
param powershelldscZip string = 'DSC/InstallIIS.zip'

@description('Location of the  of the WebDeploy package zip file relative to the URI specified in _artifactsLocation, i.e. WebDeploy/DefaultASPWebApp.v1.0.zip')
param webDeployPackage string = 'WebDeploy/DefaultASPWebApp.v1.0.zip'

@description('Version number of the DSC deployment. Changing this value on subsequent deployments will trigger the extension to run.')
param powershelldscUpdateTagVersion string = '1.0'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('Fault Domain count for each placement group.')
param platformFaultDomainCount int = 1

var vmScaleSetName = toLower(substring('vmssName${uniqueString(resourceGroup().id)}', 0, 9))
var longvmScaleSet = toLower(vmssName)
var addressPrefix = '10.0.0.0/16'
var subnetPrefix = '10.0.0.0/24'
var vNetName = '${vmScaleSetName}vnet'
var publicIPAddressName = '${vmScaleSetName}pip'
var subnetName = '${vmScaleSetName}subnet'
var loadBalancerName = '${vmScaleSetName}lb'
var publicIPAddressID = publicIPAddress.id
var lbProbeID = resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'tcpProbe')
var natPoolName = '${vmScaleSetName}natpool'
var bePoolName = '${vmScaleSetName}bepool'
var lbPoolID = resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, bePoolName)
var natStartPort = 50000
var natEndPort = 50119
var natBackendPort = 3389
var nicName = '${vmScaleSetName}nic'
var ipConfigName = '${vmScaleSetName}ipconfig'
var frontEndIPConfigID = resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, 'loadBalancerFrontEnd')
var osType = {
  publisher: 'MicrosoftWindowsServer'
  offer: 'WindowsServer'
  sku: windowsOSVersion
  version: 'latest'
}
var securityProfileJson = {
  uefiSettings: {
    secureBootEnabled: true
    vTpmEnabled: true
  }
  securityType: securityType
}
var imageReference = osType
var webDeployPackageFullPath = uri(_artifactsLocation, '${webDeployPackage}${_artifactsLocationSasToken}')
var powershelldscZipFullPath = uri(_artifactsLocation, '${powershelldscZip}${_artifactsLocationSasToken}')

resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = {
  name: loadBalancerName
  location: location
  properties: {
    frontendIPConfigurations: [
      {
        name: 'LoadBalancerFrontEnd'
        properties: {
          publicIPAddress: {
            id: publicIPAddressID
          }
        }
      }
    ]
    backendAddressPools: [
      {
        name: bePoolName
      }
    ]
    inboundNatPools: [
      {
        name: natPoolName
        properties: {
          frontendIPConfiguration: {
            id: frontEndIPConfigID
          }
          protocol: 'Tcp'
          frontendPortRangeStart: natStartPort
          frontendPortRangeEnd: natEndPort
          backendPort: natBackendPort
        }
      }
    ]
    loadBalancingRules: [
      {
        name: 'LBRule'
        properties: {
          frontendIPConfiguration: {
            id: frontEndIPConfigID
          }
          backendAddressPool: {
            id: lbPoolID
          }
          protocol: 'Tcp'
          frontendPort: 80
          backendPort: 80
          enableFloatingIP: false
          idleTimeoutInMinutes: 5
          probe: {
            id: lbProbeID
          }
        }
      }
    ]
    probes: [
      {
        name: 'tcpProbe'
        properties: {
          protocol: 'Tcp'
          port: 80
          intervalInSeconds: 5
          numberOfProbes: 2
        }
      }
    ]
  }
}

resource vmScaleSet 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = {
  name: vmScaleSetName
  location: location
  sku: {
    name: vmSku
    tier: 'Standard'
    capacity: instanceCount
  }
  properties: {
    overprovision: true
    upgradePolicy: {
      mode: 'Automatic'
    }
    singlePlacementGroup: singlePlacementGroup
    platformFaultDomainCount: platformFaultDomainCount
    virtualMachineProfile: {
      storageProfile: {
        osDisk: {
          caching: 'ReadWrite'
          createOption: 'FromImage'
        }
        imageReference: imageReference
      }
      osProfile: {
        computerNamePrefix: vmScaleSetName
        adminUsername: adminUsername
        adminPassword: adminPassword
      }
      securityProfile: ((securityType == 'TrustedLaunch') ? securityProfileJson : null)
      networkProfile: {
        networkInterfaceConfigurations: [
          {
            name: nicName
            properties: {
              primary: true
              ipConfigurations: [
                {
                  name: ipConfigName
                  properties: {
                    subnet: {
                      id: vNet.properties.subnets[0].id
                    }
                    loadBalancerBackendAddressPools: [
                      {
                        id: lbPoolID
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
      extensionProfile: {
        extensions: [
          {
            name: 'Microsoft.Powershell.DSC'
            properties: {
              publisher: 'Microsoft.Powershell'
              type: 'DSC'
              typeHandlerVersion: '2.9'
              autoUpgradeMinorVersion: true
              forceUpdateTag: powershelldscUpdateTagVersion
              settings: {
                configuration: {
                  url: powershelldscZipFullPath
                  script: 'InstallIIS.ps1'
                  function: 'InstallIIS'
                }
                configurationArguments: {
                  nodeName: 'localhost'
                  WebDeployPackagePath: webDeployPackageFullPath
                }
              }
            }
          }
        ]
      }
    }
  }
}

resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
  name: publicIPAddressName
  location: location
  properties: {
    publicIPAllocationMethod: 'Static'
    dnsSettings: {
      domainNameLabel: longvmScaleSet
    }
  }
}

resource vNet 'Microsoft.Network/virtualNetworks@2023-04-01' = {
  name: vNetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        addressPrefix
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: subnetPrefix
        }
      }
    ]
  }
}

resource autoscalehost 'Microsoft.Insights/autoscalesettings@2022-10-01' = {
  name: 'autoscalehost'
  location: location
  properties: {
    name: 'autoscalehost'
    targetResourceUri: vmScaleSet.id
    enabled: true
    profiles: [
      {
        name: 'Profile1'
        capacity: {
          minimum: '1'
          maximum: '10'
          default: '1'
        }
        rules: [
          {
            metricTrigger: {
              metricName: 'Percentage CPU'
              metricResourceUri: vmScaleSet.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT5M'
              timeAggregation: 'Average'
              operator: 'GreaterThan'
              threshold: 50
            }
            scaleAction: {
              direction: 'Increase'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT5M'
            }
          }
          {
            metricTrigger: {
              metricName: 'Percentage CPU'
              metricResourceUri: vmScaleSet.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT5M'
              timeAggregation: 'Average'
              operator: 'LessThan'
              threshold: 30
            }
            scaleAction: {
              direction: 'Decrease'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT5M'
            }
          }
        ]
      }
    ]
  }
}

output applicationUrl string = uri('http://${publicIPAddress.properties.dnsSettings.fqdn}', '/MyApp')

다음 리소스는 Bicep 파일에 정의되어 있습니다.

확장 집합 정의

Bicep 파일을 사용하여 Virtual Machine Scale Set를 만들려면 적절한 리소스를 정의합니다. Virtual Machine Scale Set 리소스 종류의 주요 부분은 다음과 같습니다.

속성 속성 설명 예제 템플릿 값
type 만들 Azure 리소스 종류 Microsoft.Compute/virtualMachineScaleSets
name 확장 집합 이름 myScaleSet
location 확장 집합을 만들 위치 미국 동부
sku.name 각 확장 집합 인스턴스에 대한 VM 크기 Standard_A1
sku.capacity 처음에 만들 VM 인스턴스의 수 2
upgradePolicy.mode 변경 발생 시의 VM 인스턴스 업그레이드 모드 자동
imageReference VM 인스턴스에 사용할 플랫폼 또는 사용자 지정 이미지 Microsoft Windows Server 2016 Datacenter
osProfile.computerNamePrefix 각 VM 인스턴스에 대한 이름 접두사 myvmss
osProfile.adminUsername 각 VM 인스턴스에 대한 사용자 이름 azureuser
osProfile.adminPassword 각 VM 인스턴스에 대한 암호 P@ssw0rd!

Virtual Machine Scale Set Bicep 파일을 사용자 지정하려면 VM 크기 또는 초기 용량을 변경하면 됩니다. 다른 옵션은 다른 플랫폼 또는 사용자 지정 이미지를 사용하는 것입니다.

샘플 애플리케이션 추가

Virtual Machine Scale Set를 테스트하려면 기본 웹 애플리케이션을 설치합니다. Virtual Machine Scale Set를 배포하는 경우 VM 확장에서 배포 후 구성 및 자동화 작업(예: 앱 설치)을 제공할 수 있습니다. 스크립트는 GitHub에서 다운로드하거나 확장 런타임에서 Azure Portal에 제공할 수 있습니다. Virtual Machine Scale Set에 확장을 적용하려면 위의 리소스 예제에 extensionProfile 섹션을 추가합니다. 확장 프로필은 일반적으로 다음 속성을 정의합니다.

  • 확장 형식
  • 확장 게시자
  • 확장 버전
  • 구성 또는 설치 스크립트의 위치
  • VM 인스턴스에서 실행할 명령

Bicep 파일은 PowerShell DSC 확장을 사용하여 IIS에서 실행되는 ASP.NET MVC 앱을 설치합니다.

설치 스크립트는 url에서 정의한 대로 GitHub에서 다운로드됩니다. 그런 다음 확장은 functionScript에 정의된 대로 IISInstall.ps1 스크립트에서 InstallIIS를 실행합니다. ASP.NET 앱 자체는 WebDeployPackagePath에 정의된 대로 GitHub에서도 다운로드되는 웹 배포 패키지로 제공됩니다.

Bicep 파일 배포

  1. Bicep 파일을 main.bicep으로 로컬 컴퓨터에 저장합니다.

  2. Azure CLI 또는 Azure PowerShell을 사용하여 Bicep 파일을 배포합니다.

    az group create --name exampleRG --location eastus
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters vmssName=<vmss-name>
    

    <vmss-name>을 Virtual Machine Scale Set의 이름으로 바꿉니다. 길이는 3~61자여야 하며 Azure 전체에서 전역적으로 고유해야 합니다. adminPassword를 입력하라는 메시지가 표시됩니다.

    참고 항목

    배포가 완료되면 배포에 성공했음을 나타내는 메시지가 표시됩니다. Virtual Machine Scale Set가 생성되고 확장을 적용하여 앱을 구성하는 데 10~15분 정도가 걸릴 수 있습니다.

배포 유효성 검사

작동 중인 Virtual Machine Scale Set를 확인하려면 웹 브라우저에서 샘플 웹 애플리케이션에 액세스합니다. Azure CLI 또는 Azure PowerShell을 사용하여 부하 분산 장치의 공용 IP 주소를 가져옵니다.

az network public-ip show --resource-group exampleRG

웹 브라우저에 부하 분산 장치의 공용 IP 주소를 http://publicIpAddress/MyApp 형식으로 입력합니다. 부하 분산 장치는 다음 예제와 같이 VM 인스턴스 중 하나에 트래픽을 분산합니다.

Running IIS site

리소스 정리

더 이상 필요 없으면 Azure Portal, Azure CLI 또는 Azure PowerShell을 사용하여 리소스 그룹 및 해당 리소스를 제거합니다.

az group delete --name exampleRG

다음 단계

이 빠른 시작에서는 Bicep 파일을 사용하여 Windows Virtual Machine Scale Set를 만들고, PowerShell DSC 확장을 사용하여 VM 인스턴스에 기본 ASP.NET 앱을 설치했습니다. 자세히 알아보려면 Azure Virtual Machine Scale Set를 만들고 관리하는 방법에 대한 자습서로 계속 진행하세요.