最近インフラの話題が熱いです。
chefを使ってインフラ構築がコード化(Infrastructure as Code)ができるようになった事でプログラムソースと同じく、サーバーの構築手順などもコードとしてgitなどで管理できるようになりました。
そうするとプログラマたちがJenkins等のCIツール(継続的インテグレーション)で自動テストしているのもやりたくなってきます。
インフラのCIにおいてVagrantやserverspecといったツールがこれらのCI環境の整備を後押しした事もあり、インフラCIの手順などの記事も増えて、とてもワクワクしています。
(参考)
Vagrant + Chef Solo + serverspec + Jenkins でサーバー構築を CI
CIする所をサーバーにしたい
いろんな記事では基本的にMacOSXにjenkins vagrant virtualbox chef を入れての作業になってます。
また、vagrant-awsなどを使用してもそれは同じで、あくまでもローカルのMACからawsを操作する内容になっています。
インフラがコード化されて、gitで管理されたとなるとやっぱり共同で開発していきたいと思うのですが、インフラCIの為にMAC/Windowsのそれぞれのクライアントに「vagrant」「jenkins」「chef」・・・ と入れてもらうのは敷居が高いしちょっとなんだかなぁと思ってました。
そこで、今回はAWSのEC2インスタンスにvagrant + Jenkins + chef-solo + serverspecを入れてCIする環境を構築したいと思います。
ツールのおさらい
vagrant
仮想サーバーの構築や破棄をコマンドで行える仮想環境構築ツール。CIする上で一番うれしいのは立ち上げたサーバへのSSHなどの接続まわりのすべてを面倒みてくれるところかな、と思います。
chef
サーバー構築の自動化フレームワークツールです。「冪等性を保証する」というポリシーで作成します。冪等性とは1回行っても複数回行っても結果が常に同じであるような事をいいます。冪等性を保証する事で、単なるサーバー構築を行うツールではなく、何度実行してもサーバーが設定どおりの状態となる事を保障できるツールとなります。
serverspec
サーバにApacheが入っているか、80ポートが空いているか、など、意図した通りにサーバが稼働しているかどうかをチェックできるツールです。
jenkins
CI(継続的インテグレーション)ツールです。jenkinsを使う事で「テスト」や「ビルド」、はては「デプロイ」まで自動化する事ができます。
ゴール
・gitのリポジトリの特定ブランチにpushがあると
・jenkinsが捕捉して、ビルドを実行
・vagrantにてEC2インスタンスを立ち上げてchefを流してサーバー構築
・サーバー構築が完了するとserverspecでサーバー状態をチェック
・結果をjenkinsで管理
jenkinsサーバーの構築
ともあれ、CIを行う為のサーバーが必要なのでEC2でインスタンスを立ち上げます。
今回はCentOSを使いました。
こちらのインスタンスにvagrant、Jenkins、chef-solo、serverspecをインストールしていきます。
EPELリポジトリを追加
1wget http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
2rpm -ivh epel-release-6-8.noarch.rpm
3
パッケージをインストール。いらないものもあると思うので適当に割愛してください
1yum install -y make gcc gcc-c++ xinetd openssl-devel libcurl-devel zlib-devel readline-devel bzip2-devel curl-devel libmcrypt libmcrypt-devel sudo redhat-lsb yum-utils yum-plugin-priorities yum-plugin-fastestmirror yum-plugin-security
2yum install -y mlocate bind-utils dstat elinks multitail nc nmap rsync traceroute tree unzip wget which zip zsh mosh uuid telnet
3yum install -y autoconf automake bison bzip2 gettext-devel libtool libxml2-devel libxslt-devel libyaml-devel libffi-devel ncurses-devel patch
4yum install -y git iftop tig python-pip rpm-build ImageMagick ImageMagick-devel jq
5yum install -y java-1.7.0-openjdk java-1.7.0-openjdk-devel
6
AWS CLIのインストール(任意)
1pip install awscli
2
chefのインストールとchefで入ったrubyにpathを通します
1curl -L https://www.opscode.com/chef/install.sh | bash
2echo "export PATH=\"/opt/chef/embedded/bin:\$PATH\"" > /etc/profile.d/chef_embedded.sh
3source /etc/profile.d/chef_embedded.sh
4
chefで入ったgemでserverspecなどをインストール
1/opt/chef/embedded/bin/gem install rake --no-ri --no-rdoc
2/opt/chef/embedded/bin/gem install serverspec --no-ri --no-rdoc
3/opt/chef/embedded/bin/gem install ci_reporter --no-ri --no-rdoc
4/opt/chef/embedded/bin/gem install unf --no-ri --no-rdoc
5
vagrantのインストール
1wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.6.5_x86_64.rpm
2rpm -i vagrant_1.6.5_x86_64.rpm
3vagrant plugin install vagrant-aws
4vagrant plugin install unf
5vagrant plugin install vagrant-serverspec
6vagrant plugin install vagrant-global-status
7vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box
8
jenkinsのインストール
1sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
2sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
3sudo yum -y install jenkins
4
各種設定
以下のようなディレクトリ構成とする事にします
1vagrant/
2Vagrantfile
3spec/
4spec_helper.rb
5hogehoge/
6httpd_spec.rb
7chef/cookbooksとかいろいろ
8
chefのレシピを配置
テストしたいchefのレシピたちを配置します。割愛
serverspecの設定
spec_helper.rbはこんな感じです
1require 'serverspec'
2require 'pathname'
3require 'net/ssh'
4include SpecInfra::Helper::Ssh
5include SpecInfra::Helper::DetectOS
6
httpd_spec.rbはこんな感じです
とりあえずポート80が空いてるかどうかのテスト
1require '../spec_helper'
2describe port(80) do
3it { should be_listening }
4end
5
vagrantの設定
1mkdir /path/to/vagrant
2cd /path/to/vagrant
3vi Vagrantfile
4
以下はサンプル。
vagrant-awsの設定とprovisionとして「シェルの実行でchefをインストール」「chef-soloの実行」「serverspecの実行」を行っています。
1# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
2# usage vagrant up --provider=aws
3VAGRANTFILE_API_VERSION = "2"
4Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
5config.vm.box = "dummy"
6config.vm.provider :aws do |aws, override|
7aws.access_key_id = 'xxxxxxxxxxxxx'
8aws.secret_access_key = 'xxxxxxxxxxxxx'
9aws.region = 'ap-northeast-1'
10aws.instance_type = 't2.micro'
11aws.ami = 'ami-xxxxx'
12aws.security_groups = ['sg-xxxxxx','sg-xxxxx']
13aws.keypair_name = 'xxxx'
14aws.ssh_host_attribute = :private_ip_address
15override.ssh.username = 'root'
16override.ssh.private_key_path = '/path/to/key'
17aws.subnet_id = 'subnet-xxxxxx'
18aws.associate_public_ip = 'true'
19aws.tags = { 'Name' => 'CI' }
20end
21# shell exec
22config.vm.provision :shell, :path => "curl -L https://www.opscode.com/chef/install.sh | bash"
23# chef exec
24config.vm.provision "chef_solo" do |chef|
25chef.cookbooks_path = ["../chef/cookbooks", "../chef/site-cookbooks"]
26chef.roles_path = "../chef/roles"
27chef.run_list = ["role[xxxxxxxx]"]
28end
29# serverspec
30config.vm.provision :serverspec do |spec|
31spec.pattern = '../spec/hogehgoe/*_spec.rb'
32end
33end
34
vagrant-awsにおけるAWSのアクセスキーや秘密鍵。セキュリティグループの設定等は割愛。
注意点としてprovision shellを実行する時にno ttyと出る時があります。
これは立ち上げるAMIの/etc/sudoers 内のDefaults requiretty行をコメントアウトすれば解決するかもしれません。
立ち上げるインスタンス用のAMIはこれらvagrantが接続してprovisionができる環境を整えたものであるといいかと思います。(例えばchefが入ってるAMIですとchefのインストールをvagrantから行う必要がなくなります)
vagrantで仮想サーバーが立ち上がり、chefをインストールし、chefを流し、serverspecでテストまで流れるかテストします。
1vagrant up --provider=aws
2
もう一度chefとかを流したりしたい場合は以下のコマンドで実行できます。
1vagrant provision
2
任意のprovisionのみ実行したいときは以下のような感じになります。
1vagrant provision --provision-with serverspec
2
確認できたら消します。ちなみにAWSでは1分でも立ちあげると1時間ぶんの料金がかかりますので注意です。
1vagrant destroy
2
jenkinsの設定
jenkinsからはvagrantコマンドを実行したりする予定ですが、root権限が必要なのでsudo設定等を行います
visudoで以下を追記
1Defaults:jenkins !requiretty
2jenkins ALL=(ALL) NOPASSWD:ALL
3
Defaults requirettyとかあったらコメントアウトしておきます。
jenkinsの起動
1/etc/init.d/jenkins start
2
http://サーバーIP:8080 でjenkinsさんがみえます。
jenkinsにgitプラグインを入れる
[Jenkinsの管理]-> [プラグインの管理] -> [利用可能タグ]でGit Pluginをインストールします。
新規ジョブを作成
[新規ジョブ作成]->[フリースタイル・プロジェクトのビルド]を選択してプロジェクト名を適当に入力して作成します。
プロジェクトの設定(git)
作成したプロジェクトをクリックしてプロジェクトの画面に進み[設定]をクリックします。
ソースコードの管理でgitを選択して監視を行うリポジトリとブランチを設定します。
今回はmasterブランチです
プロジェクトの設定(ビルド・トリガ)
ビルド・トリガを設定します。
通常であればgitのリポジトリ側からpushがあった場合にjenkinsに通知する事でビルドが実行されるのがよいのですが、簡単な方法として、jenkins側から定期的にgitリポジトリに更新を確認しにいくポーリングを設定しました。
この設定ですと、10分に1回更新を確認しにいき、pushされていればビルドを実行する設定になります。
プロジェクトの設定(ビルド)
最後にビルド設定です。
ビルドは「シェルの実行」を選択します。
ビルド実行でvagrantを立ち上げ、テストして落とすという処理となりますので以下のようなコードとしました。
このときのシェルはjenkinsユーザーが実行するので適宜sudo等をつけてあげる必要があります。
1cd /path/to/vagrant
2sudo vagrant up || RET1=$?
3sudo vagrant provision --provision-with serverspec > /tmp/serverspec.tmp
4cat /tmp/serverspec.tmp
5SPEC=`cat /tmp/serverspec.tmp | grep "0 failures" | wc -l`
6RET2=0
7if [ $SPEC -ne 1 ]; then RET2=1 ; fi
8rm -f /tmp/serverspec.tmp
9sudo vagrant destroy
10if [ $RET1 -ne 0 ]; then exit $RET1 ; fi
11if [ $RET2 -ne 0 ]; then exit $RET2 ; fi
12exit 0
13
途中ややこしい事になっていますがvagrant-serverspecプラグインでのserverspecの結果は成功/失敗によらず正常終了となるので、serverspecが失敗した場合はjenkinsでFailとなるようにすこし調整しています。
最終的にはこんな感じになりました。(gitの部分はエラーになってますが)
この設定でchefのレシピをCIできるようになりました。