Rubyで作ったスクリプトを動かしたら"Out of Memory"と怒られた。

# 問題発生

Rubyでスクレイピングのスクリプトを作った。
Crontabに登録して走らせたいが、うまく走らない。
__"Out of Memory"__ とのこと。

ローカルの開発環境（MacBookAir）では問題なく走る。

そんなにガシガシスクリプトを書いて動かしたこともないし、
"Out of Memory"なんて怒られたこともないので、
解決方法がわからない。

_--- 単にサーバーのメモリ不足？_
_--- スワップを用意すれば回避できる？_
_--- スクリプトの中に、無断な箇所がある？_


## 症状

__xvfb__を使った仮想ディスプレイに__firefox__を立ち上げて、
Rubyにて__WebDriver__を走らせてスクレイピングさせるスクリプトを作った。

最初はmechanizeを用いて書いていたが、
ブラウザ上でJavaScriptを動かさなきゃいけないので、
Seleniumを選択。

crontabでスクリプトを２つ同時に走らせたところで、
しばらくすると、"Out of Memory"とエラーを吐いてスクリプトが落ちる。


## 環境

- [Vultr](https://www.vultr.com/)
- __1CPU__
- メモリ__768MB__。

CentOS 6.6
ruby 2.2.1


# 対策

Vultrのプランをあげるのは嫌なので、
サーバーのスペック／メモリの増強は考えない。

## 疑問

### Q.そもそも Swapファイル作った覚えがないけど、最初から作ってくれてるの？

[メモリとSwap領域の確認](http://blueeyesblue.cocolog-nifty.com/technote/2010/03/swap-ad70.html)

```bash
$ /sbin/swapon -s
Filename				Type		Size	Used	Priority
```
なくない？

```bash

$ cat /proc/meminfo 
MemTotal:         760132 kB
MemFree:           73004 kB
Buffers:           24816 kB
Cached:           161044 kB
SwapCached:            0 kB
Active:           527912 kB
Inactive:         106316 kB
Active(anon):     448380 kB
Inactive(anon):      340 kB
Active(file):      79532 kB
Inactive(file):   105976 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB # あれ Totalが 0になってる...
SwapFree:              0 kB
Dirty:              1056 kB
Writeback:             0 kB
AnonPages:        448420 kB
Mapped:            49016 kB
Shmem:               352 kB
Slab:              33084 kB
SReclaimable:      11800 kB
SUnreclaim:        21284 kB
KernelStack:        1664 kB
PageTables:         5092 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      380064 kB
Committed_AS:    1064536 kB
VmallocTotal:   34359738367 kB
VmallocUsed:        7080 kB
VmallocChunk:   34359727352 kB
HardwareCorrupted:     0 kB
AnonHugePages:     61440 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        8064 kB
DirectMap2M:      778240 kB
```

SwapTotalが0ということは、Swap領域はないのかな。

```bash
$ free -kt
             total       used       free     shared    buffers     cached
Mem:        760132     433004     327128        256      25092     133304
-/+ buffers/cache:     274608     485524
Swap:            0          0          0
Total:      760132     433004     327128
```

CacheとSwapの違いがよく分からない。

### Q.てか Swap て、そもそも何だっけ？

[IT用語辞典 スワップ](http://e-words.jp/w/%E3%82%B9%E3%83%AF%E3%83%83%E3%83%97.html)

> メモリ容量が不足してきたら現在使われていないプログラム(プロセス)を一時的にスワップファイルに書き出して消去し、占有していたメモリを開放する。

HDDの領域を使って、少ないメモリ容量でも、メモリ不足にならないように適えてくれる仕組み、だろうか。


### Q. CacheとSwapの違いは？

...調査中。

## Swapファイルを作ってみる

参考 [1]: [Out Of Memory(OOM killer)でプロセスを強制に落とされてしまうのをなんとかする。](http://brokendish.org/debian/926/)
> 実メモリも仮想メモリも足りなくなった場合にOOM Killerというヤツが召喚される。OOM killerが動いてしまうと、幾つかのプロセスを選択し勝手にKillしまくってしまう。これは良くも悪くもあるので、一旦Swapを今の倍にして様子を見る。

### Q. /dev/zero って何？

> 参考: [/dev/zero](http://ja.wikipedia.org/?title=/dev/zero)
> 全てヌル文字（ASCII の NUL、0x00）の内容を読み出すことができる。典型的利用例として、何らかの情報を上書きするキャラクタストリームとして使う。あるいは、特定サイズの内容が何もないファイルを作成するのにも使われる。

参考 [2]: [ddコマンド](http://itpro.nikkeibp.co.jp/article/COLUMN/20060227/230741/)
> ファイルを変換をしながら指定したブロック・サイズでコピーを行う。

参考 [3]: [linux スワップ（swap）領域の作成](http://kazmax.zpp.jp/linux_beginner/mkswap.html)
> deviceにファイルを指定する場合は、以下のように、ddコマンドを使って先にスワップ用のファイルを作成しておきます。


サーバーのメモリは768MB。その２倍のSwapを確保するとして、2GBのSwapファイルを作成する。

Swapファイルを作成する場所は`/home/swap/`ディレクトリとする。

```bash
# dd if=/dev/zero of=/home/swap/swap2GB bs=1024 count=2048000
2048000+0 records in
2048000+0 records out
2097152000 bytes (2.1 GB) copied, 9.6431 s, 217 MB/s

```

先の`/dev/zero`をコピーした。

```bash
# ls /home/swap -alh
合計 2.0G
drwxr-xr-x 2 root root 4.0K  4月 19 20:50 2015 .
drwxr-xr-x 4 root root 4.0K  4月 19 20:49 2015 ..
-rw-r--r-- 1 root root 2.0G  4月 19 20:50 2015 swap2GB
```
2GBのファイルができてるっぽい。
次に`mkswap`コマンドでSwap領域の準備をする。

```bash
# mkswap /home/swap/swap2GB 
mkswap: /home/swap/swap2GB: warning: don't erase bootbits sectors
        on whole disk. Use -f to force.
スワップ空間バージョン1を設定します、サイズ = 2047996 KiB
ラベルはありません, UUID=9c4de271-aa3a-4c18-8e4e-5ba313b0edd5
```

Swapを有効にする。

```bash
# swapon /home/swap/swap2GB

```

再起動時にも有効になるように、`fstab`に書き込み。
[fstabの設定](http://unixlife.jp/linux/system/fstab.html)
> fstabの書式は以下のようになります。
> [デバイス] – [マウントポイント] – [ファイルシステム] – [マウントオプション] – [dumpオプション] – [fsckオプション]

```bash
# vim /etc/fstab

```

```vim
#
# /etc/fstab

...

#Swap
/home/swap/swap2GB      swap    swap    default 0       0

```

### Q. メモリオーバーコミットとは？

参考: [ESXiの使いどころ！](http://thinkit.co.jp/article/127/4/)

> メモリオーバーコミットとは、物理環境に搭載されたメモリの容量以上に、各仮想マシンに対してメモリを割り当て、仮想的なswapでコントロールするという技術です。

参考: [Linux のオーバーコミットについて調べてみた](http://passingloop.tumblr.com/post/11957331420/overcommit-and-oom-killer)

> Linux カーネルは実メモリ以上にメモリをプロセスに割り当てることができる。
> ...
> 76152GB = 74TB も malloc できた計算になる．つまり，Linux では
>（オーバーコミットできるメモリの大きさ）>（実際に利用可能なメモリの大きさ）
> となる。
> ...
> 言い換えると OOM killer が無実の重要なプロセスを殺すかもしれないということである。これでは困るので、重要なプロセスが走っているホストではオーバーコミットを無効にすることがある。
>

"Out of Memory"で、スクリプトが落ちるのは、
オーバーコミットがOnになっているから、ということだろうか。
しかし、そもそも私の環境にはSwap領域がなかったので、
オーバーコミットの力が活かされていなかったのか。

### 確認

```bash
$ /sbin/swapon -s
Filename				Type		Size	Used	Priority
/home/swap/swap2GB                      file		2047996	0	-1
```

```bash
$ cat /proc/meminfo
MemTotal:         760132 kB
MemFree:          658012 kB
Buffers:            9088 kB
Cached:            38888 kB
SwapCached:            0 kB
Active:            28964 kB
Inactive:          28132 kB
Active(anon):       9128 kB
Inactive(anon):      148 kB
Active(file):      19836 kB
Inactive(file):    27984 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       2047996 kB
SwapFree:        2047996 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:          9184 kB
Mapped:             7284 kB
Shmem:               160 kB
Slab:              28000 kB
SReclaimable:       9428 kB
SUnreclaim:        18572 kB
KernelStack:         584 kB
PageTables:         1812 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     2428060 kB
Committed_AS:      51592 kB
VmallocTotal:   34359738367 kB
VmallocUsed:        7592 kB
VmallocChunk:   34359726788 kB
HardwareCorrupted:     0 kB
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        8064 kB
DirectMap2M:      778240 kB
```

```bash
$ free -kt
             total       used       free     shared    buffers     cached
Mem:        760132     102244     657888        160       9092      39012
-/+ buffers/cache:      54140     705992
Swap:      2047996          0    2047996
Total:     2808128     102244    2705884
```

Swapは正しく認識されているっぽい。

# 検証

これでどのくらい状況が変わるのか、変わらないのかチェック。
来週くらいに更新予定。