kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

Packer v1.7を試す

f:id:kun432:20210508233221p:plain

Packerのv1.7からpacker initプラグインをダウンロード・インストールできるようになったので試してみました。

目次

要件

以下に注意してください。

  • packer v1.7以上
  • テンプレートはHCL(Hashicorp Configuration Language)で書くこと、JSONは使えない
  • packer init対応プラグインであること

HCLテンプレート

Apacheをインストール・起動するAWS AMIを作成する、シンプルなテンプレートで試してみましょう。JSONだとこんな感じです。

{
  "variables": {
    "instance_type": "t2.micro",
    "region": "ap-northeast-1",
    "host": "webserver"
  },
  "builders": [
    {
      "type": "amazon-ebs",
      "region": "{{user `region`}}",
      "source_ami_filter": {
        "filters": {
          "name": "amzn2-ami-hvm-*-x86_64-gp2"
        },
        "owners": ["137112412989"],
        "most_recent": true
      },
      "instance_type": "{{user `instance_type`}}",
      "ssh_username": "ec2-user",
      "ssh_timeout": "5m",
      "ami_name": "{{user `host`}}-{{isotime | clean_resource_name}}",
      "tags": {
        "Base_AMI_ID": "{{ .SourceAMI }}",
        "Base_AMI_NAME": "{{ .SourceAMIName }}"
      }
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "sudo yum update -y",
        "sudo amazon-linux-extras install -y epel",
        "sudo yum install -y @development jq git",
        "sudo yum install httpd -y",
        "echo 'version 1' | sudo tee /var/www/html/index.html",
        "sudo systemctl enable httpd"
      ]
    }
  ]
}

HCLで書くとこうなります。Terraformっぽく複数のファイルに分けてみました。ファイル名は*.pkr.hclとするのが良いようです。

variables.pkr.hcl

変数の定義を行います。まんまTerraformと同じです。

variable "instance_type" {
  default = "t2.micro"
}

variable "region" {
  default = "ap-northeast-1"
}

variable "host" {
  default = "webserver"
}

plugins.pkr.hcl

プラグインの設定です。

packer {
  required_plugins {
    amazon = {
      version = ">= 0.0.1"
      source = "github.com/hashicorp/amazon"
    }
  }
}

sources.pkr.hcl

これまでのprovisionersにあたるものです。これまでsource_ami_filterで指定していたのがdataを使って参照していますね。ぐっとTerraformっぽくなりました。

あと、テンプレのように書いていたclean_resource_nameですがHCLでは対応していないようです。

data "amazon-ami" "amazon_linux2" {
  filters = {
    name = "amzn2-ami-hvm-*-x86_64-gp2"
  }
  most_recent = true
  owners      = ["137112412989"]
  region      = "ap-northeast-1"
}

source "amazon-ebs" "webserver" {
  region        = var.region
  source_ami    = data.amazon-ami.amazon_linux2.id
  instance_type = var.instance_type
  ssh_username  = "ec2-user"
  ssh_timeout   = "5m"
  ami_name      = "${var.host}-{{ timestamp }}"
  tags = {
    Base_AMI_ID = "{{ .SourceAMI }}"
    Base_AMI_NAME = "{{ .SourceAMIName }}"
  }
}

build.pkr.hcl

これまでのbuildersにあたるものです。sourcesのところでsources.pkr.hclの定義を呼んでいます。

build {
  sources = [
    "source.amazon-ebs.webserver"
  ]
  provisioner "shell" {
    inline = [
      "sudo yum update -y",
      "sudo amazon-linux-extras install -y epel",
      "sudo yum install -y @development jq git",
      "sudo yum install httpd -y",
      "echo 'version 1' | sudo tee /var/www/html/index.html",
      "sudo systemctl enable httpd"
    ]
  }
}

最終的にこんな感じで一つのディレクトリ内においておきます。

$ ls
build.pkr.hcl       plugins.pkr.hcl     sources.pkr.hcl     variables.pkr.hcl   variables.pkrvars.hcl

ではプラグインをインストールしましょう。packer initでプラグインを記載したhclファイル(plugins.pkr.hcl)か、hclファイルのあるディレクトリを指定します。ここではカレントディレクトリを指定しました。

$ packer init .
Installed plugin github.com/hashicorp/amazon v0.0.1 in "/XXX/XXX/.packer.d/plugins/github.com/hashicorp/amazon/packer-plugin-amazon_v0.0.1_x5.0_darwin_amd64"

プラグインは~/.packer.d/plugins配下にダウンロードされます。

