cgroups: Linux에서 사용자별 디스크 I/O 대역폭 제한

cgroups: Linux에서 사용자별 디스크 I/O 대역폭 제한

나는 Apache + PHP-FPM인 Linux 웹 서버(만약의 경우 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...

자주 발생하는 일은 일부 웹사이트가 해킹당하고(업데이트하는 것을 잊어버리고 뻔뻔하게 안전하지 않은 플러그인을 설치하는 등) 사용 가능한 서버 리소스를 차지하기 시작한다는 것입니다. 제 경우에는 하드 드라이브 I/O 대역폭이 가장 일반적이었습니다.

동일한 서버에 있는 모든 웹사이트에 DoSing을 방지하기 위해 해킹된 웹사이트만 충돌하고 다른 모든 웹사이트에는 영향을 주지 않도록 각 웹사이트의 하드 드라이브 I/O 대역폭을 제한하고 싶습니다.

웹사이트당 하드 디스크 I/O 대역폭을 제한하는 방법은 무엇입니까? 글쎄요, 각 사용자마다 VirtualHost전용 사용자가 있기 때문에 사용자별로 제한할 수 있을 것 같습니다 . 어떻게 해야 합니까? 글쎄요, Google에서는 cgroup을 사용하라고 제안했습니다.

하지만 추가하는 방법에 대해서는 아무것도 찾을 수 없습니다.사용자, 프로세스 ID 대신 제어 그룹에 전달됩니다.

내가 잘못된 것을 찾고 있는 걸까? 아니면 제가 키워드를 잘못 검색한 걸까요? 잘 모르겠지만 질문은 다음과 같습니다. Linux에서 cgroup을 사용하여 각 사용자의 하드 디스크 I/O 대역폭을 제한하는 방법은 무엇입니까?

답변1

감사해요LL3내 질문에 대한 의견에 대한 응답으로 맞춤 스크립트를 사용해야 한다는 사실을 알게 되었습니다. 그래서 그게 다입니다. PHP-FPM 프로세스를 조회하고, 해당 실행 사용자와 PID를 가져오고, cgroupsPID를 생성하여 올바른 cgroups.

crontab그런 다음 스크립트가 매시간 실행되도록 항목을 구성했는데 , 제 경우에는 충분했습니다. Linux는 자동으로 하위 프로세스를 상위 프로세스에 넣기 cgroup때문에 스크립트는 실제로 PHP-FPM이 다시 시작할 때마다 실행하면 됩니다. 시작 후 한 번 실행 (예: 시작 시 또는 새 사이트/사용자 추가 시) 매시간 실행되므로 예정된 시간에 수동으로 실행하는 것을 잊지 않도록 도와줍니다.

스크립트는 /cgroups디렉토리가 파일 시스템으로 마운트된다고 가정 cgroup2하므로 이를 미리 준비해야 합니다. /etc/fstab커널이 cgroups기본적으로 버전 1을 설치하는 경우 /etc/default/grubv1을 비활성화하고 재부팅하도록 구성하는 항목을 추가할 수 있습니다 (팁: cgroup_no_v1=all에 추가한 GRUB_CMDLINE_LINUX_DEFAULT후 실행 update-grub && reboot).

현재 한 달 동안 프로덕션 환경에서 실행 중이며 작동하는 것 같습니다. 나는 이것을 배치한 두 서버 중 적어도 하나에 손상된 사이트가 있다고 확신하며 피해를 제한하는 데 꽤 효과적입니다(관련되지 않은 사이트는 때때로 몇 초 동안만 다운됩니다).

이 스크립트의 첫 번째 버전 이후에는 디스크 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

관련 정보