はじめに
サーバや開発環境 (以下,インフラ) を手順書に従って構築することは手間がかかる.少なくない時間を費やす必要がある上に,ヒューマンエラーをはじめとした各種トラブルが発生する恐れもある.インフラ構築は基礎を勉強する上では役に立つが,肝心のアプリの開発や研究に割くための時間が圧迫されることは歓迎できない.
そこで Infrastructure as Code を導入する.インフラをコードで管理して構築作業を自動化し,あわせてテストを行うことでインフラが正しく構築されることを確認する. Infrastructure as Code により想定する環境をコマンド一撃で構築できるようになるため,たとえばチームメンバの開発環境をまったく同一に揃えることや,環境の作成と破棄を繰り返すといったことが手軽に行えるようになる.
以下,仮想環境構築ツールとして Vagrant を,インフラ構築を自動化するツール(プロビジョニングツール)として Ansible を,インフラ設定をテストするツールとして Serverspec を用いて Infrastructure as Code を試す.
利用するツール
Vagrant
Create and configure lightweight, reproducible, and portable development environments.
仮想環境構築ツール (VirtualBox, VMware, etc.) のフロントエンドであり,仮想環境を手軽に扱えるようにしてくれる.コマンド一撃で仮想環境を立ち上げたり破棄したりできるようになる.また,設定ファイルを使い回すことで同一の仮想環境を再現できる.
Ansible
Deploy apps. Manage systems. Crush complexity. Ansible is a powerful automation tool that you can learn quickly.
プロビジョニングツールの一つであり, OS やミドルウェアといったインフラの設定をコードで管理できる.コードには対象とするマシンのあるべき状態を記述する.対象のマシンに設定が適用される際には冪等性が確保される.同様のツールに Puppet, Chef, Itamae 等がある.
Serverspec
With Serverspec, you can write RSpec tests for checking your servers are configured correctly.
インフラの設定をテストするためのツールである.インフラのあるべき状態をコードに記述して実行することで,対象のマシンがコード通りの状態となっているか否かを調べて報告してくれる.インフラ構築後にはテストを実行し,問題なく通過することを確認する. TDD のように先にテストを書き,それからテストを通過するようにインフラ構築用のコードを書く方法もある (テスト駆動インフラ).
ツールのインストール
Vagrant
Download Vagrant からダウンロードしてインストールする.また,バックエンドとして用いる VirtualBox を Downloads – Oracle VM VirtualBox からダウンロードしてインストールする.
Ansible
Homebrew でインストールする.
brew update
brew install ansible
Serverspec
Ruby gem でインストールする.
gem 'serverspec'
bundle
ツールの初期設定
Vagrant
Box ファイルの設定
Vagrant では OS イメージを Box と呼ばれる形式で管理している.最初は Box ファイルが何もインストールされていない.
vagrant box list
There are no installed boxes! Use `vagrant box add` to add some.
有志の方々が様々な OS の Box ファイルを公開しているため,使用したいものをローカルに保存して利用する.本記事では CentOS 6.5 を centos65 というキーでインストールする.
vagrant box add centos65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
CentOS 6.5 (centos65) がインストールされた.
vagrant box list
centos65 (virtualbox, 0)
仮想環境の起動
適当な名前のプロジェクトディレクトリを作成して移動する.
mkdir -p ~/Documents/vagrant/test && cd $_
仮想環境を初期化する (仮想環境の設定を記述した Vagrantfile
を作成する).
vagrant init centos65
Vagrant.configure(2) do |config|
config.vm.box = "centos65" # Box ファイルは centos65 を使用する
config.vm.network "private_network", ip: "192.168.33.10" # 仮想環境の IP アドレス.ホストとの通信用
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024" # 仮想環境のメモリを 1GB とする
end
end
コマンド一撃で仮想環境が立ち上がる.
vagrant up
立ち上げた仮想環境に SSH で接続する.
vagrant ssh
[vagrant@vagrant-centos65 ~]$ cat /etc/system-release
CentOS release 6.5 (Final)
仮想環境を破棄することもできる.
vagrant destroy
以上, Vagrant を用いることで仮想環境を手軽に作成・破棄できる.
Ansible
Vagrant で立ち上げた仮想環境に Ansible で接続する.
まずは SSH の接続情報を ~/.ssh/config
に追記する.
vagrant ssh-config >> ~/.ssh/config
Host vagrant-test
HostName 127.0.0.1
User vagrant
Port 2222
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
PasswordAuthentication no
IdentityFile ~/Documents/vagrant/test/.vagrant/machines/default/virtualbox/private_key
IdentitiesOnly yes
LogLevel FATAL
次に Ansible の hosts ファイルを作成し,接続する仮想環境の情報を Ansible に伝える.
mkdir /usr/local/etc/ansible
echo "vagrant-test" > /usr/local/etc/ansible/hosts
Ansible と仮想環境の疎通確認を行う.
ansible vagrant-test -m ping
vagrant-test | success >> {
"changed": false,
"ping": "pong"
}
ping
に対して無事に pong
が返ってきた.
Serverspec
初期化コマンドを実行し,対話形式の質問に応答する.
bundle exec serverspec-init
Select OS type:
1) UN*X
2) Windows
Select number: 1
Select a backend type:
1) SSH
2) Exec (local)
Select number: 1
Vagrant instance y/n: y
Auto-configure Vagrant from Vagrantfile? y/n: y
+ spec/
+ spec/default/
+ spec/default/sample_spec.rb
+ spec/spec_helper.rb
+ Rakefile
+ .rspec
テスト実行に必要なファイルが配置された.
Infrastructure as Code
簡単なテスト駆動インフラ構築により Infrastructure as Code を試す.最初に Serverspec のテストを書き,このテストを通過するように Ansible で対象マシン (Vagrant で立ち上げた CentOS 6.5) の設定を行う.以下, web サーバ (nginx) の立ち上げを例にして記述する.
テスト作成
Serverspec のテストを記述する. Nginx のインストール・有効化・起動と, 80 番ポートの状態をテストする.
vim ~/Documents/vagrant/test/spec/default/nginx_spec.rb
require 'spec_helper'
describe package('nginx') do
it { should be_installed }
end
describe service('nginx') do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
テスト実行 (失敗)
対象マシンはまだ何も設定していないので,当然テストは失敗する.
bundle exec rake spec
...
(省略)
...
Failed examples:
rspec ./spec/default/nginx_spec.rb:4 # Package "nginx" should be installed
rspec ./spec/default/nginx_spec.rb:8 # Service "nginx" should be enabled
rspec ./spec/default/nginx_spec.rb:9 # Service "nginx" should be running
rspec ./spec/default/nginx_spec.rb:13 # Port "80" should be listening
インフラ設定
テストを通過するように Ansible で設定を行う. Ansible では playbook と呼ばれるファイルにマシンのあるべき状態を記述する.そして ansible-playbook
コマンドを実行することで,対象マシンの設定は playbook に記述された状態に収束する.
Nginx のインストール・有効化・(再)起動と, iptables の設定・有効化・(再)起動を行う.
mkdir ~/Documents/vagrant/test/ansible
vim ~/Documents/vagrant/test/ansible/nginx.yml
- hosts: vagrant-test
user: vagrant
sudo: yes
tasks:
- name: install nginx
yum: name=nginx state=latest
notify: restart nginx
- name: start nginx
service: name=nginx enabled=yes state=started
- name: set iptables
copy: src=iptables dest=/etc/sysconfig/iptables owner=root group=root mode=0744
notify: restart iptables
- name: start iptables
service: name=iptables enabled=yes state=started
handlers:
- name: restart nginx
service: name=nginx enabled=yes state=restarted
- name: restart iptables
service: name=iptables enabled=yes state=restarted
対象のマシンに転送する iptables
を作成する.
vim ~/Documents/vagrant/test/ansible/iptables
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
Nginx 関連の設定を行う playbook (+ iptables) が作成できたので,この内容を対象マシンに適用する.
ansible-playbook ansible/nginx.yml
PLAY [vagrant-test] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [vagrant-test]
TASK: [install nginx] *********************************************************
changed: [vagrant-test]
TASK: [start nginx] ***********************************************************
changed: [vagrant-test]
TASK: [set iptables] **********************************************************
changed: [vagrant-test]
TASK: [start iptables] ********************************************************
changed: [vagrant-test]
NOTIFIED: [restart nginx] *****************************************************
changed: [vagrant-test]
NOTIFIED: [restart iptables] **************************************************
changed: [vagrant-test]
PLAY RECAP ********************************************************************
vagrant-test : ok=7 changed=6 unreachable=0 failed=0
すべての処理が成功した.対象マシンに web ブラウザでアクセスすると,確かに nginx が立ち上がっている.
ここで,先ほどの playbook をもう一度適用してみる.
ansible-playbook ansible/nginx.yml
PLAY [vagrant-test] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [vagrant-test]
TASK: [install nginx] *********************************************************
ok: [vagrant-test]
TASK: [start nginx] ***********************************************************
ok: [vagrant-test]
TASK: [set iptables] **********************************************************
ok: [vagrant-test]
TASK: [start iptables] ********************************************************
ok: [vagrant-test]
PLAY RECAP ********************************************************************
vagrant-test : ok=5 changed=0 unreachable=0 failed=0
先ほどと異なりすべての処理の結果が changed
から ok
に変わっている.これは対象サーバがすでに playbook 通りの設定となっており変更の必要がなかったことを示している. Ansible は playbook の記述内容と対象マシンの設定を照らし合わせ,異なる箇所のみ設定を変更する.
テスト実行 (成功)
Ansible で対象マシンの設定を行ったので,改めて Serverspec でテストを行う.
bundle exec rake spec
...
(省略)
...
4 examples, 0 failures
今度は無事にテストを通過した.
おわりに
仮想環境構築ツール (Vagrant), プロビジョニングツール (Ansible), インフラ設定テストツール (Serverspec) を用いて簡単な Infrastructure as Code を試した.インフラ構築を自動化できるだけでなく,コードを参照することでインフラの状態を把握することもできて便利である.本記事では扱わなかったが,上述の一連の流れを継続的インテグレーションすれば,インフラ構築用コードの正しさが常に保証されるようになり,より便利になると思われる.