~/.packer.d/plugins/
└── github.com
    └── hashicorp
        └── amazon
            ├── packer-plugin-amazon_v0.0.1_x5.0_darwin_amd64
            └── packer-plugin-amazon_v0.0.1_x5.0_darwin_amd64_SHA256SUM

packer validateも使えます(v1.7リリース時は使えなかったみたいですがv1.7.2では使えました)

$ packer validate .
Error: Unknown source type amaozn-ebs

  on  line 0:
  (source code not available)

known builders: [vsphere-clone hyperone proxmox vmware-iso digitalocean
virtualbox-ovf amazon-instance vagrant amazon-ebssurrogate openstack
googlecompute jdcloud hyperv-iso docker hyperv-vmcx parallels-pvm profitbricks
proxmox-iso virtualbox-iso cloudstack lxc vsphere-iso lxd amazon-ebsvolume
amazon-ebs hcloud ncloud azure-chroot proxmox-clone scaleway ucloud-uhost linode
triton file oneandone alicloud-ecs parallels-iso qemu amazon-chroot osc-chroot
azure-arm null oracle-oci tencentcloud-cvm azure-dtl osc-bsusurrogate vmware-vmx
osc-bsuvolume oracle-classic osc-bsu yandex virtualbox-vm]

ではビルドしてみましょう。

$ packer build .

amazon-ebs.webserver: output will be in this color.

==> amazon-ebs.webserver: Prevalidating any provided VPC information
==> amazon-ebs.webserver: Prevalidating AMI Name: webserver-1620491713
    amazon-ebs.webserver: Found Image ID: ami-0ca38c7440de1749a
==> amazon-ebs.webserver: Creating temporary keypair: packer_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
==> amazon-ebs.webserver: Creating temporary security group for this instance: packer_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
==> amazon-ebs.webserver: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups...
==> amazon-ebs.webserver: Launching a source AWS instance...
==> amazon-ebs.webserver: Adding tags to source instance
    amazon-ebs.webserver: Adding tag: "Name": "Packer Builder"
    amazon-ebs.webserver: Instance ID: i-XXXXXXXXXXXXXXXX
==> amazon-ebs.webserver: Waiting for instance (i-XXXXXXXXXXXXXXXX) to become ready...
==> amazon-ebs.webserver: Using ssh communicator to connect: 54.250.137.121
==> amazon-ebs.webserver: Waiting for SSH to become available...
==> amazon-ebs.webserver: Connected to SSH!
==> amazon-ebs.webserver: Provisioning with shell script: /var/folders/c8/XXXXXXXXXXXXXXXXXXXXXXXX/T/packer-shellXXXXXXXXX
(snip)
==> amazon-ebs.webserver: Stopping the source instance...
    amazon-ebs.webserver: Stopping instance
==> amazon-ebs.webserver: Waiting for the instance to stop...
==> amazon-ebs.webserver: Creating AMI webserver-XXXXXXXXXX from instance i-XXXXXXXXXXXXXXXX
    amazon-ebs.webserver: AMI: ami-XXXXXXXXXXXXXXXX
==> amazon-ebs.webserver: Waiting for AMI to become ready...
==> amazon-ebs.webserver: Adding tags to AMI (ami-XXXXXXXXXXXXXXXX)...
==> amazon-ebs.webserver: Tagging snapshot: snap-XXXXXXXXXXXXXXXX
==> amazon-ebs.webserver: Creating AMI tags
    amazon-ebs.webserver: Adding tag: "Base_AMI_ID": "ami-0ca38c7440de1749a"
    amazon-ebs.webserver: Adding tag: "Base_AMI_NAME": "amzn2-ami-hvm-2.0.20210427.0-x86_64-gp2"
==> amazon-ebs.webserver: Creating snapshot tags
==> amazon-ebs.webserver: Terminating the source AWS instance...
==> amazon-ebs.webserver: Cleaning up any extra volumes...
==> amazon-ebs.webserver: No volumes to clean up, skipping
==> amazon-ebs.webserver: Deleting temporary security group...
==> amazon-ebs.webserver: Deleting temporary keypair...
Build 'amazon-ebs.webserver' finished after 4 minutes 12 seconds.

==> Wait completed after 4 minutes 12 seconds

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

できました。

プラグインを使ってみる

AMIの作成自体もプラグインで行ったわけですが、他のプラグインも使ってみましょう。

以下で紹介されているAMIの世代管理を行ってくれるプラグイン"amazon-ami-management"を使ってみます。

plugins.pkr.hclを修正します。

packer {
  required_plugins {
    amazon = {
      version = ">= 0.0.1"
      source = "github.com/hashicorp/amazon"
    }
    amazon-ami-management = {
      version = ">= 1.0.0"
      source = "github.com/wata727/amazon-ami-management"
    }
  }
}

