Mac上のNFSをasync書き込み許可してVagrantを爆速にする - NFS of Vagrant on the Mac to detonation velocity share folder

**2015-04-29 更新**
VMWareをお持ちであれば、[Mac上のdockerでVMWareのネイティブフォルダシェアが実現した話](http://qiita.com/zchee/items/17a3e31577fa19509f49)をどうぞ。

# まえおき
業務で実際に仮想環境としてよく使われるようになったVagrant，Macでのローカルのファイル同期はnfsを使うのが一般的だと思いますが，そのnfsの設定を変更して，Vagrantを爆速にする話．
手元の環境では，大体２倍の速度が出ています．
大体はタイトルの通りasyncを効かせるんですが，日本国内でVagrantに適応している情報がなかったので，書いておきます．
結構単純なのにあまり情報がないのと，計測方法が分からないので，実はプラシーボなのかもしれません．そしたらすみません．

いちおう，コメントかtweet頂ければ，出来る限りお手伝いします．

# 注意
Vagrantの今の使用層を考えて，黒い画面が不慣れでも分かりやすいようにかなり冗長に書いているので，nfs早くするだけなら[tl;dr](#tldr)見てください．
**nfsについて知識があるわけでなく，海外で見つけた情報を元にした記事です**
**/etc/を触る比較的コアな設定ですので，自己責任にて．**
また，自分はいまDockerのためにしかVagrantを使用していない（ Mac -> Vagrant -> boot2docker -> Docker ）ので，通常のVagrant運用でどうかは試していません．
気が向いたら．

また、**サーバー**とか**クライアント**とか言うと，VagrantでのそれとSSHでのそれとでは意味が少しだけ違ってきて分かりづらくなるので，ここぞという時にしか極力使わないようにします．
併せて，Vagrant記事では基本的にどっちで（Mac側なのかVagrant内のLinux等なのか）コマンド打っているか分かりづらいので，
Macであれば `mac$ sudo vi hoge`
Vagrantであれば `vagrant$ sudo vi hoge`
で統一します．

# 緩募
* Vagrantとのnfs接続速度をbonnie++などで計る方法
* 普通にUbuntuなどをVagrantで立ち上げた時の動作確認

# tl;dr
On Mac,
`mac$ sudo vi /etc/nfs.conf`

```mac$nfs.conf
nfs.server.async=1
nfs.server.verbose=3
nfs.server.nfsd_threads=512
nfs.server.tcp=1
nfs.server.verbose=3
nfs.server.wg_delay_v3=1
nfs.client.allow_async=1
nfs.client.nfsiod_thread_max=512
nfs.client.mount.options=async,nfsvers=3,mountvers=3,rsize=8192,wsize=8192,acdirmax=3600,soft,intr,tcp
nfs.statd.verbose=1
```

and, nfs daemon restart
`mac$ sudo nfsd restart`
check nfsd status,
`mac$ sudo nfsd status`

```shell-session:mac$sudo_nfsd_status_result
nfsd service is enabled
nfsd service is loaded
nfsd is running (pid 43918, 512 threads)
lockd service is enabled
lockd service is loaded
lockd is running (pid 278)
statd.notify service is enabled
statd.notify service is loaded
statd.notify is not running
statd service is loaded
statd is running (pid 279)
rquotad service is loaded
rquotad is running (pid 44194)
```

Your Vagrantfile,
`mac$ vi Vagrantfile`

```rb:mac$Vagrantfile
Vagrant.configure("2") do |config|
  # ...
    config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ["async", "nolock", "nfsvers=3", "vers=3", "tcp", "noatime", "soft", "rsize=8192", "wsize=8192"]
  # ...
end
```

`mac$ vagrant up`

Done.

# 実現可能そうな環境
* OS X 10.9 〜
* Vagrant 1.7.2（2015/04/17 最新）
* VirtualBox・Parallels Desktop（VMWareは使える環境ですが試していません）

# 実際動いてる，手元の環境
Mac (RubyMine etc...) -> Vagrant -> boot2docker -> Docker

* OS X Yosemite 10.10.3
* Vagrant 1.7.2
* Parallels Desktop 10.2.0 (28956)
* boot2docker 1.6（今朝でましたね）
* Docker 1.6（今朝でましたね）


# いつものVagrantfile
通常nfs設定を有効にするVagrantfileの設定は，
http://docs.vagrantup.com/v2/synced-folders/nfs.html

```rb:Vagrantfile
Vagrant.configure("2") do |config|
  # ...

  config.vm.synced_folder ".", "/vagrant", type: "nfs"
  # ...
end
```

`type`でnfsを指定することにより，nfsで共有可能になります．
これだけで通常のVirtualbox共有などより早くなるのは周知の事実ですね．Qiitaでもメモされている方がいっぱいいらっしゃいます．

この設定で`mac$ vagrant up`すると，Mac側の`/etc/exports`に共有するフォルダ情報が書き込まれ，**Macをサーバーとして，daemonを立ち上げ**nfs共有が開始され，Vagrantで立ち上げたOSにマウントされます．（あってる？）

具体的に`/etc/exports`を見てみましょう．
`mac$ cat /etc/exports`

```s
# VAGRANT-BEGIN: 501 5c******-c***-4684-8e21-35289a3c****
"/vagrant" "/path/in/dir" 10.211.55.33 -alldirs -mapall=501:20
# VAGRANT-END: 501 5c******-c***-4684-8e21-35289a3c****
```

想像で書いたので実際と違うかもしれませんが．`/path/in/dir`と`10.211.55.33`はそれぞれ皆さんの環境によって違い，` -alldirs -mapall=501:20`はもしかしたら普通は書いてないかもしれません．

ともかく，**Macでnfs daemon**を立ち上げているので，Vagrantの裏設定の闇に入らずとも，少しはMac側でカジュアルにいろいろと設定できそうな雰囲気がしてきます．

# /etc/nfs.conf
Macでのnfs daemonの設定は．`/etc/nfs.conf`に記述します．
デフォルトでは，空のファイルだったかコメントがあるのみだったかと思います．

そこに，async（非同期）書き込みを許可する設定をします．

`vim`や`Sublime Text`（いまVagrant触ってるフロントエンドの人はSublime多そうな気がする．そしてThemeはSeti_UIな気がする．作者のctf0はいい人）で`nfs.conf`を開きます．
ただ，`/etc/`内なので管理者権限が必要ですので，`vim`なら`sudo`を付け，Sublimeの保存時にMacのパスワードを入れて保存してください．
`mac$ sudo vi /etc/nfs.conf`

```mac$nfs.conf
nfs.server.async=1
nfs.server.verbose=3
nfs.server.nfsd_threads=512
nfs.server.tcp=1
nfs.server.verbose=3
nfs.server.wg_delay_v3=1
nfs.client.allow_async=1
nfs.client.nfsiod_thread_max=512
nfs.client.mount.options=async,nfsvers=3,mountvers=3,rsize=8192,wsize=8192,acdirmax=3600,soft,intr,tcp
nfs.statd.verbose=1
```

![Screenshot 2015-04-18 00.40.41.png](https://qiita-image-store.s3.amazonaws.com/0/36099/f9f38291-c928-7915-4c1f-33c3fe2db4d2.png)


大体設定内容は見ればわかりそうな感じです．
詳しくは記事下部にあるリンク，apple manpageを見てください．

### nfs.server.async=1
async書き込み許可する．
デフォルトは0(許可しない)

### nfs.server.verbose=3
nfsログをDEBUGモードにする．
デフォルトは0．

### nfs.server.nfsd_threads=512
nfsスレッドを設定数に変更する
たぶんこれは多すぎます．Redhatのサイトとかだと20指定していました．
Macデフォルトは8．

### nfs.server.tcp=1
nfsでのTCPを許可する．
デフォルトでも1．

### nfs.server.wg_delay_v3=1
ちょっとよく覚えてませんが，設定するとオーバーヘッドが減るらしいです．
デフォルトは0．

### nfs.client.***
nfs.client以下は．Macがserverにあたるのであまり意味がないように思いますが，後述するVagrantfileへの設定を，実は書かなくても自動でやってくれるんじゃないかという期待を胸に一応書いています．
ただ，試していません．
現状動いているので，試すよりRails書いてたいです．


# nfsd restart
Macのnfs daemonを再起動して，設定を反映させましょう．
Macの場合は，以下で再起動できます．
`mac$ sudo nfsd restart`

何も表示されずに終わります．
不安なのでstatusをチェックします．
`mac$ sudo nfsd status`

```shell-session:mac$sudo_nfsd_status_result
nfsd service is enabled
nfsd service is loaded
nfsd is running (pid 43918, 512 threads)
lockd service is enabled
lockd service is loaded
lockd is running (pid 278)
statd.notify service is enabled
statd.notify service is loaded
statd.notify is not running
statd service is loaded
statd is running (pid 279)
rquotad service is loaded
rquotad is running (pid 44194)
```

![Screenshot 2015-04-18 00.39.05.png](https://qiita-image-store.s3.amazonaws.com/0/36099/90cd0bbb-89f1-e04e-96cb-e32c27e9dffe.png)

指定したスレッド数になっています．
それでも不安であれば，nfs.confのスレッドの数値を変更してrestartしましょう．
`nfsd is running`が変化するはずです．


# 再度Vagrantfile
Mac側でasyncの設定を許可したので，クライアント側でもasyncを設定します．
ついでに`private_network`も指定しましょう．特にDockerならあとで色々と楽です．

Vagrant自体がbuggyで何度もyakを刈っていたので嫌になり，今はDockerしか使っていないのなんとも言えませんが，原理的には，

```rb:Vagrantfile
Vagrant.configure("2") do |config|
  # ...
    config.vm.network 'private_network', ip: '192.168.55.33'
    config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ["async", "nolock", "nfsvers=3", "vers=3", "tcp", "noatime", "soft", "rsize=8192", "wsize=8192"]
  # ...
end
```
で良さそうな感じです．

動作確認している自分の`Vagrantfile`

```rb:Vagrantfile
Vagrant.configure("2") do |config|
  # ...
    config.vm.network 'private_network', ip: '192.168.55.33'
    config.vm.synced_folder ".", Dir.pwd, type: "nfs", mount_options: ["async", "nolock", "nfsvers=3", "vers=3", "tcp", "noatime", "soft", "rsize=8192", "wsize=8192"]
    config.vm.synced_folder "/Users", "/Users", type: "nfs", mount_options: ["async", "nolock", "nfsvers=3", "vers=3", "tcp", "noatime", "soft", "rsize=8192", "wsize=8192"]
  # ...
end
```

最初の設定と違い，かつ`/Users`以下すべてを共有する設定になっているのは，この設定がboot2docker用だからです．
お察しください．

ちなみにVagrantのデフォルトmount_optionsは
https://github.com/mitchellh/vagrant/search?utf8=%E2%9C%93&q=mount_options&type=Code
から適当に追ってください

# Vagrant up
これで`vagrant up`をすれば，幾分普通に起動したときより早いはずです．

# ベンチマーク
する方法がよく分からなくて困ってます．教えていただけると嬉しいです．
pythonサーバー建てて`wrk`で計測したりしましたが，単純な書き込み速度テストじゃない気がします．
`bonnie++`をDocker内で走らせてみましたが，なんか違う感じ．
本来はクライアントからnfsでマウントしてる`/mnt`とかにbonnie++する気がするんですが，どこにあるか分からない．

# 体感
最初にも書きましたが，自分の環境では
Mac -> Vagrant -> boot2docker -> Docker
でnfsを効かせて接続しています．
すごいニッチですが，RubyMineで`bundle install`した際にSSHでMac側にgemをsyncするんですが，それが爆速になりました．
あとは，Dockerでdocker pullも早くなってます．

# yak shaving
もしかしたら，他のVagrantを起動しようとしたときに`/etc/expors`のPermission errorが起きるかもしれない．
その場合，
`mac$ sudo rm -rf /etc/exports`
`mac$ sudo touch /etc/exports`

# 終わりに
次はMacのboot2dockerでnfs使って動作を早くする方法
その後にRubyMineを使ってDocker上でRails開発する方法と，chown問題を書きたいと思います．

# 参考
nfsd(8)
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/nfsd.8.html
nfs.conf(5)
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man5/nfs.conf.5.html
mount(8)
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/mount.8.html