PackerでのイメージビルドをCircleCIで実行する
PackerでEC2のAMIをビルドするのはともかく、どこで誰がビルドするのかは悩ましいポイントです。 今回はみんな大好きCircleCIでAMIのビルドとAMI IDの管理する方法を考えてみました。
流れ
- GithubのPRを出す
- CirleCIで
packer validate
を実行する - テストにパスしたら実際にPRをマージ
- CircleCIが
packer build
を実行する - ビルドに成功したら作成されたAMI IDをリポジトリ内の
images.yml
にAMI IDを追記し、Githubへpushする
マージする度にビルドを実行する必要はないため、
作成したAMIのIDを images.yml
に保存し、次回以降はすでにIDの記載がある場合は
packer build
を実行しないようにしています(そのIDが最新かどうかは確認しない)。
テンプレートを編集したあと、同時にビルドを実行したい場合はID部分を削除することで ビルドを実行するようにします。
今回はRubyでこの処理を自動化する packer build
のラッパーを書きました。
実装例
https://github.com/nabeken/circleci-packer-example
ディレクトリ構成
https://github.com/chef/bento 同様に packer
ディレクトリ以下にテンプレートとスクリプト
を用意しました。
CI用のスクリプトは ci
ディレクトリ以下へ用意しています。
circle.yml
特に特別なことはしていませんが、 dependenciesセクションでpackerのバイナリをキャッシュしています。
testは packer/*.json
に対して packer validate
を実行しています(ci/packer-validate.sh
)。
deploymentセクションでは2つのスクリプトを実行しています。
packer-ami.rb
今回のメインです。
packerでAMIをビルドするとログの最後にAMI IDが出力されます。
packer-ami.rb
は packer build
を実行し、
ビルドが成功した場合のみログからAMI IDを取り出し、images.yml
を更新するラッパースクリプトです。
例: 引数にはテンプレート名(ファイル名から .json
を取ったもの)を与えて実行します。
$ ./ci/packer-ami.rb ec2-ubuntu-14.04-docker
// ...通常のPackerのログ...
==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-9201f992
$ cat images.yml
---
ec2-ubuntu-14.04-docker: ami-9201f992
push_to_github.sh
images.yml
に差分が発生している場合はマージ先へpushするだけのスクリプトです。
作業例
AMIにGoをインストールしてみます。作業前の images.yml
は上の状態です。
まず、PRを作ります。
ここではテンプレートでAMI名を修正(重複しているとエラーになる)し、Goをインストールする処理を追加しています。
最後にビルドをトリガーするため、 images.yml
からIDを削除しました。
CircleCIでテスト が通ったのを確認し、マージします。
https://circleci.com/gh/nabeken/circleci-packer-example/4 でマージ後のdeploymentとしてビルドの実行と images.ymlの更新が確認できます。
今後の課題とか
Dockerfileを使ってDockerコンテナをビルドしているとあるプロジェクトでは、 タグにGitのコミットIDを使うことで、CIする時にタグがDockerHubに存在しなければ自動的にリビルドし、 pushする運用をしています。こうすることでチーム内で誰かがビルド職人になることを防いでいます。
同じようなことを今回にも適用するとすると、AMIのタグにGitのコミットIDなどから生成した識別子を付与すれば images.yml
なしに
運用できそうです。
あと、複数のリージョンを扱う場合ももうすこし工夫が必要だと思われますが、現状は必要ないのでこのままになっています。