Packerで最新のイメージを使用してAMIを作成する(Amazon Linux 2/Windows Server 2016対応)

今回はPackerで、「その時の最新のイメージ」を利用してAMIを作成する方法を試してみたので紹介します。また試したのはAmazon Linux 2とWindows Server 2016となります。
2018.09.28

こんにちは。サービスグループの武田です。

Packer使ってますか?ゴールデンイメージを簡単に作れて便利ですよね。Packerって何?という方は大瀧のエントリを参考にしてください。

PackerでAmazon LinuxのAMI(Amazon Machine Image)を作成する

今回はPackerで、「その時の最新のイメージ」を利用してAMIを作成する方法を試してみたので紹介します。また試したのはAmazon Linux 2Windows Server 2016となります。その他の環境についてもできるはずですのでぜひやってみてください。

環境

次の環境で検証しています。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65

$ packer version
Packer v1.3.1

$ aws --version
aws-cli/1.16.21 Python/3.7.0 Darwin/17.7.0 botocore/1.12.11

$ jq --version
jq-1.5

またAWS CLI用のdefaultプロファイルが設定されています。

ファイルの用意

それではそれぞれのOS用のビルドファイルを作成していきます。なおビルドするタイミングによって最新のAMIが変わるため、タグにどのAMIをベースとしたのか残すようにしておきます。こちらについては吉江が記事を書いています。

【小ネタ】PackerでAMIを作成する時にAMI_IDとAMI名を付けませんか?

Amazon Linux 2

まずはAmazon Linux 2用にビルドファイルを用意します。

build-amzn2.json

{
  "variables": {
    "PROFILE": "default"
  },
  "builders": [{
    "type": "amazon-ebs",
    "profile": "{{user `PROFILE`}}",
    "region": "ap-northeast-1",
    "source_ami_filter": {
      "filters": {
        "name": "amzn2-ami-hvm-*-x86_64-gp2"
      },
      "owners": ["137112412989"],
      "most_recent": true
    },
    "instance_type": "t3.micro",
    "ssh_username": "ec2-user",
    "ssh_timeout": "5m",
    "ami_name": "amazon-linux-2-{{timestamp}}",
    "tags": {
      "Base_AMI_ID": "{{ .SourceAMI }}",
      "Base_AMI_NAME": "{{ .SourceAMIName }}"
    }
  }],

  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo yum update -y"
    ]
  }]
}

ポイントはbuilders[].source_ami_filterです。filters.nameamzn2-ami-hvm-*-x86_64-gp2と日付などが入る部分をワイルドカードとし、most_recenttrueを指定します。こうすることで自動的にマッチするAMIの最新のものが使用されます。

Windows Server 2016

続いて同様のファイルをWindows Server 2016でも準備します。Windows Server 2016の場合、ビルドファイルだけではなくSysprep用のファイルなども用意する必要があります。基本的な流れは加藤のエントリと同じです。

PackerでWindows Server 2016のAMIを作成してみた

build-win2016.json

{
  "variables": {
    "PROFILE": "default"
  },
  "builders": [{
    "type": "amazon-ebs",
    "profile": "{{user `PROFILE`}}",
    "region": "ap-northeast-1",
    "source_ami_filter": {
      "filters": {
        "name": "Windows_Server-2016-Japanese-Full-Base-*"
      },
      "owners": ["801119661308"],
      "most_recent": true
    },
    "instance_type": "t3.micro",
    "ami_name": "Windows_Server-2016-Japanese-{{timestamp}}",
    "user_data_file": "{{template_dir}}/setup_winrm.txt",
    "tags": {
      "Base_AMI_ID": "{{ .SourceAMI }}",
      "Base_AMI_NAME": "{{ .SourceAMIName }}"
    },

    "communicator": "winrm",
    "winrm_username": "Administrator"
  }],

  "provisioners": [{
    "type": "powershell",
    "scripts": [
      "scripts/sysprep_winsvr2016.ps1"
    ]
  }]
}

ポイントは先ほどと同じくbuilders[].source_ami_filterです。filters.nameWindows_Server-2016-Japanese-Full-Base-*と日付などが入る部分をワイルドカードとし、most_recenttrueを指定します。こうすることで自動的にマッチするAMIの最新のものが使用されます。

他に必要なファイルを用意します。

setup_winrm.txt

<powershell>
winrm quickconfig -q
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="300"}'
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'

netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow
netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow

net stop winrm
sc config winrm start=auto
net start winrm

Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope LocalMachine
</powershell>

scripts/sysprep_winsvr2016.ps1

C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule
C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\SysprepInstance.ps1

