Vagrant + chef-solo によるPHP + MySQLな開発環境構築入門

開発環境をローカルPC上の仮想サーバに構築することが当たり前になってきた昨今、
環境構築をもっと効率良く、楽にしたいということで、遅ればせながらVagrant+chef-soloをいじり始めました。
なんとかそれっぽい開発環境が構築できたのでまとめておきます。

とにかく動く環境をまず作ってみる、Vagrant + chef-soloで開発できるイメージを持つ、ことを重視して作りました。

 

開発環境の概要

大体こんな感じの環境を作ります。 vagrant_chef_article 今回はFedora20上のVirtualBoxに仮想サーバを構築しています。 もちろんWindows上でもOSX上でも構築可能です。

※Windowsの場合、以降で説明するコマンドと少し違った手順が必要になる可能性があります。

「PHP」となっている部分も、Rubyなど他の言語にも置き換えが効くと思いますが、 今回はPHPで構築しています。 ソースコードを共有フォルダで参照するというやり方も、 他にもっといいやり方があるかもしれませんがとりあえずこれで。

Vagrant, chef-solo って?

Vagrant

乱暴に言うと、コマンドラインからVirtualBoxなどの仮想化ソフトウェアを操作できるツールです。 「仮想マシンのフロントエンド」というような説明がされることも多いようです。

VirtualBoxにあたる部分はプロバイダと呼ばれ、プロバイダにはVMwareやAmazonEC2も使えるようです。 Vagrantを使わなくても、VirtualBoxだけで仮想サーバを構築することはできます。 ただ、

  • 仮想サーバの構築・破棄を素早く行う
  • Chefなどのインフラ構築自動化ツールと連携する

ようなことが、Vagrantを使うことによって実現できます。 そのため、より柔軟で素早く、再現性の容易な開発環境を構築することができるようになります。

chef-solo

インフラ構築を自動化してくれるツール「Chef」のスタンドアロン版です。 (Chef ServerとChef Clientによる、クライアント/サーバー版もあります)

インフラの構築作業をコードで記述することができるため、 構築する環境ごとに手動で構築作業をする必要がなく、 記述した通りの環境を正確に何度でも再構築することができるようになります。

必要なツールのインストール

開発環境構築に必要なツールをインストールしていきます。

VirtualBox

https://www.virtualbox.org/wiki/Downloads からパッケージをダウンロードしてインストールします。

今回はFedora 20を使って構築するため、
http://download.virtualbox.org/virtualbox/4.3.12/VirtualBox-4.3-4.3.12_93733_fedora18-1.x86_64.rpm
をダウンロードしてインストールします。

$ sudo yum install VirtualBox-4.3-4.3.12_93733_fedora18-1.x86_64.rpm

Vagrant

http://www.vagrantup.com/downloads.html からパッケージをダウンロードしてインストールします。

今回はFedora 20を使って構築するため、
https://dl.bintray.com/mitchellh/vagrant/vagrant_1.6.3_x86_64.rpm
をダウンロードしてインストールします。

$ sudo yum install vagrant_1.6.3_x86_64.rpm

Vagrantがインストールされていることを確認します。

$ vagrant -v
Vagrant 1.6.3

vagrant-omnibus

vagrant up(privision)の際に、仮想サーバにchefを自動インストールしてくれるvagrantのプラグインです。

$ vagrant plugin install vagrant-omnibus
Installing the 'vagrant-omnibus' plugin. This can take a few minutes...
Installed the plugin 'vagrant-omnibus (1.4.1)'!

プラグインのリストを確認します。

$ vagrant plugin list
vagrant-login (1.0.1, system)
vagrant-omnibus (1.4.1)
vagrant-share (1.1.0, system)

Ruby、RubyGems

後述するknife-soloのインストールにRubyGemsが必要なため、 RubyとRubyGemsをインストールします。インストールの詳細は割愛します。 私はRVMでインストールしています。

knife-solo

gemを使って、knife-solo をインストールします。

knifeとは、作成したクックブックをサーバに適用するためのコマンドを提供するツールです。
knife-soloはknifeのプラグインで、リモートサーバに対してローカルのクックブックやレシピを適用することができます。 knife-soloのインストールに伴って、依存関係のあるknifeも同時にインストールされます。

$ gem install knife-solo

