mikanmarusanのブログ

テクノロジーとかダイビングとか

vagrant + fabric + cuisineで開発環境を自動生成する

はじめに

9/28のDevOps Days Tokyo 2013に参加してきた。

Vagrantをつかってみた というエントリだけでやりっ放しにするのはかっこよくないし、ブログを書くまでがDevOps Daysと幹事の方も言っていたので(意味が違う)、vagrant + fabric + cuisineを使って開発環境を自動生成してみる。

DevOps Days TokyoではChefの話が多かったのだけど、こんなエントリ書いておきながらRubyはまだしっくりきていないし、Pythonのシンプルさが好みなので、Chef-SoloっぽいことができるPythonのライブラリを探してみると fabric というものがあるらしい。
そして、Chefといえば冪等性(何回同じ操作をしても同じ結果になること)だけど、cuisine を組み合わせれば冪等性が少しは担保できるらしい。

目的

  • Mac Book Air上に Ubuntu-12.04 LTSの仮想環境が5分で導入できること
    • AWSでもUbuntuを使っているので将来見据えて
  • git clone + vagrant up の2コマンドで完了できること
  • ついでだから Apache + Python (WSGI) でサンプルが動いていると面白い

やってみた

下準備

まずは、Virtual Box/vagrantの確認をしておく。以前、vagrantはruby gemでインストールしていたけれども、いまは Vagrant Downloads からダウンロードすればいい、楽チン。

Virtual Box: 4.2.0
vagrant: 1.3.3

Ubuntu自体は用意されているboxをそのまま利用

$ vagrant box add ubuntu-12.04 http://files.vagrantup.com/precise64.box
$ vagrant box list
ubuntu-12.04

fabricとcuisineのインストール

pipが入ってなければ、 sudo easy_install pip でインストールする。
easy_installだとインストールパッケージ一覧を出すのがしんどいし、pipの方が今風だからこっちにする。

$ sudo pip install fabric
$ vagrant plugin install vagrant-fabric
$ sudo pip install cuisine

Vagrantfile の記載

srcとvagrantディレクトリを作り、vagrantディレクトリの中で vagrant init <box名> を実行する。実行するとVagrantfileができる。

$ mkdir src vagrant (srcは後述のsynced_folderのところで使い道がある)
$ cd vagrant
$ vagrant init ubuntu-12.04

以下のようにVagrantfileを書き換える

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

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    # box名
    config.vm.box = "ubuntu-12.04"
    # ローカル環境でIPが使えるようにする
    config.vm.network :private_network, ip: "192.168.33.10"
    # synced_folder (親ホストのVagrantfileから見た時の ../src を 仮想環境の /src に割り当てる
    config.vm.synced_folder "../src", "/src", create: true, owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=777,fmode=666']

    # プロビジョニング(パッケージの追加や設定の反映)
    # provision.py に記載した setup メソッドを実行する
    config.vm.provision :fabric do |fabric|
        fabric.fabfile_path = "./provision.py"
        fabric.tasks = ["setup"]
    end
end
synced_folder

親ホストのディレクトリを仮想ホストのディレクトリを共有する仕組み。
開発は親ホスト上のお好きなエディタやIDEで実施し、仮想サーバで実行することができる。

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

例えば前述のVagrantfileに記載した内容の場合、下記のような共有ディレクトリが生成される。

  • 親ホストの ../src ディレクトリ(Vagrantfileからみて) と 仮想サーバの /src を共有する
  • 仮想サーバに /src が存在しなければ生成する(create: trueのところ)
  • ディレクトリオーナーとグループは vagrant にし、パーミッションは777、生成されるファイルのパーミッションは666になる。

ちなみにVagrantfileが設置されるディレクトリは自動的に synced_folder となり仮想サーバの方では /vagrant というディレクトリで参照できる。
なお、synced_folder については、下記記事が分かりやすかった。

プロビジョニングを記載する

