cgroups: Linux でのユーザー固有のディスク I/O 帯域幅の制限

cgroups: Linux でのユーザー固有のディスク I/O 帯域幅の制限

私はApache + PHP-FPMであるLinux Webサーバー(もしあればDebian)を持っています。各Apacheは、VirtualHost専用システムユーザーとして実行される専用のPHP-FPMプール(専用のUnixソケット経由)を使用します。

たとえば、example.comがあるとしますVirtualHost。その後、VirtualHostApacheの設定には次のものがあります。

ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php/php7.3-fpm-examplecom.sock|fcgi://127.0.0.1:9000/home/examplecom/htdocs/www.example.com/

そして、/etc/php/7.3/fpm/pool.d/PHP-FPMプールに対応する設定ファイルがあります。つまり、次examplecom.confが含まれます。

[examplecom]
user = examplecom
listen = /run/php/php7.3-fpm-examplecom.sock
; and other things...

頻繁に発生することは、一部のWebサイトがハッキングされ(更新するのを忘れてしまい、安全でないプラグインをインストールするなど)、利用可能なサーバーリソースを占有し始めることです。私の場合、ハードドライブのI / O帯域幅は最も一般的でした。

同じサーバー上のすべてのWebサイトにDoSingを防ぐためにハッキングされたWebサイトのみがクラッシュし、他のすべてのWebサイトに影響を与えないように、各WebサイトのハードドライブI / O帯域幅を制限したいと思います。

WebサイトあたりのハードディスクI / O帯域幅を制限する方法は?さて、各ユーザーにVirtualHost専用のユーザーがいるので、ユーザーごとに制限できるようです。どうすればいいですか?まあ、Googleはcgroupを使うように提案しました。

しかし、追加する方法については何も見つかりません。ユーザー、プロセスIDの代わりに制御グループに渡されます。

私は間違ったものを探していますか?それとも私が間違ってキーワードを検索したのでしょうか?よくわかりませんが、質問は次のとおりです。 Linuxでcgroupを使用して各ユーザーのハードディスクI / O帯域幅を制限する方法は?

答え1

ありがとうLL3私の質問に対するコメントに応じてカスタムスクリプトを使用する必要があることを知りました。だからそれはすべてです。 PHP-FPMプロセスを照会し、その実行ユーザーとPIDを取得し、cgroupsPIDを生成して正しいcgroups

crontabその後、スクリプトが毎時間実行されるようにエントリを設定しましたが、私の場合は十分でした。 Linuxは自動的に子プロセスを親プロセスに入れるcgroupので、スクリプトは実際にPHP-FPMが再起動するたびに実行するだけです。起動後に 1 回実行 (たとえば、起動時や新しいサイト/ユーザーを追加する場合) 毎時間実行されるので、スケジュールされた時間に手動で実行することを忘れないようにします。

スクリプトはディレクトリ/cgroupsがファイルシステムとしてマウントされていると仮定するcgroup2ため、事前に準備する必要があります。/etc/fstabカーネルがcgroupsデフォルトでバージョン1をインストールしている場合は、/etc/default/grubv1を無効にして再起動するように設定するエントリを追加できます(ヒント:cgroup_no_v1=allに追加してGRUB_CMDLINE_LINUX_DEFAULT実行update-grub && reboot)。

現在、1 か月間本番環境で稼働しており、機能しているようです。私はこれを配置した2つのサーバーの少なくとも1つに破損したサイトがあると確信しており、ダメージを制限するのにかなり効果的です(関連していないサイトは時々数秒間ダウンします)。

このスクリプトの最初のバージョン以降は、ディスクI / O監視だけでは不十分であるため、CPUとメモリも監視するように編集する必要がありました。

#!/bin/bash

DEVICE="254:0" # see /proc/partitions, this is /dev/vda in my case
WRITE_IOLIMIT=2097152 # 2MB/s, choose this as a fraction of your disk max write rate, I tested mine with `dd if=/dev/vda of=/testmaxspeed.dd bs=1M count=10000 status=progress`
READ_IOLIMIT=4194304 # 4MB/s, choose this as a fraction of your disk max read rate, I tested mine with `dd if=/dev/vda of=/dev/null bs=1M count=10000 status=progress`

function cleanup
{
  rm -f "$PLISTFILE" >/dev/null 2>&1
}

trap cleanup INT TERM EXIT

PLISTFILE=$(mktemp)

ps axo user:40,pid,comm | grep php-fpm | grep -v 'grep php-fpm' | grep -v '^root ' | tr -s ' ' | cut -d' ' -f 1-2 | sort | uniq > $PLISTFILE

cat $PLISTFILE | while read ROW ; do
  USERNAME=$(echo "$ROW" | cut -d' ' -f1)
  mkdir /cgroups/$USERNAME 2>/dev/null
  echo "$DEVICE rbps=$READ_IOLIMIT" > /cgroups/$USERNAME/io.max
  echo "$DEVICE wbps=$WRITE_IOLIMIT" > /cgroups/$USERNAME/io.max
  echo 402653184 > /cgroups/$USERNAME/memory.high # throttle when user is above 384MB of RAM
  echo 603979776 > /cgroups/$USERNAME/memory.max # OOM-Kill when user is above 576MB of RAM
done

echo "+io" > /cgroups/cgroup.subtree_control
echo "+cpu" > /cgroups/cgroup.subtree_control
echo "+memory" > /cgroups/cgroup.subtree_control

cat $PLISTFILE | while read ROW ; do
  USERNAME=$(echo "$ROW" | cut -d' ' -f1)
  PROCESSID=$(echo "$ROW" | cut -d' ' -f2)
  echo $PROCESSID > /cgroups/$USERNAME/cgroup.procs  
done

関連情報