Vagrant を使って Mac からダブルクリックで操作できる仮想環境を作る
この記事を公開する直前に Vagrant Cloud がはてブに上がってるのを見て、今あわてて追記しています。すごい楽しいことになってますねー。
ブログ記事をざっと読んだ感じだと、Vagrant 1.5 の新機能は、
- VagrantShare - まるで公開されてるサイトみたいに VM の URL をチームメンバーに見せる
- Box を koseki/centos みたいな短い名前で指定できるように。バージョン管理
- rsync と SMB でフォルダ同期
- Hyper-V - MS の仮想環境に対応
- insecure-private-key の代わりに SSH でパスワードが使えるように
- プラグイン管理
- Funtoo, NetBSD, TinyCore Linux のゲストに対応。ネットワーク設定などができるように
特にウェブ開発をやっていると、VagrantShare が大きそう。使ってみるのが楽しみです。
VagrantCloud は、Box のリポジトリと VagrantShare の仲介をやってくれるみたい。コミュニティ向けには無料、ということなので、パブリックな Box のホスティングは無料ということなんでしょうか。すばらしい。
というわけで本題。メジャーリリースの 1.5 でも後方互換は保たれているということなので、書いてた記事をそのまま公開です。ぽちり。
マウス操作で仮想環境
サーバ上で実行したい各種タスクを Mac からダブルクリックで起動できる、仮想環境のサンプルを作りました。VirtualBox + Vagrant を使います。
デザイナやフロントエンドエンジニアが、本番と同様のレスポンスを確認しながら開発できるように、手軽な仮想環境のセットアップ方法がほしい、というのがそもそもの動機です。
Vagrant で、やりたい事はほとんど実現できていましたが、ターミナルでコマンドを打たずに済むように、いくつか工夫しています。
デモ
- VirtualBox と Vagrant をインストール
(インストーラで簡単に入ります。Ruby を入れたりする必要はありません) - リポジトリを
git clone
またはダウンロード start.command
をダブルクリックしてしばらく待つと、VirtualBox の CentOS 上で Nginx, Rails, MySQL が起動します- http://192.168.33.10:8080/ にアクセスして Rails が起動しているのを確認します
VM が、app/sample
以下に Rails アプリケーションを生成します。Mac でファイルを書き換えて、VM で確認できます。DB は VM 上で動いています。
構成
こんな感じになっています。
サーバの詳細を知る必要がない開発メンバーには、以下のように見えてほしい。VM が、プロジェクトのファイルを読んでブラウザに応答する一個のアプリケーションとして振る舞うようにします。
プロジェクトの動作環境を VM にカプセル化すると、別のプロジェクトに干渉する心配がなくなります。開発が終わったら VM を捨てれば元通りです。
また、VM と同じ設定でローカルでもサーバが動くようにします。エンジニアは Mac で直接サーバを動かした方が便利そうです。
エンジニアが自分が使っていない設定はメンテされなくなってしまうので、VM でもローカルでもなるべく同じ設定を使います。
ディレクトリ構成
. ├── LICENSE ├── README.md ├── app │ └── sample …… VM が Rails を生成する空のディレクトリ ├── htdocs │ ├── index.html │ └── static │ └── index.html └── sandbox …… 設定の本体。プロジェクトのルートに、この sandbox を足して使うイメージです ├── Procfile ……foreman の設定。Nginx と Rails をまとめて起動 ├── bin │ ├── halt.command …… VM をシャットダウンします │ ├── reset_db.command …… VM に SSH して local/reset_db.command を起動 │ ├── sample.command …… VM に SSH して local/sample.command を起動 │ ├── ssh.command …… VM に SSH します │ ├── start.command …… VM に SSH して local/start.command を起動 │ ├── status.command …… VM の起動状態を表示します │ ├── up.command …… VM を起動します │ ├── local …… VM またはローカルで実行するシェルスクリプト │ │ ├── reset_db.command …… DB をリセットします。rake db:setup を実行 │ │ ├── sample.command …… rails server を実行 │ │ └── start.command …… foreman start を実行 │ └── provision.command …… プロビジョニングを実行 ├── conf …… サーバ設定。このサンプルでは Nginx の設定が入っています │ ├── mime.types │ └── nginx.conf ├── logs ├── tmp └── vm ├── Vagrantfile …… Vagrant 設定 ├── provision.sh …… プロビジョニングスクリプト。Mac 上で実行されることはないので .sh です └── uninstall.command …… VM をアンインストールします
コマンドファイル
OSX で、シェルスクリプトの拡張子を .command
にして実行権限を付けると、ダブルクリックで実行できるようになります。
*.command
ファイルを使って、vagrant
コマンドを実行します。たとえば VM を起動する up.command
は、
#! /bin/sh DIR=`dirname $0` cd $DIR/../vm vagrant up
のようになっています。
VM に SSH 接続してシェルスクリプトを起動するコマンド
vagrant ssh -c 'コマンド'
で、VM に SSH してコマンドを実行することができます。Vagrant Box がパスフレーズ無しでログインできる sshd を起動してくれるおかげで、SSH 経由で簡単に VM を操作できます。*1
この仕組みを使って、ダブルクリックで VM 上のシェルスクリプトを起動するコマンドを作ります。
たとえば、
は、全て同じ中身になっています。
#! /bin/sh DIR=`dirname $0` NAME=`basename $0` echo "--- VM ---" cd $DIR/../vm vagrant up vagrant ssh -c "/vagrant/sandbox/bin/local/$NAME"
vagrant up
で VM を(起動していなかったら)起動し、local ディレクトリ以下の同名のスクリプトを実行します。
最後の行で SSH してシェルスクリプトを実行しています。プロジェクトのルートを /vagrant にマウントしているので、/vagrant/sandbox/bin/local
は、このディレクトリが見えています。
local
以下の *.command
ファイルがスクリプトの本体です。たとえば local/start.command
は、
#! /bin/sh set -e DIR=`dirname $0` cd $DIR/../.. foreman start
のようになっています。
Foreman でサーバをまとめて起動・終了する
Foreman は最近教えてもらって知ったのですが、簡潔ですごくいいアプリケーションでした。OSに標準でついててほしい。
Foreman が Procfile を読んで、Nginx と Rails を起動します。
nginx: nginx -p . -c conf/nginx.conf rails_sample: bin/local/sample.command
start.command
をダブルクリックすると、Nginx と Rails が起動し、ターミナルにログが流れます。Ctrl-C
で起動したサーバをまとめて停止します。
サーバの設定を読み込み直すときは、Ctrl-C
で止めてダブルクリックで再起動です。
Procfile
は、Mac 上でサーバを起動する時にも使えます。
$ cd sandbox $ foreman start
プロビジョニング
サーバの構成は、開発が進むにつれて変化していきます。環境が変わるたびに VM をセットアップし直しになっては困ります。
このサンプルでは、シェルスクリプトでプロビジョニングする Vagrant の機能を使っています。
config.vm.provision :shell, :path => 'provision.sh'
これで、初回の起動時と、provisioning.command
をダブルクリックした際に、VM で provision.sh が実行されます。
Vagrant は path で指定されたファイルを VM にアップロードして実行するだけのようです。
プロビジョニングのたびに毎回同じスクリプトが実行されるのでは使いにくいので、自前で簡単なバージョン管理を実装します。
# バージョン番号を記録するファイル VERSION_FILE=/root/.provisioning_version # バージョンファイルがあったら CURRENT_VERSION に読み込む。なかったら 0 if [ -f $VERSION_FILE ]; then CURRENT_VERSION=`cat $VERSION_FILE` else CURRENT_VERSION='0' fi
のようにして、最後に実行したプロビジョニングのバージョンを読み込みます。あとは以下のようにして、バージョン番号が付いたブロックを足していきます。
VERSION=1 if [ $CURRENT_VERSION -lt $VERSION ]; then echo "--- $VERSION ---" # 何かする。 # バージョンファイルに $VERSION を書き込み echo $VERSION > $VERSION_FILE fi
ファイルに記録された $CURRENT_VERSION より $VERSION の方が数字が大きかったら処理を実行します。
原始的ですが、開発サーバ向けにはこの程度でも良いのかなという気がしました。セキュリティの要件などが全然違うので、Chef などを使って本番と同様のプロビジョニングを実行するのは大変そうです。
あるいは、一般的な構成なら Vagrant Box を公開してもいいかもしれません。プロジェクトを /vagrant
にマウントして使うため、VM にはプロジェクトに関わるファイルを一切含めないことが可能です。
Nginx
Nginx は sandbox/conf/nginx.conf を直接読んで起動します。
nginx -p . -c conf/nginx.conf
daemon off;
でデーモン化せず、ターミナルにログを書きながら実行します。
sendfile off;
を設定しないと、共有フォルダのファイルが壊れて見える場合があるようです。
- http://httpd.apache.org/docs/2.2/mod/core.html#EnableSendfile
- http://docs-v1.vagrantup.com/v1/docs/config/vm/share_folder.html
Nginx に限らず、サーバはできるだけプロジェクトに入っている設定ファイルを直接読んで起動した方がいいと思います。/etc
にコピーなどしてしまうと、設定をかえるたびに毎回プロビジョニングの実行が必要になってしまいます。設定ファイルがプロジェクトに入っていれば、再起動するだけで済みます。
VM を使わずに Mac 上でサーバを起動する
$ cd sandbox $ foreman start
でサーバ一式を起動することができます。また、sandbox/bin/local/*command
ファイルをダブルクリックしてローカルで実行できます。
Docker
この構成でやる前は Docker を試していました。0.8 から Mac がサポートされていますが、まだファイル共有がスムーズにいかないようです。
VirtualBox (boot2docker) + Docker の2階建ては、まだちょっとローカルから直に触れていない印象でした。将来的には Docker に切り替えるのもありかと思います。