また、knifeの初期設定も行っておきます。
いくつか設定項目を聞かれますが、すべてデフォルトのままで作成します。

$ knife configure

全体のディレクトリ構造

以下のディレクトリ構造となるように作業を進めていきます。

phpms/
├── Vagrantfile    -- Vagrant設定ファイル(vargant init で作成)
├── chef/          -- chefリポジトリ(knife solo init で作成)
└── src/           -- ソースコード
    └── phpms/     -- PHPソースコード

必要なディレクトリを作成して、移動しておきます。

$ mkdir -p phpms/src/phpms
$ cd phpms

Vagrantの設定

boxの追加、Vagrantfileの作成

vagrant initコマンドを実行します。VagrantfileというVagrantの設定ファイルが作成されます。
OSのboxイメージは、http://www.vagrantbox.es/ こちらに公開されているものから、CentOS6.5を使います。

$ vagrant init phpms https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

作成されたVagrantfileの中を確認すると、以下の項目が自動作成されています。

config.vm.box = "phpms"
config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box"

config.vm.box_url

‘vagrant up’コマンドで仮想サーバーを起動した際に、 boxがローカルにあればそれを適用して起動、ローカルに無ければbox_urlのアドレスからboxを取ってきて起動、という動作をしてくれます。

他にも、開発環境として利用するためにいくつか項目を追加する必要がありますが、とりあえずデフォルトのままにしておきます。Chefのクックブックやレシピを作成した後で編集します。

(参考) vagrant box add を使った作成

‘vagrant box add’, ‘vagrant init’ コマンドを使うことでも、boxの追加とVagrantfileの作成ができます。 ただしこの場合、Vagrantfile内の”config.vm.box_url”項目が自動作成されないようです。

他のメンバーにVagrantfileを渡した際に、’vagrant up’一発で仮想サーバー起動ができるようにしたかったため、今回は’vagrant init’コマンドで、box_urlを自動作成してもらうようにしました。
(手動でbox_url追加しても同じ話、なわけですが)

Vagrant起動

ここで一旦Vagrantで仮想サーバを起動します。 初回起動はboxのダウンロードも必要なため少し時間がかかります。

$ vagrant up
==> default: Box 'phpms' could not be found. Attempting to find and install...
default: Box Provider: virtualbox
default: Box Version: >= 0
==> default: Adding box 'phpms' (v0) for provider: virtualbox
default: Downloading: https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
==> default: Successfully added box 'phpms' (v0) for 'virtualbox'!
==> default: Importing base box 'phpms'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: phpms_default_1402016740766_62758
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
==> default: Forwarding ports...
default: 22 => 2200 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2200
default: SSH username: vagrant
default: SSH auth method: private key
default: Warning: Connection timeout. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
default: /vagrant => /path/to/phpms
==> default: Box 'phpms' could not be found. Attempting to find and install...

とあるように、phpmsというbox名が見つからないため、追加(インストール)してくれています。 box一覧を確認します。

$ vagrant box list
phpms (virtualbox, 0)

ホーム配下の.vagrant.dディレクトリにも仮想マシン用ファイルが作成されています。

~/.vagrant.d/boxes/
├── phpms
│ └── 0
│ └── virtualbox
│ ├── Vagrantfile
│ ├── box-disk1.vmdk
│ ├── box-disk2.vmdk
│ ├── box.ovf
│ └── metadata.json

起動した仮想サーバにsshでアクセスすることもできます。

$ vagrant ssh

Chefリポジトリの作成

vagrantを使って仮想サーバの起動ができたので、 起動されたサーバに適用するインフラ構成をChefで定義していきます。

まずはknifeを使って、Chefの実行に必要なChefリポジトリを作ります。 リポジトリの名前(ディレクトリ名)は”chef”とします。

$ knife solo init chef
Creating kitchen...
Creating knife.rb in kitchen...
Creating cupboards...

chefリポジトリの中には、 Chefの実行に必要なファイルとディレクトリが格納されています。

リポジトリのディレクトリ構成

knifeで作成されたリポジトリのディレクトリ構成です。

chef/
├── cookbooks
├── data_bags
├── environments
├── nodes
├── roles
└── site-cookbooks

多くのディレクトリやファイルがありますが、今回は、

  • site-cookbooks

しか使いません。

