PackerでHCL2がサポートされたので既存のJson形式のコードを書き換えた話
インフラエンジニアの寺岡です。
Packerで待ちに待ったアップデートがありました。
ついにHCL2をサポートしてくれました!
今回は既存のJsonで書いていたコードをHCL2ベースに書き直してみましたので
それぞれのコードを比較しながらまとめてみたいと思います。
Packerとは
マルチクラウド対応のゴールデンイメージを作成できるCLIツールです。
この手のゴールデンイメージを作成するツールは
Immutable Infrastructureやブルーグリーンデプロイメントと非常に相性が良く
AutoScalingなどのサーバの作成と破棄が頻繁に繰り返される環境下において重宝します。
開発はHashiCorpによって継続されておりGolangで記述されています。
同じくTerraformも有名なツールなのでご存知の方が多いのではないでしょうか。
PackerはAWS・GCP・Azureなどの主要なクラウドサービスはもちろんのこと
DockerイメージやVMwareなどのハイパーバイザー型のマシンイメージの作成にも対応しています。
多数のサービス(Packerではbuilderと言います)に対応していますが
どのbuilderでも共通の書式でコードを書いて処理を記述できるのがメリットです。
以前まではJson形式でしか記述できませんでしたが
バージョン1.5よりHCL2形式をサポートしました。
本記事を書いた時点(2020/03/31)ではまだベータ版のため
プロダクション環境における利用は事前によく検証してからの方が良いかもしれません。
HCLとは
HashiCorp Configuration Languageの略で同じくHashiCorpにとって開発されています。
実装的にはJson互換ですが、あくまで独自言語のためJsonとは全く違う独自の記述を行うことができます。
コマンドラインツールでの利用が想定されており、Terraformでは以前からサポートされていたので
tfファイルを書いている方からすると馴染み深いものになっていると思います。
ちなみにバージョン違いでHCL1とHCL2があり現在はHCL2が主流になっています。
Packerのテンプレートを書いてみる(Json)
Packerはイメージの作成をテンプレートと呼ばれるものを記述して作成しますが
テンプレートの内容をJson形式と比較しながらHCL2で書いていきたいと思います。
まずはJson形式で書いたものを見てみましょう。
{ "builders": [ { "type": "amazon-ebs", "region": "{{user `region`}}", "source_ami_filter": { "filters": { "name": "{{user `ami_name`}}*" }, "owners": [ "self" ], "most_recent": true }, "instance_type": "{{user `instance_type`}}", "ssh_username": "{{user `ssh_username`}}", "ami_name": "{{user `ami_name`}}_{{timestamp}}", "tags": { "Name": "{{user `ami_name`}}_{{timestamp}}" }, "iam_instance_profile": "{{user `iam_instance_profile`}}" } ], "provisioners": [ { "type": "shell", "inline": [ "sudo yum -y update" ] } ] }
{{user `ami_name`}}などの部分が変数となっており
格納される値は別ファイルに記述されています。
{ "ssh_username": "ec2-user", "region": "ap-northeast-1", "instance_type": "t3.micro", "ami_name": "ami-packer-test", "iam_instance_profile": "role-packer-test" }
このテンプレートファイルを用いて
$ packer build main.json -var-file=variables.json
とコマンドを実行することで
variables.jsonから変数に格納する値を読み込みつつイメージをビルドすることができます。
上記のテンプレートの内容通りにビルドを行うと
- source_ami_filterに記述した内容でベースとなるAMIIDを検索する
- 検索に該当したAMIをベースにビルド処理を開始する
- ベースAMIから新しくEC2インスタンスを起動する
- EC2インスタンスにログインするためのテンポラリのキーペアを作成する
- SSHでインスタンスにログインしてprovisionersに記述されている処理を実行する
- EC2インスタンスを停止する
- 停止したインスタンスからAMIを取得する
- 取得完了後にインスタンスを削除する
という一連の処理を行います。
AMIの情報を取得するときはIAMロールを利用することができるため
上記の例ではpackerコマンドを実行するEC2インスタンスに
実行に必要なIAMロールを事前に設定しています。
IAMロールに必要な権限は以下にまとめられています。
https://packer.io/docs/builders/amazon.html
Packerのテンプレートを書いてみる(HCL2)
では上記のJsonをHCL2に書き直してみるとどのようになるでしょうか。
まず変数に対して格納する値を書いていきます。
以下の2つのファイルを利用します。
variables.pkrvars.hcl
ssh_username = "ec2-user" region = "ap-northeast-1" instance_type = "t3.micro" ami_name = "ami-packer-test" iam_instance_profile = "role-packer-test"
variables.pkr.hcl
variable "ssh_username" { type = string description = "SSH User Name" } variable "region" { type = string description = "AWS Region" } variable "instance_type" { type = string description = "EC2 Instance Type" } variable "ami_name" { type = string description = "EC2 AMI Name" } variable "iam_instance_profile" { type = string description = "EC2 IAM Instance Profile" }
variables.pkrvars.hclに変数に格納する値を記述します。
Jsonでは「値名:値」でしたがHCL2では「値名 = 値」になります。
HCL2で変数を表現する場合はvariableを利用します。
プログラミング言語と同様に型の概念があり
今回は全て文字列(string)型の変数を利用しています。
その他にも
- 数値型(number)
- リスト型(list)
- マップ型(map)
- ブーリアン型(bool)
などがあります。
次に実際のビルド処理部分の記述をしていきます。
以下の2つのファイルを利用します。
sources.pkr.hcl
## Source source "amazon-ebs" "example" { region = var.region ami_name = "${var.ami_name}_{{timestamp}}" instance_type = var.instance_type iam_instance_profile = var.iam_instance_profile ssh_username = var.ssh_username source_ami_filter { owners = ["self"] most_recent = true filters { virtualization-type = "hvm" name = "${var.ami_name}*" root-device-type = "ebs" } } tags { Name = "${var.ami_name}_{{timestamp}}" } }
build.pkr.hcl
## Build build { sources = [ "source.amazon-ebs.example" ] provisioner "shell" { inline = [ "sudo yum -y update" ] } }
この通り、Json形式では1つのファイルに書いていたものを
sourceとbuildという単位で別ファイルに分けることができます。
またHCL2の場合はコードの途中でコメントを挿入することができるのが特徴です。
変数の参照は「var.変数名」で可能です。
build.pkr.hclではsourceの設定を読み込み
provisionerの部分でビルド時に実行する処理を書いています。
sourcesの部分はlist型になっているため以下のように複数指定することも可能です。
sources = [ "source.amazon-ebs.example", "source.amazon-ebs.example2" ]
ビルドを実行するときはテンプレートファイルが存在するディレクトリに移動して
$ packer build -var-file=variables.pkrvars.hcl ./
とすればカレントディレクトリのファイルを全て読み込んで実行してくれます。
現時点の問題点
Packerには構文チェックを行うvalidateコマンドがありますが
HCLで記述した場合はこれが利用できません。
今後のアップデートに期待です。
https://github.com/hashicorp/packer/issues/8538
まとめ
Packerはクラウドサービスを使ったDevOpsを実践する上で便利なツールとなっています。
CI/CDのフローに組み込みやすくPacker自体の構成も非常にシンプルなため
皆様も是非一度利用してみてください。