プラグインをインストールします。

$ packer init .
Installed plugin github.com/wata727/amazon-ami-management v1.1.2 in "/XXX/XXX/.packer.d/plugins/github.com/wata727/amazon-ami-management/packer-plugin-amazon-ami-management_v1.1.2_x5.0_darwin_amd64"

AMIのタグでAmazon_AMI_Management_Identifierを付与します。"amazon-ami-management"プラグインはこのタグから世代数を確認するようです。sources.pkr.jsonを修正します。

(snip)
  tags = {
    Base_AMI_ID = "{{ .SourceAMI }}"
    Base_AMI_NAME = "{{ .SourceAMIName }}"
    Amazon_AMI_Management_Identifier = var.host
  }
(snip)

プラグインの設定はpost-processorsセクションで設定します。JSONの場合は独立したセクションでしたが、HCLの場合はbuildの中になります。build.pkr.hclを以下のように修正します。

build {
  sources = [
    "source.amazon-ebs.webserver"
  ]
  provisioner "shell" {
    inline = [
      "sudo yum update -y",
      "sudo amazon-linux-extras install -y epel",
      "sudo yum install -y @development jq git",
      "sudo yum install httpd -y",
      "echo 'version 1' | sudo tee /var/www/html/index.html",
      "sudo systemctl enable httpd"
    ]
  }
  # 以下を追加
  post-processor "amazon-ami-management" {
    regions = [var.region]
    identifier = var.host
    keep_releases = 3
  }
}

あと、identifierで指定した文字列が

何度かビルドしてみると、AMIが3世代だけ残っていることが確認できると思います。

変数のオーバーライド

-var-var-fileを使った変数のオーバーライドはこれまでと同じです。

$ packer build -var host=webserver2 .
$ packer build -var-file=variables.pkrvars.file .

ファイル名は*.pkrvars.hclにするのがよいようです。中身はこんな感じです。

host = "webserver2"

環境ごとに変数を分けて複数の環境に対応するようなケースは、これまでと同じやり方でいけそうです。

既存のJSONテンプレートからHCLテンプレートに変換する

packer hc2_upgradeを使うと、既存のJSONテンプレートをHCLに変換してくれます。

$ packer hcl2_upgrade packer.json
Successfully created packer.json.pkr.hcl

中身を見てみるとこんな感じです。

variable "host" {
  type    = string
  default = "webserver"
}

variable "instance_type" {
  type    = string
  default = "t2.micro"
}

variable "region" {
  type    = string
  default = "ap-northeast-1"
}

data "amazon-ami" "autogenerated_1" {
  filters = {
    name = "amzn2-ami-hvm-*-x86_64-gp2"
  }
  most_recent = true
  owners      = ["137112412989"]
  region      = "${var.region}"
}

# 1 error occurred upgrading the following block:
# unhandled "clean_resource_name" call:
# there is no way to automatically upgrade the "clean_resource_name" call.
# Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
# Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.

source "amazon-ebs" "autogenerated_1" {
  ami_name      = "${var.host}-{{ clean_resource_name `${timestamp()}` }}"
  instance_type = "${var.instance_type}"
  region        = "${var.region}"
  source_ami    = "${data.amazon-ami.autogenerated_1.id}"
  ssh_timeout   = "5m"
  ssh_username  = "ec2-user"
  tags = {
    Base_AMI_ID   = "{{ .SourceAMI }}"
    Base_AMI_NAME = "{{ .SourceAMIName }}"
  }
}

build {
  sources = ["source.amazon-ebs.autogenerated_1"]

  provisioner "shell" {
    inline = ["sudo yum update -y", "sudo amazon-linux-extras install -y epel", "sudo yum install -y @development jq git", "sudo yum install httpd -y", "echo 'version 1' | sudo tee /var/www/html/index.html", "sudo systemctl enable httpd"]
  }

}

上でも記載したとおり、clean_resource_nameは使えないので修正が必要、ということで、コメントとして記載されています。

多少の修正は必要になるかもしれませんが、イチから書き直さなくていいので便利ですね。

まとめ

Packerについて調べると、ほとんどがJSONの情報で、HCLでの情報はあまり見当たらないのが辛いところですが、色々メリットもあります。

  • JSONの使いにくさ(ケツカンマでコケる、コメント入れれない、そもそも見にくい)に悩まされることもなくなる
  • プラグインが手軽に使える
  • 既存のJSONからの変換もできる
  • Terraformを日常的に使っていれば、同じ書き方でいける

徐々にシフトしていけばいいのではないかと思います。