Vagrantfileのコメントにも記載している通り、provision.py にプロビジョニングを記載する。
cuisine の *_ensure あたりを使うと冪等性が担保できるし、file I/Oをうまく使えばかなりの割合で冪等なプロビジョニングができると想像できる(sudoでコマンドを流すのは便利だが冪等性を担保しにくくなる。)

from fabric.api import run
from fabric.api import sudo
from fabric.utils import puts
from fabric.colors import red, green
from fabric.context_managers import *

import cuisine

cuisine.select_package("apt")

def setup():
        _setup_ubuntu()
        _setup_devtools()
        _setup_packages()
        _configuration_apache2()
        _restart_daemons()

# 初期設定系(なんとなく)
def _setup_ubuntu():
        puts(green('Setting Operation System'))
        sudo("cp /usr/share/zoneinfo/Japan /etc/localtime")
        sudo("apt-get update")

# 開発パッケージ系
# cuisine.package_ensure がいい感じ
def _setup_devtools():
        puts(green('Installing Devtools'))
        cuisine.package_ensure('vim')
        cuisine.package_ensure('python-setuptools')

# アプリケーション
def _setup_packages():
        puts(green('Installing Packages'))
        cuisine.package_ensure('libapache2-mod-wsgi')
        cuisine.package_ensure('apache2-mpm-worker')

# Apache2の設定
# 折角なので Apache設定ファイルを file_write で書き込み
# 設定ファイルの有効化とreloadをfabricのsudoで実行してみた
def _configuration_apache2():
        if not cuisine.file_exists('/etc/apache2/sites-enabled/hello'):
                cuisine.file_write(
                                location = '/etc/apache2/sites-available/hello',
                                content  = "WSGIScriptAlias /hello /src/hello.py"
                   "\n"
                   "<Directory /src>\n"
                   "  SetHandler wsgi-script\n"
                   "\n"
                   "  Order deny,allow\n"
                   "  Allow from all\n"
                   "</Directory>\n",
                                mode=None,
                                owner=None,
                                group=None,
                                sudo=True

                )
                sudo('a2ensite hello')
                sudo('/etc/init.d/apache2 reload')

# デーモンの再起動
def _restart_daemons():
        puts(green('Restarting Daemons'))
        cuisine.upstart_ensure('apache2')

../src ディレクトリには、Apache + mod_wsgiHello world が出力されるような hello.py を設置する。
Vagrantfileとprovision.py の設定により、http://192.168.33.10/hello で hello.py が実行される。

def application(environ, start_response):
    status = "200 OK"
    response_headers = [("Content-type", "text/plain")]
    start_response(status, response_headers)
    return ["Hello world! (mod_wsgi)"]

仮想サーバを起動

vagrant up を実行すれば、無事に仮想環境が立ち上がる。vagran ssh でログインすることができる。

$ vagant up
$ vagrant ssh
or
$ ssh vagrant@192.168.33.10 (pwはvagrant)

Vagrantの1.3.0からは、2回目以降の起動時にはプロビジョニングが実行されないので、2度目以降のupの場合は、下記を実行しなくてはならない。

$ vagant up --provision

親ホストの ../src ディレクトリで開発をすると、仮想サーバの /src 以下が自動的に変更されるので開発がらくちん。 ついでに../srcごとgithubにpushしちゃうとアプリケーション+仮想環境ごと複製できて飯がうまいうまい(ということで後述)

git(github)と連携する

githubに上げる時は、Vagrantfileと同じ場所に生成される .vagrant ディレクトリが追加されないように .gitignoreに記載することをお忘れなく(失敗すると面倒なことに)。

下記の通りに実行すると、仮想サーバを構築して Apache + PythonHello worldが!

$ git clone https://github.com/mikanmarusan/vagrant.git vagrant
$ cd vagrant/ubuntu/vagrant (ディレクトリ構成微妙)
$ vagrant up

まとめ

  • vagrantやっぱり便利
  • fabricとcuisineを使うとchef-soloっぽいのができる(けどシェルの高機能版って感じもする)
  • githubと連携できるとどこにいても同じ環境が作れる。