クックブックの作成

PHP + MySQL環境に必要な以下パッケージのクックブックを作成します。

  • apache
  • php
  • mysql
  • remi
  • localedef

自作のクックブックはsite-cookbooksに置くそうなので、site-cookbookに作成します。
※自作ではないクックブックというのは、コミュニティクックブックと言って、 WEB上に公開されているクックブックのことです。

$ cd chef/
$ knife cookbook create apache -o ./site-cookbooks
$ knife cookbook create php -o ./site-cookbooks
$ knife cookbook create mysql -o ./site-cookbooks
$ knife cookbook create remi -o ./site-cookbooks
$ knife cookbook create localedef -o ./site-cookbooks

レシピ、テンプレートの作成

インフラ構成を定義するためのレシピと、レシピが利用するテンプレートを書いていきます。
レシピは、各クックブックのrecipes配下に、テンプレートファイルは、各クックブックのtemplates/default配下に作成します。

作成するファイルを以下に示します。
各クックブックのrecipes/default.rb は”knife cookbook create”実行時に自動的に作成されています。

chef/
└── site-cookbooks
    ├── apache
    │   ├── recipes
    │   │   ├── default.rb  -- apacheインストール、起動、自動起動設定
    │   │   └── phpms.rb    -- phpms.vm.conf の配置
    │   └── templates
    │       └── default
    │           ├── httpd-2.2.conf.erb  -- httpd.confのテンプレート
    │           └── phpms.vm.conf.erb   -- phpms.vm.confのテンプレート(NameVirtualHost設定)
    ├── localedef
    │   └── recipes
    │       └── default.rb  -- localedef実行
    ├── mysql
    │   ├── recipes
    │   │   ├── createdb_phpms.rb  -- データベース、ユーザー、テーブル作成
    │   │   └── default.rb         -- MySQLインストール、起動、自動起動設定
    │   └── templates
    │       └── default
    │           ├── create_schema_phpms.sql.erb  -- テーブル作成SQLテンプレート
    │           ├── createdb.sql.erb             -- データベース作成SQLテンプレート
    │           └── createuser.sql.erb           -- DBユーザー作成SQLテンプレート
    ├── php
    │   └── recipes
    │       └── default.rb  -- PHPインストール
    └── remi
        └── recipes
            └── default.rb   -- remiインストール

localedef/recipes/default.rb

ファイル中の日本語を正常に読み込むためにロケール設定用のクックブックを作成します。
※これ分かるまで、日本語の文字化け状態が解消できずに結構ハマりました。。

bash 'localedef' do
  code 'localedef -f UTF-8 -i ja_JP ja_JP.UTF-8'
end

remi/recipes/default.rb

各パッケージの新しいバージョンをインストールするために、外部yumリポジトリremiのクックブックを作成します。

remote_file "#{Chef::Config[:file_cache_path]}/remi-release-6.rpm" do
  source "http://rpms.famillecollet.com/enterprise/remi-release-6.rpm"
  action :create
end
rpm_package "remi-release-6" do
  source "#{Chef::Config[:file_cache_path]}/remi-release-6.rpm"
  action :install
end

apache/recipes/default.rb

Apacheインストールのレシピです。

%w[
  httpd
  httpd-devel
  mod_ssl
].each do |pkg|
  package "#{pkg}" do
    action :install
  end
end

service 'httpd' do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end

template "/etc/httpd/conf/httpd.conf" do
  source "httpd-2.2.conf.erb"
end

serviceブロックの説明

supportでは、サービスが使えるアクションを定義するらしく、とりあえず指定しておきます。
actionでは、:enableで自動起動設定、:startでサービスの起動を指示します。

templateブロックの説明

「ローカルの”http-2.2.conf.erb”を 仮想サーバの”/etc/httpd/conf/httpd.conf”として配置する」
という意味になります。

apache/recipes/phpms.rb

後述のphpms.vm.confを配置するためのレシピです。

template "/etc/httpd/conf.d/phpms.vm.conf" do
  source "phpms.vm.conf.erb"
end

apache/templates/default/httpd-2.2.conf.erb

httpd.confのテンプレートです。
NameVirtualHostを有効にするために、http2.2のインストール時に作成されるhttpd.confファイルをコピーしてきて、 以下の内容を追記しただけ。

