Azure에서 Packer를 사용하여 Windows 가상 머신 이미지를 만드는 방법How to use Packer to create Windows virtual machine images in Azure

Azure의 각 VM(가상 머신)은 Windows 배포판 및 OS 버전을 정의하는 이미지에서 만들어집니다.Each virtual machine (VM) in Azure is created from an image that defines the Windows distribution and OS version. 이미지는 사전 설치된 응용 프로그램 및 구성을 포함할 수 있습니다.Images can include pre-installed applications and configurations. Azure Marketplace는 가장 일반적인 OS 및 응용 프로그램 환경에 대한 다양한 자사 및 타사 이미지를 제공하거나 사용자 요구에 맞게 사용자 지정 이미지를 만들 수 있습니다.The Azure Marketplace provides many first and third-party images for most common OS' and application environments, or you can create your own custom images tailored to your needs. 이 문서에는 오픈 소스 도구 Packer를 사용하여 Azure에서 사용자 지정 이미지를 정의하고 빌드하는 방법을 자세히 설명합니다.This article details how to use the open-source tool Packer to define and build custom images in Azure.

Azure 리소스 그룹 만들기Create Azure resource group

빌드 프로세스 동안 Packer는 원본 VM을 빌드하므로 임시 Azure 리소스를 만듭니다.During the build process, Packer creates temporary Azure resources as it builds the source VM. 이미지로 사용하기 위해 해당 원본 VM을 캡처하려면 리소스 그룹을 정의해야 합니다.To capture that source VM for use as an image, you must define a resource group. Packer 빌드 프로세스의 출력은 이 리소스 그룹에 저장됩니다.The output from the Packer build process is stored in this resource group.

New-AzureRmResourceGroup을 사용하여 리소스 그룹을 만듭니다.Create a resource group with New-AzureRmResourceGroup. 다음 예제에서는 eastus 위치에 myResourceGroup이라는 리소스 그룹을 만듭니다.The following example creates a resource group named myResourceGroup in the eastus location:

$rgName = "myResourceGroup"
$location = "East US"
New-AzureRmResourceGroup -Name $rgName -Location $location

Azure 자격 증명 만들기Create Azure credentials

Packer는 서비스 사용자를 사용하여 Azure를 인증합니다.Packer authenticates with Azure using a service principal. Azure 서비스 사용자는 앱, 서비스 및 Packer와 같은 자동화 도구를 사용할 수 있는 보안 ID입니다.An Azure service principal is a security identity that you can use with apps, services, and automation tools like Packer. 서비스 주체가 Azure에서 수행할 수 있는 작업에 대한 사용 권한은 사용자가 제어하고 정의합니다.You control and define the permissions as to what operations the service principal can perform in Azure.

New-AzureRmADServicePrincipal을 사용하여 서비스 사용자를 만들고 New-AzureRmRoleAssignment를 사용하여 리소스를 만들고 관리하는 권한을 서비스 사용자에게 할당합니다.Create a service principal with New-AzureRmADServicePrincipal and assign permissions for the service principal to create and manage resources with New-AzureRmRoleAssignment:

$sp = New-AzureRmADServicePrincipal -DisplayName "AzurePacker" `
    -Password (ConvertTo-SecureString "P@ssw0rd!" -AsPlainText -Force)
Sleep 20
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $sp.ApplicationId

Azure를 인증하려면 Get-AzureRmSubscription을 사용하여 Azure 테넌트 및 구독 ID를 가져와야 합니다.To authenticate to Azure, you also need to obtain your Azure tenant and subscription IDs with Get-AzureRmSubscription:

$sub = Get-AzureRmSubscription
$sub.TenantId[0]
$sub.SubscriptionId[0]

다음 단계에서 이러한 두 개의 ID를 사용합니다.You use these two IDs in the next step.

Packer 템플릿 정의Define Packer template

이미지를 작성하려면 JSON 파일로 템플릿을 만듭니다.To build images, you create a template as a JSON file. 템플릿에서 실제 빌드 프로세스를 통해 수행하는 작성기와 프로비저너를 정의합니다.In the template, you define builders and provisioners that carry out the actual build process. Packer에는 이전 단계에서 만든 서비스 사용자 자격 증명과 같은 Azure 리소스를 정의하도록 허용하는 Azure용 작성기가 있습니다.Packer has a builder for Azure that allows you to define Azure resources, such as the service principal credentials created in the preceding step.

windows.json이라는 파일을 만들고 다음 콘텐츠를 붙여 넣습니다.Create a file named windows.json and paste the following content. 다음에 대해 사용자 고유의 값을 입력합니다.Enter your own values for the following:

매개 변수Parameter 얻을 수 있는 위치Where to obtain
client_idclient_id $sp.applicationId를 사용하여 서비스 사용자 ID 보기View service principal ID with $sp.applicationId
client_secretclient_secret $securePassword에서 지정한 암호Password you specified in $securePassword
tenant_idtenant_id $sub.TenantId 명령의 출력Output from $sub.TenantId command
subscription_idsubscription_id $sub.SubscriptionId 명령의 출력Output from $sub.SubscriptionId command
object_idobject_id $sp.Id를 사용하여 서비스 사용자 개체 ID 보기View service principal object ID with $sp.Id
managed_image_resource_group_namemanaged_image_resource_group_name 첫 번째 단계에서 만든 리소스 그룹의 이름Name of resource group you created in the first step
managed_image_namemanaged_image_name 만들어진 관리되는 디스크 이미지의 이름Name for the managed disk image that is created
{
  "builders": [{
    "type": "azure-arm",

    "client_id": "0831b578-8ab6-40b9-a581-9a880a94aab1",
    "client_secret": "P@ssw0rd!",
    "tenant_id": "72f988bf-86f1-41af-91ab-2d7cd011db47",
    "subscription_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
    "object_id": "a7dfb070-0d5b-47ac-b9a5-cf214fff0ae2",

    "managed_image_resource_group_name": "myResourceGroup",
    "managed_image_name": "myPackerImage",

    "os_type": "Windows",
    "image_publisher": "MicrosoftWindowsServer",
    "image_offer": "WindowsServer",
    "image_sku": "2016-Datacenter",

    "communicator": "winrm",
    "winrm_use_ssl": "true",
    "winrm_insecure": "true",
    "winrm_timeout": "3m",
    "winrm_username": "packer",

    "azure_tags": {
        "dept": "Engineering",
        "task": "Image deployment"
    },

    "location": "East US",
    "vm_size": "Standard_DS2_v2"
  }],
  "provisioners": [{
    "type": "powershell",
    "inline": [
      "Add-WindowsFeature Web-Server",
      "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
      "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }]
}

이 템플릿은 Windows Server 2016 VM을 빌드하고 IIS를 설치한 다음 Sysprep을 사용하여 VM을 일반화합니다.This template builds a Windows Server 2016 VM, installs IIS, then generalizes the VM with Sysprep. IIS 설치는 PowerShell 프로비저너를 사용하여 추가 명령을 실행하는 방법을 보여줍니다.The IIS install shows how you can use the PowerShell provisioner to run additional commands. 그런 다음, 최종 Packer 이미지에는 필요한 소프트웨어 설치 및 구성이 포함됩니다.The final Packer image then includes the required software install and configuration.

Packer 이미지 작성Build Packer image

로컬 컴퓨터에 Packer를 아직 설치하지 않은 경우 Packer 설치 지침을 따릅니다.If you don't already have Packer installed on your local machine, follow the Packer installation instructions.

다음과 같이 Packer 템플릿 파일을 지정하여 이미지를 작성합니다.Build the image by specifying your Packer template file as follows:

./packer build windows.json

이전 명령에서 출력의 예는 다음과 같습니다.An example of the output from the preceding commands is as follows:

azure-arm output will be in this color.

==> azure-arm: Running builder ...
    azure-arm: Creating Azure Resource Manager (ARM) client ...
==> azure-arm: Creating resource group ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> Location          : ‘East US’
==> azure-arm:  -> Tags              :
==> azure-arm:  ->> task : Image deployment
==> azure-arm:  ->> dept : Engineering
==> azure-arm: Validating deployment template ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> DeploymentName    : ‘pkrdppq0mthtbtt’
==> azure-arm: Deploying deployment template ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> DeploymentName    : ‘pkrdppq0mthtbtt’
==> azure-arm: Getting the certificate’s URL ...
==> azure-arm:  -> Key Vault Name        : ‘pkrkvpq0mthtbtt’
==> azure-arm:  -> Key Vault Secret Name : ‘packerKeyVaultSecret’
==> azure-arm:  -> Certificate URL       : ‘https://pkrkvpq0mthtbtt.vault.azure.net/secrets/packerKeyVaultSecret/8c7bd823e4fa44e1abb747636128adbb'
==> azure-arm: Setting the certificate’s URL ...
==> azure-arm: Validating deployment template ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> DeploymentName    : ‘pkrdppq0mthtbtt’
==> azure-arm: Deploying deployment template ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> DeploymentName    : ‘pkrdppq0mthtbtt’
==> azure-arm: Getting the VM’s IP address ...
==> azure-arm:  -> ResourceGroupName   : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> PublicIPAddressName : ‘packerPublicIP’
==> azure-arm:  -> NicName             : ‘packerNic’
==> azure-arm:  -> Network Connection  : ‘PublicEndpoint’
==> azure-arm:  -> IP Address          : ‘40.76.55.35’
==> azure-arm: Waiting for WinRM to become available...
==> azure-arm: Connected to WinRM!
==> azure-arm: Provisioning with Powershell...
==> azure-arm: Provisioning with shell script: /var/folders/h1/ymh5bdx15wgdn5hvgj1wc0zh0000gn/T/packer-powershell-provisioner902510110
    azure-arm: #< CLIXML
    azure-arm:
    azure-arm: Success Restart Needed Exit Code      Feature Result
    azure-arm: ------- -------------- ---------      --------------
    azure-arm: True    No             Success        {Common HTTP Features, Default Document, D...
    azure-arm: <Objs Version=“1.1.0.1” xmlns=“http://schemas.microsoft.com/powershell/2004/04"><Obj S=“progress” RefId=“0"><TN RefId=“0”><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N=“SourceId”>1</I64><PR N=“Record”><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>
==> azure-arm: Querying the machine’s properties ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> ComputeName       : ‘pkrvmpq0mthtbtt’
==> azure-arm:  -> Managed OS Disk   : ‘/subscriptions/guid/resourceGroups/packer-Resource-Group-pq0mthtbtt/providers/Microsoft.Compute/disks/osdisk’
==> azure-arm: Powering off machine ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> ComputeName       : ‘pkrvmpq0mthtbtt’
==> azure-arm: Capturing image ...
==> azure-arm:  -> Compute ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm:  -> Compute Name              : ‘pkrvmpq0mthtbtt’
==> azure-arm:  -> Compute Location          : ‘East US’
==> azure-arm:  -> Image ResourceGroupName   : ‘myResourceGroup’
==> azure-arm:  -> Image Name                : ‘myPackerImage’
==> azure-arm:  -> Image Location            : ‘eastus’
==> azure-arm: Deleting resource group ...
==> azure-arm:  -> ResourceGroupName : ‘packer-Resource-Group-pq0mthtbtt’
==> azure-arm: Deleting the temporary OS disk ...
==> azure-arm:  -> OS Disk : skipping, managed disk was used...
Build ‘azure-arm’ finished.

==> Builds finished. The artifacts of successful builds are:
--> azure-arm: Azure.ResourceManagement.VMImage:

ManagedImageResourceGroupName: myResourceGroup
ManagedImageName: myPackerImage
ManagedImageLocation: eastus

Packer가 VM을 빌드하고 프로비저너를 실행하고 배포를 정리하는 데 몇 분 정도 걸립니다.It takes a few minutes for Packer to build the VM, run the provisioners, and clean up the deployment.

Packer 이미지에서 VM 만들기Create a VM from the Packer image

이제 New-AzureRmVM을 사용하여 이미지에서 VM을 만들 수 있습니다.You can now create a VM from your Image with New-AzureRmVM. 아직 없는 경우 지원 네트워크 리소스가 만들어집니다.The supporting network resources are created if they do not already exist. 메시지가 표시되면 VM에 만들어질 관리 사용자 이름 및 암호를 입력합니다.When prompted, enter an administrative username and password to be created on the VM. 다음 예제에서는 myPackerImage에서 myVM이라는 VM을 만듭니다.The following example creates a VM named myVM from myPackerImage:

New-AzureRmVm `
    -ResourceGroupName $rgName `
    -Name "myVM" `
    -Location $location `
    -VirtualNetworkName "myVnet" `
    -SubnetName "mySubnet" `
    -SecurityGroupName "myNetworkSecurityGroup" `
    -PublicIpAddressName "myPublicIpAddress" `
    -OpenPorts 80 `
    -Image "myPackerImage"

