Vagrant and Chef on Windows

===========================

[Vagrant](http://www.vagrantup.com)は[Chef](http://www.opscode.com/chef)で環境構築を自動化してくれるツールです。仮想マシンには[VirtualBox](https://www.virtualbox.org)などが利用できます。
Windowsは`ssh`や`rsync`などを追加すると[Knife Solo](http://matschaffer.github.io/knife-solo/)が使えます。

[Chef Soloの入門](http://tatsu-zine.com/books/chef-solo)としてRuby(rbenv)の環境を自動構築する方法を紹介します。
(WindowsにUNIX互換の環境を追加するだけなのでChefの使い方はMacやLinuxでも同じです。)


# Download & Install

こちらをWindowsのインストーラーでインストールします。

## VirtualBox

[VirtualBox for Windows](https://www.virtualbox.org/wiki/Downloads)をインストールします。
VirtualBoxの登場はここだけです。あとはVagrantで仮想マシンをコントロールします。

## Vagrant

[Vagrant for Windows](http://downloads.vagrantup.com)をインストールします。
(この手順のインストールディレクトリは`C:¥opt¥vagrant¥`です。)

VagrantにはRubyが必要なので[Ruby for Windows](http://rubyinstaller.org)がインストールされます。
これで[MinGW](http://www.mingw.org/)もインストールされるのでUNIX互換の環境が利用できます。

([RubyGems](http://docs.vagrantup.com/v2/installation/index.html)は`1.0.x`系なので注意しましょう。)

## Minimalist GNU for Windows

VagrantのMinGWにパッケージ管理ツールはないので[MinGW](http://sourceforge.net/projects/mingw/files/)をインストールします。
(この手順のインストールディレクトリは`C:¥opt¥MinGW¥`です。)

## msysGit

MinGWのパッケージにGitはないので[Git for Windows](http://msysgit.github.io/)をインストールします。
(この手順のインストールディレクトリは`C:¥opt¥git¥`です。)

# Terminal
Vagrantのインストールで[Mintty](https://code.google.com/p/mintty/)もインストールされています。
まず`C:\opt\vagrant\embedded\bin\mintty.exe`のショートカットを作ります。
そのショートカットの`リンク先(T)`に`/bin/bash --login`を追加します。


`.minttyrc`ファイルを設定していますが、通常はGUIで設定します。
(Minttyは背景を透過にできます。Macでも作業をするので透過はポイントです。)

```bash
$ cat << __EOS__ > ~/.minttyrc
Transparency=high
FontHeight=12
__EOS__
```

# Package

VagrantのMinGWに`mingw-get`コマンドを追加して`ssh`や`rsync`などをインストールします。

```bash
$ cp -r /c/opt/MinGW/. /c/opt/vagrant/embedded/
$ cd /c/opt/vagrant/embedded/var/lib/mingw-get/data/
$ cat defaults.xml|(rm defaults.xml;sed 's/%R\/msys\/1.0/\/opt\/vagrant\/embedded/g' > defaults.xml)
$ cat profile.xml|(rm profile.xml;sed 's/%R\/msys\/1.0/\/opt\/vagrant\/embedded/g' > profile.xml)
$ mingw-get update
$ mingw-get upgrade
$ mingw-get install msys-openssl msys-openssh
$ mingw-get install msys-rsync msys-tar
$ mingw-get install msys-vim
```

パッケージの一覧はこちらで確認できます。

```bash
$ mingw-get list | grep Package
```

# Editor

[Vim](http://www.vim.org/)をインストールしていますが***好きなエディター***を使いましょう。
WindowsのExplorerからの操作でVagrantfileやChefのRecipeを編集できます。

[Sublime Text](http://www.sublimetext.com/)はTerminalとExplorerから操作できるので使用しています。
`.bash_profile`ファイルにエイリアスを設定すると便利です。

```bash
$ cat << __EOS__ >> ~/.bash_profile
alias ll='ls -al'
alias ee='/c/opt/sublimetext2/sublime_text'
__EOS__
$ . ~/.bash_profile
```

# Virtual Machine

仮想マシンは[Getting Started](http://docs.vagrantup.com/v2/getting-started/index.html)と同じ`Ubuntu 12.01`を利用します。
ログインは`vagrant ssh`コマンドではなく`/.ssh/config`ファイルと`ssh`コマンドを使います。

```bash
$ vagrant box add precise32 http://files.vagrantup.com/precise32.box
$ mkdir -p ~/vagrant/precise
$ cd ~/vagrant/precise
$ vagrant init precise32
$ vagrant up
$ mkdir ~/.ssh
$ vagrant ssh-config --host precise >> ~/.ssh/config
$ ssh precise
...
Welcome to your Vagrant-built virtual machine.
```

これでゲストOSにログインできました。次は[Vagrantfile](http://docs.vagrantup.com/v2/vagrantfile/index.html)を設定して環境を自動構築しましょう。


# Chef

Vagrantは**Chef Solo**が利用されます。リモートマシンにリポジトリ(構築手順)を
`rsync`で同期させて`ssh`でコマンドを実行するシンプルな仕組みです。

```
ホストOS(Knife Solo) <-- ssh --> ゲストOS(Chef Solo)
[Chef Repository] <--- rsync ---> [Chef Repository]
```

## Knife Solo

Gemの`knife-solo`をインストールするとChefが使えるようになりますが
MSYSに対応していない部分があるのでソースからインストールします。

Cygwinと認識されてディレクトリの先頭に`/cygdrive/`を付けられるので`v0.2.0`をForkして[修正](https://github.com/ogom/knife-solo/commit/ae3cfd3f1e06a604e5efb3c48e4007d0ec0bb5e5)しました。
(CygwinとMSYSを`RbConfig`で区別できないものか… `pull request`をするなら`v0.3.x`系にします)

### Git

GitHubからソースを利用するので`git`コマンドを使えるようにします。
また、Chefのリポジトリ管理は[Git](http://git-scm.com/)を使います。

```bash
$ ln -s /c/opt/git/bin/git.exe /c/opt/vagrant/embedded/bin/git.exe
$ ln -s /c/opt/git/libexec/git-core /c/opt/vagrant/embedded/libexec/git-core
$ ln -s /c/opt/git/share/git-core /c/opt/vagrant/embedded/share/git-core
```

### Gem

ドキュメントが不要なら`.gemrc`ファイルに設定します。

```bash
$ echo 'gem: --no-ri --no-rdoc' > ~/.gemrc
$ gem install bundler
$ gem install rake
$ gem install ffi
```

修正した`msys`のブランチをインストールします。

```bash
$ git clone git://github.com/ogom/knife-solo.git
$ cd ./knife-solo/
$ git checkout msys
$ bundle && rake install
$ cd ..
$ rm -rf ./knife-solo/
```

### Cookbook

まずは簡単なレシピを作りましょう。`Hello, Chef!`が出力されると成功です。
(個人のクックブックは`site-cookbooks`に作ります。)

```bash
$ cd ~/vagrant/precise
$ knife configure
$ knife solo init chef-repo
$ cd ./chef-repo/
$ knife cookbook create hello -o ./site-cookbooks
$ echo 'log "Hello, Chef!"' >> ./site-cookbooks/hello/recipes/default.rb
$ echo '{"run_list":["hello"]}' > ./nodes/precise.json
$ knife solo prepare precise
$ knife solo cook precise
...
  * log[Hello, Chef!] action write
```

ここでGitのリポジトリを作ります。
リポジトリは`knife cookbook`コマンドの前に必要です。

```bash
$ git config --global user.email "user@example.com"
$ git config --global user.name "user"
$ git init
$ git add .
$ git commit -m 'first commit'
```

KnifeでゲストOSで'git'コマンドを使えるようにします。
(サードパーティーのクックブックは`cookbooks`を使います。)

```bash
$ knife cookbook site install git -o ./cookbooks
$ echo '{"run_list":["git"]}' > ./nodes/precise.json
$ knife solo cook precise
Recipe: git::default
  * package[git] action install
    - install version 1:1.7.9.5-1 of package git
$ ssh precise
vagrant@precise32:~$ git --version
git version 1.7.9.5
```

### Vagrantfile

先ほどのGitの`recipe`をVagrantに設定します。

```bash
$ cd ~/vagrant/precise/
$ cat << __EOS__ > ./Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "precise32"
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "./chef-repo/cookbooks"
    chef.roles_path = "./chef-repo/roles"
    chef.data_bags_path = "./chef-repo/data_bags"
    chef.add_recipe "git"
  end
end
__EOS__
```

マシンを削除してから、マシンを新しく作ります。

```bash
$ vagrant destroy --force
$ vagrant up
$ ssh precise
vagrant@precise32:~$ git --version
git version 1.7.9.5
```

マシンを再作成しても`git`コマンドが使えます。続いてRubyの環境を自動構築します。

# rbenv

[rbenv](https://github.com/sstephenson/rbenv)はRubyのバージョン管理ツールです。これを簡単にインストールできます。
Cookbookを管理するには[Berkshelf](http://berkshelf.com/)が便利です。

```bash
$ gem install berkshelf
```

先ほどのリポジトリに追加します。(rbenvはGitHubのリポジトリを利用します。)

```bash
$ cd ~/vagrant/precise/chef-repo
$ cat << __EOS__ > ./Berksfile
site :opscode
cookbook 'git'
cookbook 'ruby_build'
cookbook 'rbenv', github: "fnichol/chef-rbenv"
__EOS__
$ berks install --path ./cookbooks
```

まずはknifeで試します。

```bash
$ cat << __EOS__ > ./nodes/precise.json
{
  "run_list": [
    "git",
    "ruby_build",
    "rbenv::user"
  ],
  "rbenv": {
    "user_installs": [
      {
        "user": "vagrant",
        "rubies": ["1.9.3-p429"],
        "global": "1.9.3-p429",
        "gems": {
          "1.9.3-p429": [
            {"name": "bundler"}
          ]
        }
      }
    ]
  }
}
__EOS__
$ knife solo prepare precise
$ knife solo cook precise
$ ssh precise
vagrant@precise32:~$ which rbenv
/home/vagrant/.rbenv/bin/rbenv
vagrant@precise32:~$ ruby -v
ruby 1.9.3p429
```

`/home/vagrant/`パスに`rbenv`がインストールされています。最後にVagrantで試します。

```bash
$ cd ~/vagrant/precise/
$ cat << __EOS__ > ./Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "precise32"
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "./chef-repo/cookbooks"
    chef.roles_path = "./chef-repo/roles"
    chef.data_bags_path = "./chef-repo/data_bags"
    chef.add_recipe "git"
    chef.add_recipe "ruby_build"
    chef.add_recipe "rbenv::user"
    chef.json = {
      "rbenv" => {
        "user_installs" => [{
          "user" => "vagrant",
          "rubies" => ["2.0.0-p195"],
          "global" => "2.0.0-p195",
          "gems" => {
            "2.0.0-p195" => [
              {"name" => "bundler"}
            ]
          }
        }]
      }
    }
  end
end
__EOS__
```

マシンを再起動します。

```bash
$ vagrant reload
$ ssh precise
vagrant@precise32:~$ rbenv versions
  system
  1.9.3-p429
* 2.0.0-p195 (set by /home/vagrant/.rbenv/version)
vagrant@precise32:~$ ruby -v
ruby 2.0.0p195
```

Rubyのバージョンが変わりました。 ***レシピ通りです。*** (ｷﾘｯ


# Actor

アクターが多いので今回の関係を図にしました。

![chef_solo](https://dl.dropboxusercontent.com/u/14690051/images/notes/vm/vagrant/chef_solo.png)


# Tree

今回の主なファイルです。(Chefの[用語](http://wiki.opscode.com/display/chef/Glossary)は慣れましょう。)

```
.
└─vagrant               [ベイグラントの作業エリア]
    └─precise           [マシンの作業エリア]
        │  Vagrantfile   [ベイグラントの設定ファイル]
        │  
        └─chef-repo     [シェフのリポジトリ]
            │  Berksfile [バークシャーの設定ファイル]
            │  solo.rb
            │  
            ├─cookbooks [各種クックブック]
            │  ├─build-essential
            │  ├─chef_handler
            │  ├─git
            │  ├─rbenv [rbenvのクックブック]
            │  │  │  Berksfile
            │  │  │  Gemfile
            │  │  │  Rakefile
            │  │  │  Vagrantfile
            │  │  │  
            │  │  ├─attributes
            │  │  ├─libraries
            │  │  ├─providers
            │  │  ├─recipes [各種レシピ]
            │  │  │      default.rb
            │  │  │      system.rb
            │  │  │      system_install.rb
            │  │  │      user.rb
            │  │  │      user_install.rb [ユーザにrbenvをインストールするレシピ]
            │  │  │      vagrant.rb
            │  │  │      
            │  │  ├─resources
            │  │  ├─templates
            │  │  └─test
            │  └─ruby_build
            ├─data_bags
            ├─nodes
            ├─roles
            └─site-cookbooks
                └─hello
                    ├─attributes
                    ├─definitions
                    ├─files
                    ├─libraries
                    ├─providers
                    ├─recipes
                    │      default.rb [Hello, Chef!のレシピ]
                    │      
                    ├─resources
                    └─templates
```


# Tips

## Chef Solo

リモートマシンでChef Soloを実施することができます。

```bash
vagrant@precise32:~$ git clone git://github.com/opscode/chef-repo.git
vagrant@precise32:~$ cd ./chef-repo/
vagrant@precise32:~$ knife configure
vagrant@precise32:~$ knife cookbook create hello -o ./cookbooks
vagrant@precise32:~$ echo 'log "Hello, Chef!"' >> ./cookbooks/hello/recipes/default.rb
vagrant@precise32:~$ echo '{"run_list":["hello"]}' > ./localhost.json
vagrant@precise32:~$ echo 'file_cache_path "/tmp/chef-solo"' > ./solo.rb
vagrant@precise32:~$ echo 'cookbook_path ["/home/vagrant/chef-repo/cookbooks"]' >> ./solo.rb
vagrant@precise32:~$ sudo chef-solo -c ./solo.rb -j ./localhost.json
...
INFO: Hello, Chef!
```

Chef Soloで確認してからKnife Soloを使いましょう。