ビルドしてみる

それではそれぞれ準備ができたので実際にビルドしてAMIを作成してみましょう。

まずはAmazon Linux 2から。

$ packer build build-amzn2.json
amazon-ebs output will be in this color.

==> amazon-ebs: Prevalidating AMI Name: amazon-linux-2-1538123173
    amazon-ebs: Found Image ID: ami-08847abae18baa040
==> amazon-ebs: Creating temporary keypair: packer_5bade5a5-1a8e-11d8-253c-cda50d4d8a47
==> amazon-ebs: Creating temporary security group for this instance: packer_5bade5a7-3870-a8f6-55ab-00937fb87047
==> amazon-ebs: Authorizing access to port 22 from 0.0.0.0/0 in the temporary security group...
==> amazon-ebs: Launching a source AWS instance...
==> amazon-ebs: Adding tags to source instance
    amazon-ebs: Adding tag: "Name": "Packer Builder"
    amazon-ebs: Instance ID: i-00e78919f5fc637eb
==> amazon-ebs: Waiting for instance (i-00e78919f5fc637eb) to become ready...
==> amazon-ebs: Using ssh communicator to connect: 13.230.41.126
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Provisioning with shell script: /var/folders/v9/hpwnqvc94fqc23jdh8w9b30r0000gp/T/packer-shell682948240
    amazon-ebs: Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
    amazon-ebs: Existing lock /var/run/yum.pid: another copy is running as pid 2462.
    amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit...
    amazon-ebs:   The other application is: yum
    amazon-ebs:     Memory :  92 M RSS (383 MB VSZ)
    amazon-ebs:     Started: Fri Sep 28 08:26:35 2018 - 00:04 ago
    amazon-ebs:     State  : Running, pid: 2462
    amazon-ebs: Existing lock /var/run/yum.pid: another copy is running as pid 2474.
    amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit...
    amazon-ebs:   The other application is: yum
    amazon-ebs:     Memory :  76 M RSS (295 MB VSZ)
    amazon-ebs:     Started: Fri Sep 28 08:26:35 2018 - 00:07 ago
    amazon-ebs:     State  : Running, pid: 2474
    amazon-ebs: Resolving Dependencies
    amazon-ebs: --> Running transaction check
    amazon-ebs: ---> Package GeoIP.x86_64 0:1.5.0-11.amzn2 will be updated
    amazon-ebs: ---> Package GeoIP.x86_64 0:1.5.0-11.amzn2.0.2 will be an update

〜中略〜

    amazon-ebs:   zip.x86_64 0:3.0-11.amzn2.0.2
    amazon-ebs:   zlib.x86_64 0:1.2.7-17.amzn2.0.2
    amazon-ebs:
    amazon-ebs: Complete!
==> amazon-ebs: Stopping the source instance...
    amazon-ebs: Stopping instance, attempt 1
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating unencrypted AMI amazon-linux-2-1538123173 from instance i-00e78919f5fc637eb
    amazon-ebs: AMI: ami-0a249cc29bf0094f8
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Adding tags to AMI (ami-0a249cc29bf0094f8)...
==> amazon-ebs: Tagging snapshot: snap-09ae9e836c78840d1
==> amazon-ebs: Creating AMI tags
    amazon-ebs: Adding tag: "Base_AMI_ID": "ami-08847abae18baa040"
    amazon-ebs: Adding tag: "Base_AMI_NAME": "amzn2-ami-hvm-2.0.20180810-x86_64-gp2"
==> amazon-ebs: Creating snapshot tags
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Cleaning up any extra volumes...
==> amazon-ebs: No volumes to clean up, skipping
==> amazon-ebs: Deleting temporary security group...
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-0a249cc29bf0094f8    

成功したみたいです。同様にWindows Server 2016もビルドしてみます。

$ packer build build-win2016.json
amazon-ebs output will be in this color.

==> amazon-ebs: Prevalidating AMI Name: Windows_Server-2016-Japanese-1538123950
    amazon-ebs: Found Image ID: ami-001b1a5edbc1aa135
==> amazon-ebs: Creating temporary keypair: packer_5bade8ae-e76f-44de-5938-ef9a43f40902
==> amazon-ebs: Creating temporary security group for this instance: packer_5bade8b1-9938-ac28-b167-237113adcd2b
==> amazon-ebs: Authorizing access to port 5985 from 0.0.0.0/0 in the temporary security group...
==> amazon-ebs: Launching a source AWS instance...
==> amazon-ebs: Adding tags to source instance
    amazon-ebs: Adding tag: "Name": "Packer Builder"
    amazon-ebs: Instance ID: i-0daddbf70f1377afa
