Jenkins + PackerでAMIを継続的インテグレーションする
みなさんこんにちは。@ryuzeeです。
前回、Packer + Serverspecでテスト済み仮想マシンイメージを自動で生成する方法を紹介しました。 でここまでできれば次にやるのは当然のことながら継続的インテグレーションでしょう。 早速やり方を紹介していきます。
Jenkinsサーバの構築
まず最初にJenkinsサーバを構築します。
Amazon EC2のインスタンスの起動
今回はAmazon EC2上に構築することとし、Amazon Linux 2014.09を使います。インスタンスの起動は通常の手順でやっていけば良いのですが、以下の2点に注意してください。
- Packerの中からAWSのAPIをコールするため、インスタンスにIAM Roleを割り当てて、アクセスキーとシークレットキーをサーバに配置することなくAPIを呼び出せるようにする
- Elactic IPを割り当てる
Jenkinsのセットアップ
毎回手作業でJenkinsをインストールし、手作業でプラグインを入れ、ビルドに必要な環境を作っていくなど狂気の沙汰です。こういう何度もやりそうな作業は自動的にできるようにしておきましょう。 ということでChef Soloを使って一気に構築します。Cookbookは僕のレポジトリにあるものを使います。
ローカルの端末でRubyが動くようにしておいてください。もちろんbundlerが必要です。
まず適当な作業ディレクトリを用意しGemfileを作成します。
source "https://rubygems.org"
gem 'chef'
gem 'knife-solo'
gem 'berkshelf'
作成し終わったら、bundle install
を実行して、必要なgemを導入します。
次にChefのレポジトリを作成します。
bundle exec knife solo init .
を実行してください。ディレクトリやファイルが以下のような構成で作成されます。
.
├── Gemfile
├── Gemfile.lock
├── cookbooks/
├── data_bags/
├── environments/
├── nodes/
├── roles/
└── site-cookbooks/
ChefのCookbookの管理にはBerkshelfを利用するので、同じディレクトリにBerksfile
を作成してください。内容は以下の通りです。
source "http://api.berkshelf.com"
cookbook 'ca-certificates', git:"https://github.com/ryuzee-cookbooks/ca-certificates.git"
cookbook 'apache2-simple', git:"https://github.com/ryuzee-cookbooks/apache2-simple.git"
cookbook 'git', git:"https://github.com/ryuzee-cookbooks/git.git"
cookbook 'jenkins', git:"https://github.com/ryuzee-cookbooks/jenkins.git"
cookbook 'packer', git:"https://github.com/ryuzee-cookbooks/packer.git"
ここまでできたら、
bundle exec berks update
bundle exec berks vendor cookbooks
を実行して、カレントディレクトリのcookbooksディレクトリ内に、cookbookをダウンロードします。
次にAmazon EC2のインスタンスへの適用を進めます。まず最初にインストール対象のサーバにChef Clientをインストールしたりといった準備をします。以下のようなコマンドを実行してください。キーペアのパスやIPアドレスは自分のものに合わせるようにします。
bundle exec knife solo prepare -i /path/to/your_key_pair.pem --no-host-key-verify ec2-user@YOUR_IP_OR_FQDN
実行すると以下のようなログが表示されます。
ffi-yajl/json_gem is deprecated, these monkeypatches will be dropped shortly
Bootstrapping Chef...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16472 100 16472 0 0 13767 0 0:00:01 0:00:01 --:--:-- 13772
Downloading Chef 11.16.2 for el...
downloading https://www.opscode.com/chef/metadata?v=11.16.2&prerelease=false&nightlies=false&p=el&pv=6&m=x86_64
to file /tmp/install.sh.2501/metadata.txt
trying wget...
url https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-11.16.2-1.el6.x86_64.rpm
md5 8731b6558009fc322f6469b415a759f8
sha256 dc7bc9d6084d29ffec67664fdae455406c3e7657c4aebcbef19f5a9ba459db37
downloaded metadata file looks valid...
downloading https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-11.16.2-1.el6.x86_64.rpm
to file /tmp/install.sh.2501/chef-11.16.2-1.el6.x86_64.rpm
trying wget...
Comparing checksum with sha256sum...
Installing Chef 11.16.2
installing with rpm...
警告: /tmp/install.sh.2501/chef-11.16.2-1.el6.x86_64.rpm: ヘッダー V4 DSA/SHA1 Signature、鍵 ID 83ef826a: NOKEY
準備しています... ################################# [100%]
更新中 / インストール中...
1:chef-11.16.2-1.el6 ################################# [100%]
Thank you for installing Chef!
Generating node config 'nodes/54.64.8.173.json'...
nodes
ディレクトリに設定ファイルが作成されたので、内容を以下のように変更します。普通に先ほどBerksfile
で指定したCookbookのレシピをrun_listで指定したり、attribute
を指定しているだけです。
{
"run_list": [
"apache2-simple",
"git",
"jenkins",
"jenkins::http",
"jenkins::rubyenv",
"jenkins::plugin",
"packer"
],
"automatic": {
"ipaddress": "54.64.8.173"
},
"jenkins": {
"plugins": ["Git"]
}
}
ここまでできたら、仮想マシンに適用します。先ほどと違うのはコマンド引数がprepare
からcook
に変わっている点だけです。
bundle exec knife solo cook -i /path/to/your_key_pair.pem --no-host-key-verify ec2-user@YOUR_IP_OR_FQDN
数分まっていると以下のように表示されて完了します。
(前略)
- create new file /var/chef/cache/packer.zip
- update content in file /var/chef/cache/packer.zip from none to 2e0a79
(file sizes exceed 10000000 bytes, diff output suppressed)
- change mode from '' to '0644'
* execute[unzip -o /var/chef/cache/packer.zip -d /usr/local/bin/] action run
- execute unzip -o /var/chef/cache/packer.zip -d /usr/local/bin/
Recipe: apache2-simple::default
* service[httpd] action reload
- reload service service[httpd]
Recipe: jenkins::plugin
* service[jenkins] action restart
- restart service service[jenkins]
Running handlers:
Running handlers complete
Chef Client finished, 30/32 resources updated in 325.175386267 seconds
これでJenkinsが起動できたので、アクセスしてみましょう。80番ポートでアクセスできるようにしてあります。 なお、この状態は認証の設定などが一切ない状態なので、次の作業に入る前に、Jenkinsの管理
→グローバルセキュリティの設定
に移動して、セキュリティの設定を行ってください。
AMIの継続的インテグレーションの設定
実はここから先はそれほど大変ではありません。Jenkins上で新しいジョブを作成します。 適当なジョブの名前を入れてください。
ソースコード管理のところで、Git
を選択し、URLにpacker用のjsonファイルなどを配置しているレポジトリを選択します。ここでは僕が作っているサンプルであるhttps://github.com/ryuzee/packer-serverspec-aws-sample.git
を指定しています。
ビルドの設定の箇所で、シェルを指定します。シェルの内容は以下のようになります。 注意としては、僕のサンプルでは、PackerのProvisonerにChef Soloを使っているため、Cookbookの操作にRubyが必要で、それにrvmを使っているため、明示的に先頭でbashのシボンを設定している点です。 また、冒頭でJenkinsサーバを作成した際に、自動でPackerを/usr/local/bin/
以下に配置しているため、Packerのパスを明示しています。
#!/bin/bash
source ~/.bash_profile
gem install bundler --no-ri --no-rdoc
bundle install
\rm -rf cookbooks
bundle exec berks vendor cookbooks
/usr/local/bin/packer build aws.json
これで準備完了なので、ビルドしてみましょう。初回は、gemを大量に持ってきたりビルドするため時間がかかります(特にRidleyのビルドに)が、暫く待つとビルドが完了するはずです。
最後に
以上のようなやり方でAMIをJenkins経由でビルドできるようになります。前回の記事にあるようにServerspecによるテストを組み込んでおけば、テスト済みのAMIを継続的に簡単に自動で用意できます。
注意すべき点としては以下のようになります。
- 頻繁にビルドして大量にAMIを作ると、ワケがわからないことになる可能性がある。古いAMIを自動的に削除していくような世代管理のロジックを何らかの形で入れた方が良いかも
- ここではミドルウェアレベルのみがインストールされたAMIを作ってますが、Auto Scalingで利用するようなアプリケーション入りのAMIを作る場合、アプリの更新頻度とミドルウェアレベルの更新頻度には差があるので、AMIのビルドをいつ行うのかは検討が必要
- 場合によってはビルドパイプラインのような機能を使って、AMIをビルドするタイミングは人によるトリガーを必要にするという手もある
それでは。