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 で、やりたい事はほとんど実現できていましたが、ターミナルでコマンドを打たずに済むように、いくつか工夫しています。

デモ

  1. VirtualBoxVagrant をインストール
    (インストーラで簡単に入ります。Ruby を入れたりする必要はありません)
  2. リポジトリgit clone またはダウンロード
  3. start.command をダブルクリックしてしばらく待つと、VirtualBoxCentOS 上で Nginx, Rails, MySQL が起動します
  4. http://192.168.33.10:8080/ にアクセスして Rails が起動しているのを確認します

VM が、app/sample 以下に Rails アプリケーションを生成します。Mac でファイルを書き換えて、VM で確認できます。DB は VM 上で動いています。

構成

こんな感じになっています。

f:id:koseki2:20140309164347p:plain

サーバの詳細を知る必要がない開発メンバーには、以下のように見えてほしい。VM が、プロジェクトのファイルを読んでブラウザに応答する一個のアプリケーションとして振る舞うようにします。

f:id:koseki2:20140309164346p:plain

プロジェクトの動作環境を VMカプセル化すると、別のプロジェクトに干渉する心配がなくなります。開発が終わったら VM を捨てれば元通りです。

また、VM と同じ設定でローカルでもサーバが動くようにします。エンジニアは Mac で直接サーバを動かした方が便利そうです。

f:id:koseki2:20140309164345p:plain

エンジニアが自分が使っていない設定はメンテされなくなってしまうので、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

のようになっています。

VMSSH 接続してシェルスクリプトを起動するコマンド

vagrant ssh -c 'コマンド' で、VMSSH してコマンドを実行することができます。Vagrant Box がパスフレーズ無しでログインできる sshd を起動してくれるおかげで、SSH 経由で簡単に VM を操作できます。*1

この仕組みを使って、ダブルクリックで VM 上のシェルスクリプトを起動するコマンドを作ります。

たとえば、

  • Rails や nginx をまとめて起動する start.command
  • Rails を単体で起動する sample.command
  • DB をセットアップしなおす reset_db.command

は、全て同じ中身になっています。

#! /bin/sh

DIR=`dirname $0`
NAME=`basename $0`

echo "--- VM ---"
cd $DIR/../vm
vagrant up
vagrant ssh -c "/vagrant/sandbox/bin/local/$NAME" 

vagrant upVM を(起動していなかったら)起動し、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 をダブルクリックした際に、VMprovision.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; 

を設定しないと、共有フォルダのファイルが壊れて見える場合があるようです。


Nginx に限らず、サーバはできるだけプロジェクトに入っている設定ファイルを直接読んで起動した方がいいと思います。/etc にコピーなどしてしまうと、設定をかえるたびに毎回プロビジョニングの実行が必要になってしまいます。設定ファイルがプロジェクトに入っていれば、再起動するだけで済みます。

VM を使わずに Mac 上でサーバを起動する

VM で foreman を起動する代わりに、Mac 上で

$ cd sandbox
$ foreman start

でサーバ一式を起動することができます。また、sandbox/bin/local/*command ファイルをダブルクリックしてローカルで実行できます。

Docker

この構成でやる前は Docker を試していました。0.8 から Mac がサポートされていますが、まだファイル共有がスムーズにいかないようです。

VirtualBox (boot2docker) + Docker の2階建ては、まだちょっとローカルから直に触れていない印象でした。将来的には Docker に切り替えるのもありかと思います。

Windows

ローカルと VM で同じスクリプトが動く利点は失われてしまいますが、 Windows でもバッチファイルを足せば同じように操作できるだろうと思います。

*1:ちなみに、ssh秘密鍵このファイルです。Vagrant をインストールすると、~/.vagrant/insecure-private-key にインストールされます。