NameVirtualHost *:80

apache/templates/default/phpms.vm.conf.erb

ホスト名”phpms.vm”でアクセスした際の設定ファイルのテンプレートです。

<VirtualHost *:80>
    ServerName phpms.vm
    DocumentRoot /var/www/src/phpms/public

    <Directory /var/www/src/phpms/public>
        Options All
        AllowOverride All
        Order allow,deny
        Allow from all
   </Directory>
</VirtualHost>

php/recipes/default.rb

PHPをインストールします。 PHP5.5をインストールするために、”remi-php55″を有効にしています。

%w[
  php
  php-pdo
  php-mbstring
  php-mysqlnd
].each do |pkg|
  package "#{pkg}" do
    action :install
    options '--enablerepo=remi-php55'
  end
end

mysql/recipes/default.rb

新しいMySQLをインストールしたいので、mysql-commnityリポジトリを追加してからインストールします。

# add mysql yum repository
remote_file "#{Chef::Config[:file_cache_path]}/mysql-community-release-el6-5.noarch.rpm" do
  source 'http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm'
  action :create
end
rpm_package "mysql-community-release" do
  source "#{Chef::Config[:file_cache_path]}/mysql-community-release-el6-5.noarch.rpm"
  action :install
end

# install mysql community server
yum_package "mysql-community-server" do
  action :install
  flush_cache [:before]
end

service "mysqld" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end

mysql/recipes/createdb_phpms.rb

データベース、ユーザーを作成し、テーブルを作成するスキーマ定義スクリプトも実行します。

# create database
db_name = 'phpms'
execute "createdb" do
  command "/usr/bin/mysql -u root < #{Chef::Config[:file_cache_path]}/createdb.sql"
  action :nothing
  not_if "/usr/bin/mysql -u root -D #{db_name}"
end

template "#{Chef::Config[:file_cache_path]}/createdb.sql" do
  owner 'root'
  group 'root'
  mode 644
  source 'createdb.sql.erb'
  variables({
    :db_name => db_name,
    })
  notifies :run, 'execute[createdb]', :immediately
end

# create database user
user_name = 'phpms'
user_password = 'phpms'
execute 'createuser' do
  command "/usr/bin/mysql -u root < #{Chef::Config[:file_cache_path]}/createuser.sql"
  action :nothing
  not_if "/usr/bin/mysql -u #{user_name} -p#{user_password} -D #{db_name}"
end

template "#{Chef::Config[:file_cache_path]}/createuser.sql" do
  owner 'root'
  group 'root'
  mode 644
  source 'createuser.sql.erb'
  variables({
      :db_name => db_name,
      :username => user_name,
      :password => user_password,
    })
  notifies :run, 'execute[createuser]', :immediately
end

# create schema
schema_file = 'create_schema_phpms.sql'
execute "create_schema_phpms" do
  command "/usr/bin/mysql -u root #{db_name} < #{Chef::Config[:file_cache_path]}/create_schema_phpms.sql"
  action :nothing
  not_if "/usr/bin/mysql -u #{user_name} -p#{user_password} -D #{db_name}  -e 'show tables' | wc -l | xargs expr 1 /"
end

template "#{Chef::Config[:file_cache_path]}/create_schema_phpms.sql" do
  owner 'root'
  group 'root'
  mode 644
  source 'create_schema_phpms.sql.erb'
  notifies :run, 'execute[create_schema_phpms]', :immediately
end

mysql/templates/default/createdb.sql.erb

CREATE DATABASE <%= @db_name %> CHARACTER SET utf8;

mysql/templates/default/createuser.sql.erb

GRANT ALL ON <%= @db_name %>.* TO '<%= @username %>'@'localhost' IDENTIFIED BY '<%= @password %>';
GRANT ALL ON <%= @db_name %>.* TO '<%= @username %>'@'%' IDENTIFIED BY '<%= @password %>';

FLUSH PRIVILEGES;

mysql/templates/default/createuser.sql.erb

CREATE TABLE test
(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(64),
  CONSTRAINT pk_test PRIMARY KEY (id)
);

INSERT INTO test (name) VALUES ('テストデータ1');
INSERT INTO test (name) VALUES ('テストデータ2');
INSERT INTO test (name) VALUES ('テストデータ3');
INSERT INTO test (name) VALUES ('テストデータ4');
INSERT INTO test (name) VALUES ('テストデータ5');