Packer 이미지와 다른 리소스 그룹 또는 지역에서 VM을 만들려는 경우 이미지 이름 대신 이미지 ID를 지정합니다.If you wish to create VMs in a different resource group or region than your Packer image, specify the image ID rather than image name. Get-AzureRmImage를 사용하여 이미지 ID를 가져올 수 있습니다.You can obtain the image ID with Get-AzureRmImage.

Packer 이미지에서 VM을 만드는 데 몇 분이 걸립니다.It takes a few minutes to create the VM from your Packer image.

VM 및 웹 서버 테스트Test VM and webserver

Get-AzureRmPublicIPAddress를 사용하여 VM의 공용 IP 주소를 가져옵니다.Obtain the public IP address of your VM with Get-AzureRmPublicIPAddress. 다음 예제에서는 앞서 만든 myPublicIP의 IP 주소를 가져옵니다.The following example obtains the IP address for myPublicIP created earlier:

Get-AzureRmPublicIPAddress `
    -ResourceGroupName $rgName `
    -Name "myPublicIPAddress" | select "IpAddress"

실제로 Packer 프로비저너에서 IIS 설치를 포함하는 VM을 확인하려면 웹 브라우저에 공용 IP 주소를 입력합니다.To see your VM, that includes the IIS install from the Packer provisioner, in action, enter the public IP address in to a web browser.

IIS 기본 사이트

다음 단계Next steps

이 예제에서는 이미 설치된 IIS를 사용하여 VM 이미지를 만드는 데 Packer를 사용했습니다.In this example, you used Packer to create a VM image with IIS already installed. Team Services, Ansible, Chef 또는 Puppet를 사용하여 이미지에서 만든 VM에 앱을 배포하는 것과 같은 기존 배포 워크플로와 함께 이 VM 이미지를 사용할 수 있습니다.You can use this VM image alongside existing deployment workflows, such as to deploy your app to VMs created from the Image with Team Services, Ansible, Chef, or Puppet.

다른 Windows 배포판에 대한 추가 예제 Packer 템플릿은 이 GitHub 리포지토리를 참조하세요.For additional example Packer templates for other Windows distros, see this GitHub repo.