cgroups:在 Linux 上限制每個使用者磁碟 I/O 頻寬
我有一個 Linux Web 伺服器(Debian,以防萬一),它是 Apache + PHP-FPM。每個 Apache 都
VirtualHost
使用一個專用的 PHP-FPM 池(通過一個專用的 unix 套接字),它在一個專用的系統使用者下執行。例如,假設我有 example.com
VirtualHost
。然後,在VirtualHost
Apache 配置中,我有類似的東西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...
它經常發生,一些網站被黑客入侵(忘記更新,安裝了無恥的不安全外掛,你的名字)並開始佔用可用的伺服器資源:在我的情況下,硬碟 I/O 頻寬是最常見的。
為了避免在同一台伺服器上對所有網站進行 DoS,我想限制每個網站的硬碟 I/O 頻寬,這樣一個被黑的網站就會單獨崩潰,而不會佔用所有其他網站。
如何限制每個網站的硬碟 I/O 頻寬?好吧,我想我可以改為限制每個使用者,因為每個
VirtualHost
使用者都有一個專用使用者。我該怎麼做?好吧,Google向我推薦了 cgroups。但是我找不到任何關於如何將使用者而不是程序 ID 添加到控制組的範例。
我在尋找錯誤的東西嗎?還是我搜尋了錯誤的關鍵字?我不知道,但問題是:如何在 Linux 下使用 cgroups 限制每個使用者的硬碟 I/O 頻寬?
感謝LL3對我的問題的評論,我發現我不得不求助於自定義腳本。所以就在這裡。它查找 PHP-FPM 程序,獲取相應的執行使用者和 PID,創建 PID
cgroups
並將 PID 插入正確的cgroups
.然後我配置了一個
crontab
條目,以便腳本每小時執行一次,這在我的情況下綽綽有餘:Linux自動將子程序放入父程序cgroup
,所以這個腳本實際上只需要在每次 PHP-FPM 重啟後執行一次(例如在啟動時或添加新網站/使用者時)。每小時執行一次它有助於避免在到期時忘記手動執行它。該腳本假定該
/cgroups
目錄與cgroup2
文件系統一起掛載,因此您應該事先安排好它。您可以添加一個條目/etc/fstab
,如果您的核心cgroups
預設安裝版本 1,則配置/etc/default/grub
為禁用 v1 並重新啟動(提示:添加cgroup_no_v1=all
到GRUB_CMDLINE_LINUX_DEFAULT
然後執行update-grub && reboot
)。它已經在生產中執行了一個月,並且似乎可以正常工作。我很確定至少在我放置它的兩台伺服器中的一台伺服器中存在被入侵的網站,並且它在限制損害方面做得很好(不相關的網站有時只會關閉幾秒鐘)。
請注意,從這個腳本的第一個版本開始,我不得不對其進行編輯以使其也監控 CPU 和記憶體,因為僅監控磁碟 I/O 是不夠的。
#!/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