ClamAVをCGroupで管理してCPUを節約する
こんにちは、せーのです。今日は最近触っているClamAVについてのちょっとした工夫をご紹介します。ClamAVに関わらずサービス実行するプログラムであれば応用がきくかと思いますので参考になさってください。今までのシリーズをご覧になった上でこちらの記事を読むとより理解が深まります。
- Amazon LinuxにClamAVを導入する
- Red Hat Enterprise Linux 5.9にClamAVを導入する
- ClamAVとCloudWatch Logsを連携してウィルス検出をメールで通知する
サーバーはウイルススキャンのために立てているわけではない
ご存じの通りClamAVはアンチウイルスソフトウェアです。サーバーがウイルスに侵されないように監視、検知するためのソフトなわけですが、ウイルススキャン中にはサーバーをくまなくチェックするため、CPU使用率がぐんっと上がります。チェックするファイル数によってはスキャン中にはCPUが100%まで上がってしまい、別の監視ソフトで異常として検知されたりする場合もあります。そもそもサーバーはサービスを実行するために立てているわけで、ウイルススキャンというサーバーの健康を保つための操作でCPUをやたらと使ってしまい、もしそれが原因でサービスが重くなったりしてしまっては本末転倒です。
"CGroup"とは
そこで今回はコントロールグループ(通称CGroup)というLinuxカーネルの機能を使ってCPUリソースを抑制してみたいと思います。CGroupとはCPUやメモリ等のシステムリソースの割り当て、優先度設定、拒否、管理、モニタリングに対する粒度の細かいコントロールを可能にする機能です。CGroupを使うことでシステムリソースをどのそれぞれのプロセスにどのくらい割り当てるか、というコントロールができるようになります。
やってみた
では早速やってみましょう。ClamAVは大きく分けて「ウイルス定義DBを更新する(freshclam)」と「ウイルススキャンをする(clamscan)」の2つの動作に分かれるのでそれぞれをCGroupにてコントロールしてみます。Amazon Linux(t2.micro)のEC2を立て、ClamAVをインストールした状態まで、上のシリーズ記事を参考に準備して下さい。でははじめます。
CGroupの設定
まずCGroupを使えるようにインストールします。隅々までスキャンする想定のためrootユーザーで行っていますが、本番環境ではスキャン用のユーザーを作成して行ってもよいかと思います。
[root@ip-172-31-28-136 ~]# yum install libcgroup -y 読み込んだプラグイン:priorities, update-motd, upgrade-helper amzn-main/latest | 2.1 kB 00:00 amzn-updates/latest | 2.3 kB 00:00 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ libcgroup.x86_64 0:0.40.rc1-5.11.amzn1 を インストール --> 依存性解決を終了しました。 依存性を解決しました ================================================================================ Package アーキテクチャー バージョン リポジトリー 容量 ================================================================================ インストール中: libcgroup x86_64 0.40.rc1-5.11.amzn1 amzn-main 146 k トランザクションの要約 ================================================================================ インストール 1 パッケージ 総ダウンロード容量: 146 k インストール容量: 320 k Downloading packages: libcgroup-0.40.rc1-5.11.amzn1.x86_64.rpm | 146 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction インストール中 : libcgroup-0.40.rc1-5.11.amzn1.x86_64 1/1 検証中 : libcgroup-0.40.rc1-5.11.amzn1.x86_64 1/1 インストール: libcgroup.x86_64 0:0.40.rc1-5.11.amzn1 完了しました! [root@ip-172-31-28-136 ~]#
次にCGroupの設定ファイルを修正します。設定ファイルは/etc/cgconfig.confにあります。
[root@ip-172-31-28-136 ~]# cat /etc/cgconfig.conf # # Copyright IBM Corporation. 2007 # # Authors: Balbir Singh <balbir@linux.vnet.ibm.com> # This program is free software; you can redistribute it and/or modify it # under the terms of version 2.1 of the GNU Lesser General Public License # as published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # See man cgconfig.conf for further details. # # By default, mount all controllers to /cgroup/<controller> mount { blkio = /cgroup/blkio; cpu = /cgroup/cpu; cpuacct = /cgroup/cpuacct; cpuset = /cgroup/cpuset; devices = /cgroup/devices; freezer = /cgroup/freezer; hugetlb = /cgroup/hugetlb; memory = /cgroup/memory; perf_event = /cgroup/perf_event; } group test { cpu { cpu.cfs_quota_us = 250000; cpu.cfs_period_us = 1000000; } cpuacct { } }
新たに「test」というCGroupを追記しました。コマンドでも追加できるのですが再起動すると消えてしまうので設定ファイルに書き込んだほうが確実です。ここではCPUリソースについて抑制する設定を書いています。 少し解説します。cfs_period_usというのはCGroupで管理する実行時間を表しcfs_quota_usというのはそのうちこのグループに所属しているプロセスがアクセスできる時間を表します。単位はそれぞれマイクロ秒(μs)です。つまり上の例で行くとこのグループに所属しているプロセスは1秒間(1000000μs)に250msのみCPUにアクセスできることになります。常にCPUにアクセスするとCPUは100%になるので、この設定だと最大25%にCPUが抑制されることになります。
ただ一つ押さえておいて欲しいのはcfs_period_usは「単一CPUで」ということです。つまり上の設定でCPUが2つある場合は1つで1秒間、つまり2つで2秒間という割り当てになるので、そのうち250msしか使えないと全体でのCPU使用率は12.5%になります。今回検証に使用しているt2.microは単一CPUなのでCPU1つとして計算していますが、実際の環境で設定する場合はそのコア数も注意して下さい。
それでは設定が終わりましたのでサービスを起動します。
[root@ip-172-31-28-136 ~]# service cgconfig start Starting cgconfig service: [ OK ]
サービスを永続化するためにchkconfigをonにします。
[root@ip-172-31-28-136 ~]# chkconfig cgconfig on
これでCGroupの設定は完了です。後はClamAVのプロセスを作動し、作成したCGroupに移行する作業に入ります。
freshclamをデーモン化する
まずはfreshClam, つまりウイルス検知のデータベースの更新をデーモン化します。freshclamはデーモン化のオプションがあるのでそれを追加するだけでOKです。また後で上にて作成したCGroupに移行する際にプロセスIDが必要になるので、それをファイルに吐き出すpオプションをつけておきます。
[root@ip-172-31-28-136 ~]# freshclam -d -c 24 -p /root/freshclampid.log -u root
こんな感じです。オプションは
- d: デーモン化する
- c: 一日に何回作動させるか
- p: プロセスIDをファイルに吐き出す
- u: 指定ユーザーにて作動させる
となります。このコマンドを実行後/root/freshclampid.logを見てみると
[root@ip-172-31-28-136 ~]# cat freshclampid.log 29721
とプロセスIDが吐き出されています。
clamscanをデーモン化する
次にclamscanをデーモン化します。ですがclamscanはfreshclamのようにデーモン化するコマンドがありません。代わりに「clamd」というサーバーをプロセス稼働させ、それを使います。 まずclamdをインストールします。
[root@ip-172-31-28-136 ~]# yum install clamd 読み込んだプラグイン:priorities, update-motd, upgrade-helper amzn-main/latest | 2.1 kB 00:00 amzn-updates/latest | 2.3 kB 00:00 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ clamd.x86_64 0:0.98.7-1.12.amzn1 を インストール --> 依存性の処理をしています: clamav-scanner-sysvinit = 0.98.7-1.12.amzn1 のパッケージ: clamd-0.98.7-1.12.amzn1.x86_64 --> トランザクションの確認を実行しています。 ---> パッケージ clamav-scanner-sysvinit.noarch 0:0.98.7-1.12.amzn1 を インストール --> 依存性の処理をしています: clamav-scanner = 0.98.7-1.12.amzn1 のパッケージ: clamav-scanner-sysvinit-0.98.7-1.12.amzn1.noarch --> 依存性の処理をしています: clamav-server-sysvinit = 0.98.7-1.12.amzn1 のパッケージ: clamav-scanner-sysvinit-0.98.7-1.12.amzn1.noarch --> トランザクションの確認を実行しています。 ---> パッケージ clamav-scanner.noarch 0:0.98.7-1.12.amzn1 を インストール --> 依存性の処理をしています: clamav-server = 0.98.7-1.12.amzn1 のパッケージ: clamav-scanner-0.98.7-1.12.amzn1.noarch ---> パッケージ clamav-server-sysvinit.noarch 0:0.98.7-1.12.amzn1 を インストール --> トランザクションの確認を実行しています。 ---> パッケージ clamav-server.x86_64 0:0.98.7-1.12.amzn1 を インストール --> 依存性解決を終了しました。 依存性を解決しました ================================================================================ Package アーキテクチャー バージョン リポジトリー 容量 ================================================================================ インストール中: clamd x86_64 0.98.7-1.12.amzn1 amzn-updates 18 k 依存性関連でのインストールをします: clamav-scanner noarch 0.98.7-1.12.amzn1 amzn-updates 25 k clamav-scanner-sysvinit noarch 0.98.7-1.12.amzn1 amzn-updates 19 k clamav-server x86_64 0.98.7-1.12.amzn1 amzn-updates 99 k clamav-server-sysvinit noarch 0.98.7-1.12.amzn1 amzn-updates 20 k トランザクションの要約 ================================================================================ インストール 1 パッケージ (+4 個の依存関係のパッケージ) 総ダウンロード容量: 182 k インストール容量: 205 k Is this ok [y/d/N]: y Downloading packages: (1/5): clamav-scanner-0.98.7-1.12.amzn1.noarch.rpm | 25 kB 00:00 (2/5): clamav-scanner-sysvinit-0.98.7-1.12.amzn1.noarch. | 19 kB 00:00 (3/5): clamav-server-0.98.7-1.12.amzn1.x86_64.rpm | 99 kB 00:00 (4/5): clamav-server-sysvinit-0.98.7-1.12.amzn1.noarch.r | 20 kB 00:00 (5/5): clamd-0.98.7-1.12.amzn1.x86_64.rpm | 18 kB 00:00 -------------------------------------------------------------------------------- 合計 821 kB/s | 182 kB 00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction インストール中 : clamav-server-0.98.7-1.12.amzn1.x86_64 1/5 インストール中 : clamav-server-sysvinit-0.98.7-1.12.amzn1.noar 2/5 インストール中 : clamav-scanner-0.98.7-1.12.amzn1.noarch 3/5 インストール中 : clamav-scanner-sysvinit-0.98.7-1.12.amzn1.noa 4/5 インストール中 : clamd-0.98.7-1.12.amzn1.x86_64 5/5 検証中 : clamav-scanner-sysvinit-0.98.7-1.12.amzn1.noa 1/5 検証中 : clamav-server-sysvinit-0.98.7-1.12.amzn1.noar 2/5 検証中 : clamav-server-0.98.7-1.12.amzn1.x86_64 3/5 検証中 : clamd-0.98.7-1.12.amzn1.x86_64 4/5 検証中 : clamav-scanner-0.98.7-1.12.amzn1.noarch 5/5 インストール: clamd.x86_64 0:0.98.7-1.12.amzn1 依存性関連をインストールしました: clamav-scanner.noarch 0:0.98.7-1.12.amzn1 clamav-scanner-sysvinit.noarch 0:0.98.7-1.12.amzn1 clamav-server.x86_64 0:0.98.7-1.12.amzn1 clamav-server-sysvinit.noarch 0:0.98.7-1.12.amzn1 完了しました!
次にclamdの設定を行います。Amazon Linuxでのclamdの設定ファイルはドキュメントにある[/etc/clamd.conf]ではなく[etc/clamd.d/scan.conf]になりますので注意してください。
ファイルを開いたら次の点を修正していきます。
Excampleをコメントアウト
# Comment or remove the line below. #Example
ログファイルを指定
# Uncomment this option to enable logging. # LogFile must be writable for the user running daemon. # A full path is required. # Default: disabled LogFile /var/log/clamd.scan
ログファイルのロックを外しておく
# By default the log file is locked for writing - the lock protects against # running clamd multiple times (if want to run another clamd, please # copy the configuration file, change the LogFile variable, and run # the daemon with --config-file option). # This option disables log file locking. # Default: no LogFileUnlock yes
ログに日時のスタンプをつける
# Log time with each message. # Default: no LogTime yes
Syslogにログを吐く設定をコメントアウト
# Use system logger (can work together with LogFile). # Default: no #LogSyslog yes
ローカルのソケットを開け、ローカルで作動するようにする
# Path to a local socket file the daemon will listen on. # Default: disabled (must be specified by a user) LocalSocket /var/run/clamd.scan/clamd.sock
rootユーザで動くようにする(clamAV用のユーザを作成している方はそのユーザを指定する)
# Run as another user (clamd must be started by root for this option to work) # Default: don't drop privileges User root
以上で設定は終了です。ではclamdを作動させます。
[root@ip-172-31-28-136 ~]# service clamd.scan start clamd.scan を起動中: [ OK ] [root@ip-172-31-28-136 ~]# chkconfig clamd.scan on
プロセスIDを返すようなオプションが無いため、psコマンドでプロセスIDを確認します。
[root@ip-172-31-28-136 ~]# ps ax PID TTY STAT TIME COMMAND 1 ? Ss 0:00 /sbin/init ... ... 3025 ? Ssl 0:00 clamd.scan -c /etc/clamd.d/scan.conf --pid /var/run/c 3028 pts/0 R+ 0:00 ps ax ... ...
clamdの動いているプロセスIDは3025のようです。
freshclamとclamdのプロセスを設定したCGroup配下に移動する
次に上の2つのプロセスをCGroup配下に移動します。移動させるには[cgclassify]コマンドを使います。
[root@ip-172-31-28-136 ~]# cgclassify -g cpu:test --sticky 3025 [root@ip-172-31-28-136 ~]# cat /proc/3025/cgroup 10:perf_event:/ 9:memory:/ 8:hugetlb:/ 7:freezer:/ 6:devices:/ 5:cpuset:/ 4:cpuacct:/ 3:cpu:/test 2:blkio:/
使用しているオプションについて軽く触れておきます。gオプションはグループの指定を指します。今回はCPUリソースについてtestグループに移動させるのでこのように書きます。stickyオプションはこのプロセスの子プロセスも同じグループにキープさせるというオプションです。実際のスキャン時にはこのプロセスから子プロセスが作られる場合もあるのでこのようなオプションをつけておきました。 cgclassifyコマンド実行後プロセスIDのcgroupを確認したところcpuリソースのみtestグループに移動していることが確認できました。これで設定完了です。freshclamのプロセスIDである29721も同様に移動させます。
まとめ
いかがでしょうか。CGroupの設定はなれるまではやることが多くて大変ですが、慣れてしまえばルーティーン化出来るかと思います。CGroupはClamAVだけではなく他のプロセスにも活用できるので是非応用してみてください。