PHPコードの記述

srcディレクトリ配下に、PHPの動作確認用のコードを追加しておきます。

src
└── phpms
    └── public
        └── select_test.php

src/phpms/public/select_test.php

<?php

$dsn = 'mysql:dbname=phpms;host=localhost';
$user = 'phpms';
$pass = 'phpms';
$db = null;
try {
    $db = new PDO($dsn, $user, $pass);
} catch (Exception $e) {
    echo $e->getMessage();
}

$sql = 'SELECT id, name FROM test';
foreach ($db->query($sql) as $row) {
    var_dump($row);
}

// end of file

Vagrantfileの編集

以下のようにVagrantfileを設定します。

# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "phpms"

  config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box"

  config.vm.network "private_network", ip: "192.168.33.104"

  config.vm.synced_folder "./src", "/var/www/src", :create => true, :owner => 'vagrant', :group => 'vagrant', :mount_options => ['dmode=777', 'fmode=666']

  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "chef/site-cookbooks/"
    chef.run_list = %w[
      recipe[localedef]
      recipe[remi]
      recipe[apache]
      recipe[apache::phpms]
      recipe[php]
      recipe[mysql]
      recipe[mysql::createdb_phpms]
    ]
  end

  config.omnibus.chef_version = :latest
end

主な設定項目

config.vm.network

private_network(ホストオンリーネットワーク)として、任意のIPアドレスを設定します。

ホストオンリーネットワークとは、ホストOSとゲストOS間でのみ通信できるネットワークです。
ホストOSから仮想サーバにアクセスする際には、ここで設定したIPアドレスを使用します。

config.vm.synced_folder

共有フォルダを設定します。

ここで設定したホストOSのフォルダは、ゲストOSからマウントされます。 この仕組みを利用して、ホストOSに存在するPHPソースコードを、ゲストOSから参照できるようにします。

config.vm.provision “chef_solo”

vagrantのプロビジョニングにchef soloを使うための設定です。

後述するvagrant upを実行すると、仮想マシンの起動とプロビジョニングが実行されます。
そのプロビジョニングの際にchef_soloを使うよう設定しています。
run_listには、実行するレシピを記述します。

config.omnibus.chef_version

先述の vagrant-omnibus を使って、 仮想サーバにchefがインストールされていない場合、自動的に最新バージョンをインストールしてもらうための設定です。

:latest という値からも、最新バージョンであることが読み取れるかと思います。

仮想サーバのプロビジョニング

以上のインフラ設定を、仮想サーバに適用します。

$ vagrant provision

PHP動作確認

PHPの動作確認をします。動作確認前に、hostsに想定するホスト名を設定します。

hostsの設定

phpms.vm というホスト名を想定しているため、hostsファイルに記述します。 Vagrantfileのprivate_networkに設定したIPアドレスを記述します。

192.168.33.104 phpms.vm

動作確認

下記のURLにアクセスすると、PHPが動作していることが確認できます。

  • http://phpms.vm/select_test.php

まとめ

以上のように、仮想サーバの環境構築を自動化することで、

  • 会社で作った環境を自宅で再現する
  • チームメンバーに開発環境を配布する

などのことが簡単にできるようになります。

今回作成したファイルは、githubにもpushしておきました。参考になれば幸いです。
https://github.com/shusatoo/phpms
ダウンロードしたファイルを適当なディレクトリに配置して、”vagrant up”を実行すれば仮想サーバーが起動します。 (VirtualBox, Vagrant, knifeがインストール済みであること)

Chefの正しい知識を身につけるならこの本で。
(購入しましたが、本記事はこの本読む前に書いてしまいました)

参考URL

以下のページを参考にさせていただきました。ありがとうございます。

今っぽい Vagrant + Chef Solo チュートリアル
ChefでMySQL5.6をインストール
構成管理ツール「Chef」の一歩進んだ使い方 ―独自のResourceを定義する―
Chef Soloの正しい始め方
仮想環境構築ツール「Vagrant」で開発環境を仮想マシン上に自動作成する
サーバー設定ツール「Chef」応用編:knife-soloとData Bagを使う
nanapi勉強会でVagrant + Berkshelfについて発表しました

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Post Navigation