clamavのスキャン中のメモリ使用量を抑える

clamavのメモリ使用量に悩まされてはや11ヶ月。どうしたらオーバーコミットを抑えつつhttpdにメモリを確保できるのか試行錯誤していましたが、ついに解決しました。

clamavの悩み

clamdのメモリ使用量は約300MB。このサーバのメモリは1GBなので300MBは大きいです。

# ps aux|grep clam
root     29087  0.3 30.5 527936 311460 ?       Ssl  Nov08   6:56 clamd

ウイルススキャンは「CentOSで自宅サーバー構築」を参考に1日1回cronでclamscanを実行していたのですが、スキャンの度にさらに300MBほどメモリを消費していました。
なるべくhttpdにメモリを確保しつつオーバーコミットしないようにメモリ空き容量を微調整し続けていましたが、どうしても10MB程度オーバーしてしまいます。
clamscan_memoryuse

オーバーコミットするとスワップが発生するのでhttpサーバのレスポンスが一瞬低下しますが、深夜帯にスキャンすれば利用者にほぼ影響がないことから改善は諦めていました。
clamscan_httpresponse

clamscanとclamdscanは違う

clamdのメモリ消費を抑えるべく試行錯誤していましたが、オプションをOFFにしたり検索除外ディレクトリを追加しても全く減りません。どうにかならないかと検索していたらこんな記事を発見。

clamav: clamscanとclamdscanの違いってなんなんだよ | 春木屋
http://d.hatena.ne.jp/flageo/20120301/p1

・clamscan
 単品で動くウイルススキャナ。
 一回だけのスキャンに使用する。
 遅い。

・clamd
 ウイルススキャナデーモン
 後述のclamdscanとセットで使う。
 あるいはメールのスキャンなどに。

・clamdscan
 clamdとセットで動くウイルススキャナ。
 指定されたファイルのパスをclamdに教えると、clamdがスキャンしてくれる。
 clamdは定義ファイルをメモリ展開しているのでclamscan(clam*d*scanじゃなく)よりもスキャンが断然速い。

・・・サーバがcronで実行しているvirusscan.sh(CentOSで自宅サーバー構築)を調べてみると、このスクリプトが利用しているのはどう見てもclamscanです本当にありがとうございました。
折角clamdを常駐させメモリを300MBも使用しているのに利用せず、追加で同量のメモリを消費するclamscanを呼び出すなんて無駄遣いでしかありません。しかも処理が遅いなんて・・・

virusscan.shを修正する

分かってしまった以上、スクリプトを修正する以外の選択肢はありません。といっても修正は「clamscan」を「clamdscan」に変更するだけです。
CentOSで自宅サーバー構築を参考に「ウィルススキャン定期自動実行設定」のスクリプトを修正します。

# virusscan.sh
CLAMSCANTMP=`mktemp`
- clamscan  --recursive --remove ${excludeopt} / > $CLAMSCANTMP 2>&1
+ clamdscan --recursive --remove ${excludeopt} / > $CLAMSCANTMP 2>&1
[ ! -z "$(grep FOUND$ $CLAMSCANTMP)" ] && \

# report mail send
grep FOUND$ $CLAMSCANTMP | mail -s "Virus Found in `hostname`" root
rm -f $CLAMSCANTMP

修正後はvirusscan.shを実行してもメモリ消費量が増えることが無くなり、オーバーコミットすることもなくなりました。clamscanに確保していたメモリはhttpdに回そうと思います。
clamdscan_memoryuse

結論

メモリがたった1GBだからこそ今回の問題に気づくことができましたが、そもそもclamavに関わらず参考サイトの情報を鵜呑み(コードのコピペ)にするのは良くありませんね。自分のサーバで使うスクリプトは中身をよく確かめてから利用すべきでした。

※2015/11/18 追記(2016/1/9再修正)

clamscanからclamdscanに変更後、/var/log/clamav/clamd.logに大量のCan’t read file ERRORが記録されていることに気付きました。どうやら/sys/配下もチェックしているらしいのですが、virusscan.shでは外部ファイルを利用する形で/sys/や/proc/に対して除外設定を入れてたはず。と思って調べてみるとclamscanに存在するオプションがclamdscanには存在しないことが分かりました。

とりあえず/sys/配下にアクセスされては困るのでExcludeオプションをclamd.confに移します。

# diff -u /etc/clamd.conf_20151110 /etc/clamd.conf
--- /etc/clamd.conf_20151110    2015-11-10 23:31:57.375132631 +0900
+++ /etc/clamd.conf     2015-11-10 23:34:43.822295177 +0900
@@ -156,8 +156,10 @@
 # Don't scan files and directories matching regex
 # This directive can be used multiple times
 # Default: scan all
-#ExcludePath ^/proc/
-#ExcludePath ^/sys/
+ExcludePath ^/proc/
+ExcludePath ^/sys/
+ExcludePath ^/dev/
+ExcludePath ^/mnt/backup/

 # Maximum depth directories are scanned at.
 # Default: 15

次にcronで実行するvirusscan.shからもexcludeoptを削除します。ついでにclamdscanには存在しないrecursiveオプションも削除してしまいます。currypanさんご指摘ありがとうございました。

# diff -u virusscan.sh_old virusscan.sh CLAMSCANTMP=`mktemp`
-clamscan --recursive --remove ${excludeopt} / > $CLAMSCANTMP 2>&1
+clamdscan --remove / > $CLAMSCANTMP 2>&1
 [ ! -z "$(grep FOUND$ $CLAMSCANTMP)" ] && \

 # report mail send

コメント

  1. currypan より:

    同じような原因で空きメモリが不足し、度々サーバが落ちてしまい困っておりました。
    こちらの記事を参考に設定を見直し、しばらく様子を見てみようと思います。

    ちなみに clamdscan には recursive オプションも存在しないようです。
    /etc/clamd.conf の MaxDirectoryRecursion がそれに相当するようです。

    169行目付近に、
    # Maximum depth directories are scanned at.
    # Default: 15
    #MaxDirectoryRecursion 20
    とありますので、
    デフォルトで15階層までサブディレクトリを再帰的に検査するものと思われます。

    情報助かりました。
    ありがとうございます。