RHEL 8.4 では 1 時間足らずで終わっていた ClamAV のフルスキャンが、RHEL 8.10 に上げた途端 11〜12 時間もかかるようになり、CPU 使用率も高止まりしてしまう——。本記事では、この「clamscan が終わらない問題」の正体と、実際に現場で使える具体的な改善手順を、設定例とコマンド付きで詳しく解説します。
ClamAV フルスキャンが 12 時間かかるケースの全体像
まずは、実際に相談されることの多い典型的なケースを整理します。
- 同一設計の 2 台の Linux サーバーで ClamAV を定期フルスキャン
- どちらもスナップショットから展開した、ほぼ同一構成の OS イメージ
- cron で毎日 04:07 に
/(ルート)を再帰スキャン
環境ごとの違いは次のようなものです。
| 項目 | サーバーA | サーバーB |
|---|---|---|
| OS | RHEL 8.4 | RHEL 8.10 |
| ClamAV バージョン | 0.103.4 | 1.0.8 |
| フルスキャン時間 | 約 53 分 | 約 13 時間 |
| CPU 使用率 | 中程度 | 高止まり(長時間) |
さらに、スキャン結果の統計値にも大きな差が出ています。
| 項目 | サーバーA | サーバーB |
|---|---|---|
| 走査ディレクトリ数 | 17,175 | 38,015 |
| 走査ファイル数 | 70,593 | 195,265(約 2.8 倍) |
| 読み取りデータ量 | 21.7 GB | 451.9 GB(約 20 倍) |
同じように / をスキャンしているのに、ファイル数は約 3 倍、読み取りデータ量は 20 倍。ここまで差が出ると、「RHEL 8.10 が遅い?」「サーバー B のディスクが壊れている?」と疑いたくなりますが、根本原因はよりシンプルです。
根本原因:ClamAV 1.0 系のスキャン範囲拡大+不要領域の過走査
ポイントを先に整理すると、次の 2 つです。
- ClamAV 1.0 系のエンジンは、0.103 系より「深く広く」ファイルを展開・検査する
/をそのままスキャンしているため、価値の薄い領域までフルスキャンしてしまっている
ClamAV 1.0 系で何が変わったのか
ClamAV 1.0 系(1.0.8 を含む)では、旧 0.103 系と比べて次のような特徴があります。
- アーカイブ形式(ZIP, 7z など)のサポートや入れ子構造の展開が強化
- マルウェア検出ロジックの高度化(ヒューリスティック、スクリプト解析など)
- デフォルトの制限値(再帰深度・展開サイズなど)が相対的に「攻めた」設定
その結果、以前は「軽く中身を覗くだけ」だったファイルも、1.0 系では「可能な限り展開してしっかりチェック」するようになり、実効的な読み取りデータ量が一気に増えます。特に、ログに混ざっている圧縮ファイル、アプリのキャッシュ、バックアップ用途のアーカイブなどは、すべて展開対象になると考えてよいでしょう。
「読み取りデータ量 20 倍」の正体
走査ファイル数は約 2.8 倍なのに、読み取りデータ量は 20 倍。このギャップの多くは、「アーカイブやコンテナイメージの展開」によるものです。
/var/log配下に溜まるローテーション済みログ(.gz)- アプリケーションのキャッシュディレクトリ(ブラウザやミドルウェア)
- コンテナランタイム(Docker / Podman)のイメージレイヤー
- バックアップ用に置きっぱなしの
.tar.gz、.zip、.7zなど
これらはすべて、ClamAV から見れば「中身を展開してチェックすべき対象」です。アーカイブ 1 つが数十 GB の実データに展開されることも珍しくありません。そのため、「ファイル数はそれほど増えていないのに読み取り量だけ爆増する」という現象が起こります。
/ を丸ごとスキャンすることのデメリット
/ をそのままスキャン対象にすると、実際には次のような「セキュリティ上の価値が低い or 実体がない」領域まで走査されます。
- カーネル・仮想ファイルシステム:
/proc,/sys,/dev,/run - ログ・キャッシュ:
/var/log,/var/cache, 各種アプリのキャッシュ - 一時ファイル・テンポラリ:
/tmp,/var/tmp - コンテナ関連:
/var/lib/docker,/var/lib/containers - ネットワークマウントや外部ボリューム:
/mnt,/media, NFS/CIFS 等
これらは「リアルタイム保護が必要なデータ」ではないことが多く、定期フルスキャンで頑張ってチェックしても、コストに見合わないケースが大半です。特にネットワークマウントは、帯域やストレージ性能に引きずられて、スキャン時間を一気に押し上げる要因になります。
改善の全体方針:除外最適化 → clamd 化 → マウント見直し
こうした問題に対して、現場での「コスパが良い」順番で対策を並べると、次のようになります。
- 除外ディレクトリの見直し(不要領域の過走査をやめる)
- clamscan から clamd + clamdscan への切り替え(デーモン化・マルチスレッド化)
- マウントポイントとスキャン範囲の見直し(ネットワーク・コンテナ層の扱い)
- 必要に応じて scan.conf(clamd 設定)で制限値を調整
順番のポイントは、「検出精度をなるべく落とさずに負荷を下げる」ことです。いきなり MaxScanSize を極端に小さく削るのではなく、まずは 「そもそもスキャンすべきでない場所」を減らす のが安全かつ効果的です。
除外ディレクトリの見直し(効果大・安全性も高い)
最初の一手として、除外ディレクトリの整理は非常に効果的です。
除外対象にすべきディレクトリの考え方
基本的な考え方は次の 3 点です。
- 実体のない仮想ファイルシステム:カーネルやデバイス情報であり、マルウェアが常駐する場所ではない
- 大量に更新される一時・ログ・キャッシュ:IO 負荷の割に検査価値が低い
- 別の仕組みで保護すべき領域:コンテナイメージや外部ストレージは、別のレイヤーでスキャンする方が現実的
具体的には、次のようなディレクトリを除外候補にするとよいでしょう。
| パス | 内容 | 除外理由 |
|---|---|---|
/proc, /sys, /dev, /run | カーネル・デバイス・一時情報 | 仮想 FS であり、実体ファイルではない。スキャンしても意味が薄い。 |
/var/lib/clamav, /var/lib/clamav-unofficial-sigs | ClamAV の定義ファイル | 自分自身の定義をスキャンしても意味がない。速度低下の原因になる。 |
/var/cache, /var/log | キャッシュ・ログ | 巨大化しやすく、圧縮ファイルも多い。フルスキャン頻度を落とすか除外する。 |
/tmp, /var/tmp | 一時ファイル | 短命なファイルが多く、フルスキャンよりもリアルタイム対策でカバーすべき。 |
/var/lib/docker, /var/lib/containers | コンテナイメージ・レイヤー | 同一データを何度もスキャンしやすい。イメージ側でのスキャンに切り分ける。 |
/mnt, /media など | 外付け・ネットワークボリューム | 性能の悪いストレージを巻き込むと一気に遅くなる。別タイミングでのスキャンが現実的。 |
clamscan での除外指定例(正規表現を 1 本に統一)
既に --exclude-dir を使っている場合でも、複数指定の書き方を誤っているパターンがよくあります。ClamAV の --exclude-dir は「正規表現 1 本」を受け取るため、複数パスを並べるときは |(パイプ)でつなぎます。
例として、次のようなコマンドが考えられます。
clamscan -r -i / \
--exclude-dir='^/(proc|sys|dev|run|var/lib/clamav(-unofficial-sigs)?|var/cache|var/log|tmp|var/tmp|var/lib/docker|var/lib/containers|mnt|media)(/|$)'
^/でルート直下から始まることを明示(...|...)で複数ディレクトリをまとめて指定var/lib/clamav(-unofficial-sigs)?のように派生ディレクトリを 1 本で表現
もし既存の設定で --exclude-dir="/sys/;/var/log" のように「セミコロン区切り」を使っている場合、それは 2 つ目以降が効いていない可能性が高いので要注意です。
clamscan から clamd + clamdscan へ:デーモン化による高速化
除外を整理したら、次は ClamAV をデーモンとして動かす「clamd」への移行です。
clamscan と clamdscan(clamd)の違い
ざっくりまとめると次のようになります。
| 項目 | clamscan | clamd + clamdscan |
|---|---|---|
| 動作方式 | コマンド実行ごとにエンジン・定義を読み込む | 常駐デーモンがメモリ上にエンジン・定義を保持 |
| スキャン速度 | 都度初期化が必要で遅くなりがち | 初期化済みのエンジンで素早くスキャン可能 |
| 並列実行 | 単一プロセスで直列実行が基本 | スレッドや複数プロセスによる並列スキャンが可能 |
| CPU / メモリ | 短時間で起動・終了するが、フルスキャンでは非効率 | 常駐にメモリを使うが、総合的には高速で安定しやすい |
特に、フルスキャンを毎日実行している環境では、定義ファイルの読み込みコストを 1 回に集約できる clamd のメリットが非常に大きいです。
clamd の有効化手順(RHEL 系の例)
RHEL 8 系の一般的な流れは次のとおりです。
# Example 行をコメントアウトして有効化
sudo sed -i 's/^Example/#Example/' /etc/clamd.d/scan.conf
sudo sed -i 's/^Example/#Example/' /etc/freshclam.conf
# デーモンと定義更新を起動・自動起動
sudo systemctl enable --now clamd@scan
sudo systemctl enable --now freshclam
# 状態確認
sudo systemctl status clamd@scan
sudo journalctl -u clamd@scan -f
/etc/clamd.d/scan.conf 内で、ソケットパス(LocalSocket)やログ出力先なども合わせて確認しておくと安心です。
clamdscan でのフルスキャン実行例
clamd を起動できたら、日次のフルスキャンは clamdscan コマンドに切り替えます。
clamdscan --multiscan --fdpass -i / \
--exclude-dir='^/(proc|sys|dev|run|var/lib/clamav(-unofficial-sigs)?|var/cache|var/log|tmp|var/tmp|var/lib/docker|var/lib/containers|mnt|media)(/|$)'
--multiscan:複数スレッド・プロセスで並列スキャン--fdpass:権限継承のためにファイルディスクリプタを渡す(root での運用時によく使う)-i:感染ファイルのみ出力
cron への登録例(優先度を下げて他処理への影響を抑える)
フルスキャンはそれなりに負荷がかかる処理なので、nice / ionice を併用して優先度を下げておくと、他サービスへの影響を最小限にできます。
7 4 * * * nice -n 10 ionice -c3 \
clamdscan --multiscan --fdpass -i / \
--exclude-dir='^/(proc|sys|dev|run|var/lib/clamav(-unofficial-sigs)?|var/cache|var/log|tmp|var/tmp|var/lib/docker|var/lib/containers|mnt|media)(/|$)' \
| tee /u/pick/last.clamscan | logger -t clamav -p security.alert
これだけでも、「clamscan 単体で 10 時間以上かかっていた環境が、clamdscan で数時間以内に収まる」ケースは珍しくありません。
マウントポイントとスキャン範囲の見直し
除外設定と clamd 化をしたうえで、さらに大きく時間を削れるのが「マウントポイントの見直し」です。
まずは現在のマウントを一覧する
どのファイルシステムがどこにマウントされているかを把握するには、次のようなコマンドが便利です。
findmnt -rn -o TARGET,FSTYPE,OPTIONS
ここで、特に次のようなファイルシステムに注目します。
- NFS / CIFS / GlusterFS などのネットワークファイルシステム
- バックアップ用途の大容量ボリューム
- overlay / aufs などのコンテナ関連ファイルシステム
ファイルシステムごとの扱い方の目安
| FSTYPE / 用途 | 例 | 推奨方針 |
|---|---|---|
| ローカルディスク(OS / アプリ) | xfs, ext4 など | 日次 or 週次でフルスキャン。必要に応じてアプリ配下を重点的に。 |
| ネットワークストレージ | NFS, CIFS, GlusterFS など | 別サーバー側でスキャンする・同期元でスキャンするなど、役割を分ける。 |
| バックアップ専用ボリューム | rsync 領域、バックアップソフト管理領域 | 復元時にスキャンする方が効率的。定期フルスキャンからは原則除外。 |
| コンテナ用ファイルシステム | overlay, btrfs サブボリュームなど | イメージ作成時・配布前にスキャンする。ホスト側フルスキャンからは除外。 |
スキャン範囲を分割する運用も有効
すべてを毎日フルスキャンする必要はありません。例えば次のような運用にすると、安定して運用しやすくなります。
- 日次:ユーザーデータ中心(
/home,/srv, アプリのデータディレクトリなど) - 週次:OS 全体(ただし不要領域は除外)
- 月次:バックアップ領域や特定ボリュームを個別にスキャン
こうすることで、「毎日 10 時間以上 CPU を専有し続けるフルスキャン」から、「日々は 1〜2 時間以内、週末に少し重めのスキャン」という現実的な運用に近づけられます。
scan.conf(clamd 設定)の軽量化と注意点
ここまでの対策でまだ時間が厳しい場合は、/etc/clamd.d/scan.conf のパラメータを調整して、負荷と検出精度のバランスを取ります。
よく調整する主要パラメータ
典型的には次のような項目を見直します。
| パラメータ | 役割 | 調整の目安 |
|---|---|---|
MaxThreads | 同時スキャンスレッド数 | 物理 / 論理コア数に合わせて設定。CPU 過負荷なら少し下げる。 |
MaxFileSize | 1 ファイルあたりの最大検査サイズ | 巨大ログ・バックアップが多い環境では 100M など適度な値に。 |
MaxScanSize | 1 スキャンあたりの総検査サイズ | 無制限だとアーカイブ展開で膨れがち。1G 前後から様子を見る。 |
MaxRecursion | アーカイブ入れ子の最大深度 | 極端に深い入れ子は現実的でないことも多い。16 前後を目安に。 |
MaxFiles | アーカイブ内の最大ファイル数 | 異常な数のファイルを含むアーカイブを早めに打ち切る。 |
例として、次のような設定がよく使われます(あくまで一例です)。
MaxThreads 4
MaxFileSize 100M
MaxScanSize 1G
MaxRecursion 16
MaxFiles 20000
ただし、これらを絞りすぎると、「実はスキャンされていないファイルが増える」ことになるため、セキュリティポリシーとの兼ね合いが重要です。設定変更後は、ログに size limit exceeded のようなメッセージが多発していないか確認しましょう。
設定変更時に確認しておきたいポイント
- 変更前後でスキャンログを比較し、「スキップされたファイル」が増えていないか
- EICAR テストファイルなどを利用して、検出漏れがないかを簡易チェック
- 定義ファイル更新(freshclam)のスケジュールと衝突していないか
特に監査対応が必要な環境では、「どのパターンでどこまでスキャンしているか」を明文化しておくと、あとから説明しやすくなります。
ボトルネック切り分けの実践手順
最後に、「どこから手を付けるべきか」を判断するための切り分け方法を簡単にまとめます。
1. ファイル数・容量を把握する
まずは、サーバー全体でどれくらいのファイルを抱えているのかを確認します。
# ローカルファイルシステムに限定してファイル数をカウント
sudo find / -xdev -type f | wc -l
# 主要ディレクトリごとの容量
sudo du -xhs /home /srv /var /opt
これにより、「そもそもサーバー B はファイル数が 2〜3 倍以上に膨らんでいる」などの事実を掴めます。特定のディレクトリ(例:/var)だけが極端に増えている場合、その配下を重点的にスキャン・除外対象として見直す価値があります。
2. CPU ボトルネックか I/O ボトルネックかを見極める
ClamAV のスキャンが遅いとき、CPU とディスク I/O のどちらが詰まっているかで対策が変わります。
- CPU が常に 100% 近い:並列度(MaxThreads)や
--multiscanの調整、除外の強化が有効 - I/O 待ちが多い(wa 時間が長い):ネットワークマウント除外・HDD→SSD などストレージ側の改善検討
例えば、次のようなコマンドで観察します。
# ディスク I/O の状況
iostat -xz 1
# プロセスごとの CPU / I/O 使用状況
pidstat -dur 1
ClamAV プロセス周辺の数値を見れば、「CPU を使い切っているのか、ディスク待ちで止まっているのか」がある程度判別できます。
3. ディレクトリごとのスキャン時間をざっくり測る
どのディレクトリがネックになっているのかを見たいときは、ディレクトリごとにスキャン時間を計測してみるのも有効です。
time clamdscan -i /home
time clamdscan -i /var
time clamdscan -i /opt
これで、例えば /var だけが突出して時間を食っていると分かれば、/var/log や /var/cache を切り分けてスキャン頻度を落とす、といった判断がしやすくなります。
よくある質問と運用のコツ
/proc や /sys をスキャンしなくても大丈夫?
これらはカーネルが提供する仮想ファイルシステムであり、いわゆる「マルウェアの実体」が置かれる領域ではありません。監査の要件などが特にない限り、通常は除外して問題ありません。
ログディレクトリ(/var/log)は完全に除外してよい?
ログそのものにマルウェアが混入しているケースは稀ですが、監査ポリシーによっては「システム全体を定常的にスキャンしていること」が求められる場合もあります。
- 日次フルスキャンからは除外し、週次・月次で別ジョブとしてスキャンする
- 圧縮ログ(
.gz)のみ除外する
など、環境ごとに段階的な妥協点を決めるのがおすすめです。
MaxScanSize や MaxFileSize を極端に小さくすれば速くなる?
たしかに速くはなりますが、その分だけ「スキャンされない領域」が増えます。あくまで除外ディレクトリの最適化や clamd 化での改善を先に行い、それでも厳しい場合に最後の手段として調整する、という順番を意識しましょう。
コンテナ環境ではどこをスキャンすべき?
コンテナホスト側では、/var/lib/docker や /var/lib/containers のようなイメージレイヤーを丸ごとスキャンすると非効率です。
- コンテナイメージのビルドパイプライン内で、ClamAV などを使ってイメージをスキャン
- ホスト側では、ユーザーデータや永続ボリューム(
/var/lib/<app>など)を重点的にスキャン
といった役割分担をすることで、性能とセキュリティを両立しやすくなります。
まとめ:RHEL 8.10 + ClamAV 1.0 系でフルスキャンが重いときの指針
RHEL 8.10 環境で ClamAV 1.0.8 を使うと、旧バージョンと比べてフルスキャンが極端に重くなることがありますが、その多くは次の組み合わせで説明できます。
- ClamAV 1.0 系でアーカイブや入れ子構造への検査が強化され、読み取りデータ量が増えやすくなった
/を丸ごとスキャンしているため、ログ・キャッシュ・コンテナ層・ネットワークボリュームまで含めて過走査してしまっている
実務上は、次の順で対策していくのがコスト対効果の高いアプローチです。
- 除外ディレクトリの最適化
仮想 FS、ログ・キャッシュ、一時ディレクトリ、コンテナイメージ、ネットワークマウントなどを明示的に除外する。 - clamscan から clamd + clamdscan への移行
定義ファイルの読み込みコストを削減し、マルチスレッドで高速化する。 - マウントポイント・スキャン範囲の見直し
ネットワークストレージやバックアップボリュームは別ジョブ・別タイミングでスキャン。 - scan.conf の制限値調整
MaxThreads / MaxScanSize / MaxRecursion などを、負荷と検出精度のバランスを見ながら段階的に調整する。
これらを組み合わせることで、12 時間以上かかっていたフルスキャンを、環境によっては数十分〜数時間程度まで短縮できる可能性があります。ClamAV のログや findmnt の結果、現状の scan.conf を丁寧に観察しながら、自組織のポリシーと性能要件に合った「ちょうどよい落としどころ」を探ってみてください。

コメント