==> amazon-ebs: Waiting for instance (i-0daddbf70f1377afa) to become ready...
==> amazon-ebs: Waiting for auto-generated password for instance...
    amazon-ebs: It is normal for this process to take up to 15 minutes,
    amazon-ebs: but it usually takes around 5. Please wait.
    amazon-ebs:
    amazon-ebs: Password retrieved!
==> amazon-ebs: Using winrm communicator to connect: 18.179.58.253
==> amazon-ebs: Waiting for WinRM to become available...
    amazon-ebs: #< CLIXML
    amazon-ebs: WinRM connected.
    amazon-ebs: <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><Obj S="progress" RefId="1"><TNRef RefId="0" /><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>
==> amazon-ebs: Connected to WinRM!
==> amazon-ebs: Provisioning with Powershell...
==> amazon-ebs: Provisioning with powershell script: scripts/sysprep_winsvr2016.ps1
    amazon-ebs:
    amazon-ebs: TaskPath                                       TaskName                          State
    amazon-ebs: --------                                       --------                          -----
    amazon-ebs: \                                              Amazon Ec2 Launch - Instance I... Ready
==> amazon-ebs: Stopping the source instance...
    amazon-ebs: Stopping instance, attempt 1
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating unencrypted AMI Windows_Server-2016-Japanese-1538123950 from instance i-0daddbf70f1377afa
    amazon-ebs: AMI: ami-0af559687a3b3c270
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Adding tags to AMI (ami-0af559687a3b3c270)...
==> amazon-ebs: Tagging snapshot: snap-003f8e7ce7a2957d9
==> amazon-ebs: Creating AMI tags
    amazon-ebs: Adding tag: "Base_AMI_ID": "ami-001b1a5edbc1aa135"
    amazon-ebs: Adding tag: "Base_AMI_NAME": "Windows_Server-2016-Japanese-Full-Base-2018.09.15"
==> amazon-ebs: Creating snapshot tags
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Cleaning up any extra volumes...
==> amazon-ebs: No volumes to clean up, skipping
==> amazon-ebs: Deleting temporary security group...
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-0af559687a3b3c270

Windows Server 2016も問題ないですね。それでは作成できたAMIを確認してみます。

$ aws ec2 describe-images --owner self | jq 'del(.Images[] | .BlockDeviceMappings)'
{
  "Images": [
    {
      "Architecture": "x86_64",
      "CreationDate": "2018-09-28T08:28:17.000Z",
      "ImageId": "ami-0a249cc29bf0094f8",
      "ImageLocation": "123456789012/amazon-linux-2-1538123173",
      "ImageType": "machine",
      "Public": false,
      "OwnerId": "123456789012",
      "State": "available",
      "EnaSupport": true,
      "Hypervisor": "xen",
      "Name": "amazon-linux-2-1538123173",
      "RootDeviceName": "/dev/xvda",
      "RootDeviceType": "ebs",
      "SriovNetSupport": "simple",
      "Tags": [
        {
          "Key": "Base_AMI_ID",
          "Value": "ami-08847abae18baa040"
        },
        {
          "Key": "Base_AMI_NAME",
          "Value": "amzn2-ami-hvm-2.0.20180810-x86_64-gp2"
        }
      ],
      "VirtualizationType": "hvm"
    },
    {
      "Architecture": "x86_64",
      "CreationDate": "2018-09-28T08:49:12.000Z",
      "ImageId": "ami-0af559687a3b3c270",
      "ImageLocation": "123456789012/Windows_Server-2016-Japanese-1538123950",
      "ImageType": "machine",
      "Public": false,
      "OwnerId": "123456789012",
      "Platform": "windows",
      "State": "available",
      "EnaSupport": true,
      "Hypervisor": "xen",
      "Name": "Windows_Server-2016-Japanese-1538123950",
      "RootDeviceName": "/dev/sda1",
      "RootDeviceType": "ebs",
      "SriovNetSupport": "simple",
      "Tags": [
        {
          "Key": "Base_AMI_ID",
          "Value": "ami-001b1a5edbc1aa135"
        },
        {
          "Key": "Base_AMI_NAME",
          "Value": "Windows_Server-2016-Japanese-Full-Base-2018.09.15"
        }
      ],
      "VirtualizationType": "hvm"
    }
  ]
}

タグも付与されていてバッチリですね!

まとめ

わざわざAMIが更新されたかチェックし、更新されていたらビルドファイルを修正する。なんてことをしたくなかったで、常に最新のAMIを使用する方法を試してみました。

どなたかの助けになれば幸いです。