この記事は Livesense - 自 Advent Calendar 2017 4日目の記事です。
本日はインフラストラクチャーグループの@nashioxがお送りします。
インフラストラクチャーグループの「自」といえばもちろん「自動化」ですね。
Packerでイメージ作成を自動化する
リブセンスではPackerを使って「AMI」「OpenStackイメージ」などを作っています。
イメージの作成そのものはCIツールを利用してGitリポジトリのPush/Margeをフックしてpacker build
を実行すれば良いわけですが、気をつけなければいけないのがイメージの削除です。
Packer only builds images. It does not attempt to manage them in any way.
Packerの公式ドキュメントにも書いてありますが、Packerはイメージのビルドのみを行います。
そのため、何度も何度も自動でビルドされていくうちにたくさんのイメージが出来上がっていくわけです。
イメージが積み重なっていくとどれが最新のイメージかわからなくなったり、AMIの場合だとそれだけでお金がどんどんかかっていくことになってしまいます。
せっかくイメージの作成を自動化しても、削除は手動では旨味が少なくなってしまいます。
Packerを使ってイメージ作成の自動化を考えていく上ではイメージの削除・世代管理を考えることが重要になってきます。
イメージの削除・世代管理を考える
イメージの削除・世代管理を考えるのが重要というのはわかりましたが、Packerそのものに機能がない以上どうすれば良いのか。
今回は「AMI」と「OpenStackイメージ」について考えてみました。
AMIの場合
AMIの場合はすでに同じことを考えていた人がいらっしゃいました。
PackerのAMI手動管理を卒業するプラグインを作ってみた
Packerのプラグイン機構を利用して、packer build
後のpost-processor
としてイメージの削除・世代管理を行ってくれるというものです。
https://github.com/wata727/packer-post-processor-amazon-ami-management
インストール
インストールは簡単。
~/.packer.d/plugins/
以下にダウンロードしてきて配置するだけです。
### 無ければ作る
$ mkdir -p ~/.packer.d/plugins
### ダウンロード(今回はLinux amd64)
$ curl -L -O https://github.com/wata727/packer-post-processor-amazon-ami-management/releases/download/v0.2.0/packer-post-processor-amazon-ami-management_linux_amd64.zip
### 解凍
$ unzip packer-post-processor-amazon-ami-management_linux_amd64.zip
### 配置
$ mv packer-post-processor-amazon-ami-management ~/.packer.d/plugins/
設定
Packerの設定ファイルに以下のようにpost-processors
に"type": "amazon-ami-management"
を設定することで世代管理されます。
{
"builders": [{
"type": "amazon-ebs",
"region": "ap-northeast-1",
"source_ami": "ami-da9e2cbc",
"instance_type": "t2.micro",
"ssh_username": "ec2-user",
"ssh_pty": "true",
"ami_name": "packer-example {{timestamp}}",
"tags": {
"Amazon_AMI_Management_Identifier": "packer-example"
}
}],
"provisioners":[{
"type": "shell",
"inline": [
"echo 'running...'"
]
}],
"post-processors":[{
"type": "amazon-ami-management",
"region": "ap-northeast-1",
"identifier": "packer-example",
"keep_releases": "3"
}]
}
builders
の段階でtags
に任意のAmazon_AMI_Management_Identifier
を設定しておけばidentifier
で世代管理するイメージをフィルターすることが可能です。
作成日時の新しい順にkeep_releases
で設定した数で世代管理されます。
これで手動でイメージをポチポチ消していく必要がなくなります。
OpenStackの場合
OpenStackの場合は色々調べてみましたが、良い方法を見つけることが出来ませんでした。
なのでpacker-post-processor-amazon-ami-managementにインスパイアされて、OpenStack版も作ってみました。
https://github.com/nashiox/packer-post-processor-openstack-image-management
インストール
~/.packer.d/plugins/
以下にダウンロードしてきて配置するだけです。
### 無ければ作る
$ mkdir -p ~/.packer.d/plugins
### ダウンロード(今回はLinux amd64)
$ curl -L -O https://github.com/nashiox/packer-post-processor-openstack-image-management/releases/download/v0.0.1/packer-post-processor-openstack-image-management_linux_amd64.zip
### 解凍
$ unzip packer-post-processor-openstack-image-management_linux_amd64.zip
### 配置
$ mv packer-post-processor-openstack-image-management_linux_amd64/packer-post-processor-openstack-image-management ~/.packer.d/plugins/
設定
Packerの設定ファイルに以下のようにpost-processors
に"type": "openstack-image-management"
を設定することで世代管理されます。
{
"builders": [{
"type": "openstack",
"identity_endpoint": "http://<destack-ip>:5000/v3",
"tenant_name": "admin",
"domain_name": "Default",
"username": "admin",
"password": "<your admin password>",
"region": "RegionOne",
"ssh_username": "root",
"image_name": "sample-image",
"source_image": "<image id>",
"flavor": "m1.tiny",
"insecure": "true"
}],
"provisioners":[{
"type": "shell",
"inline": [
"echo 'running...'"
]
}],
"post-processors":[{
"type": "openstack-image-management",
"identity_endpoint": "http://<destack-ip>:5000/v3",
"tenant_name": "admin",
"domain_name": "Default",
"username": "admin",
"password": "<your admin password>",
"region": "RegionOne",
"insecure": "true",
"identifier": "sample-image",
"keep_releases": "2"
}]
}
identifier
にはイメージ名を利用しています。
作成日時の新しい順にkeep_releases
で設定した数で世代管理されます。
プラグインの実装
Packerプラグインの実装に関しては詳しくは公式ドキュメントを御覧ください。
今回は作成したpacker-post-processor-openstack-image-managementの仕組みについて簡単に紹介します。
プラグインの起動
プラグインの起動はmain
関数で以下のようにします。
package main
import (
"github.com/hashicorp/packer/packer/plugin"
openstackimagemanagement "github.com/nashiox/packer-post-processor-openstack-image-management/openstack-image-management"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterPostProcessor(new(openstackimagemanagement.OpenStackPostProcessor))
server.Serve()
}
github.com/hashicorp/packer/packer/plugin
を利用してイメージを作成しています。
今回はイメージ作成後、古いイメージを削除していきたいのでpost-processor
の仕組みを利用します。
server.RegisterPostProcessor
でプラグインを登録します。
イメージリストの取得
イメージのリストは以下のように取得しています。
var imageList []images.Image
log.Println("Describing images for generation management")
pager := images.List(p.conn, images.ListOpts{Name: p.config.Identifier})
pager.EachPage(func(page pagination.Page) (bool, error) {
imgs, err := images.ExtractImages(page)
if err != nil {
return false, err
}
imageList = append(imageList, imgs...)
return true, nil
})
sort.Slice(imageList, func(i, j int) bool {
return imageList[i].CreatedAt.After(imageList[j].CreatedAt)
})
images.ListOpts
でフィルターを設定することが出来ます。
今回はイメージ名でフィルターをするのでName: p.config.Identifier
を設定しています。
images.List
でイメージリストのページャーが取得出来るので、pager.EachPage
でページごとにイメージを取得、imageList
Sliceに追加をしていきます。
そしてsort.Slice
を使って、CreatedAt
が新しい順にソートします。
Go言語でSliceのソートをするのは結構面倒な印象だったのですが、sort.Slice
がGo 1.8から追加されて非常に書きやすくなっていました。
https://github.com/golang/go/commit/22a2bdfedb95612984cec3141924953b88a607b7
イメージの削除
イメージは以下のように削除・世代管理しています。
for i, img := range imageList {
if i < p.config.KeepReleases {
continue
}
ui.Message(fmt.Sprintf("Deleting image: %s", img.ID))
log.Printf("Deteting image (%s)", img.ID)
if result := images.Delete(p.conn, img.ID); result.Err != nil {
return nil, true, result.Err
}
}
KeepReleases
で世代数を確認し、images.Delete
で対象となるイメージを削除しています。
まとめ
Packerを利用するとイメージの作成が自動化されてすごく便利でしたが、削除まわりで今一歩感がありました。
しかし今回Packerのプラグイン機構を利用すればそれも簡単にできるようになりました。
ただ、Packerのプラグイン機構はまだあまり一般的ではないのか、探すのが大変でした。
Goの1バイナリを置けば動いてしまうのは簡単で良いですが、GitHubの広い海を探して回るのはなかなか大変なので何かいい方法をご存知の方がいれば教えてください。