cgroups V2 리소스 제한(시스템드 포함)

11분 소요 - 2026년 6월 3일

hero section cover
목차
  • systemd를 사용한 cgroups v2 리소스 제한
  • cgroups v2 활성화
  • systemd가 cgroup을 구성하는 방식
  • CPU 제한
  • cgroups v2의 메모리 제한
  • I/O 제한
  • 슬라이스를 활용한 멀티 테넌트 격리
  • systemd-cgtop 및 PSI를 사용한 모니터링
공유

Cgroups v2 및 systemd로 CPU, 메모리 및 I/O 제한을 설정하세요. PSI 모니터링 및 슬라이스 격리를 통해 멀티테넌트 Linux 호스트를 위한 실용적인 구성.

systemd를 사용한 cgroups v2 리소스 제한

cgroups v2는 리눅스 커널의 통합 리소스 제어 프레임워크입니다. 이는 분산된 v1 계층 구조를 CPU, 메모리, I/O를 일관되게 처리하는 단일 트리로 대체하며, Docker, Kubernetes 및 systemd에서 컨테이너 격리를 뒷받침합니다. 이 게시물에서는 cgroups v2를 활성화하고, systemd를 통해 제한을 설정하며, 실제 멀티 테넌트 호스팅 시나리오에 적용하는 방법을 다룹니다.

cgroups v2 활성화

최신 배포판(Ubuntu 21.10+, Debian 11+, Fedora 31+, RHEL/Rocky 9+)은 기본적으로 cgroups v2가 활성화된 상태로 제공됩니다. 구형 시스템은 하이브리드 계층 구조를 사용하거나 여전히 v1을 기본값으로 설정할 수 있습니다. 다음 명령어로 확인하십시오:

stat -fc %T /sys/fs/cgroup/

다음 명령어의 출력 결과 cgroup2fs 명령어 출력이 v2가 활성화되었음을 확인합니다. tmpfs 는 일반적으로 v1을 의미합니다.

하이브리드 시스템을 순수 v2로 전환하려면 /etc/default/grub 파일을 편집하고 다음 내용을 GRUB_CMDLINE_LINUX_DEFAULT:

systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all

그런 다음 GRUB를 재생성하고 재부팅하십시오:

sudo update-grub
sudo reboot

프로덕션 환경에서는 v2용 cgroup freezer를 사용할 수 있도록 커널 5.2 이상을 실행하고, 완전한 cpuset 위임을 위해 systemd 244 이상을 사용하십시오. Rocky Linux 8 및 RHEL 8에서는 다음 줄을 /etc/systemd/system.conf:

DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes

다음 명령으로 재로드하십시오 sudo systemctl daemon-reexec로 재로드하십시오. 재부팅 후 사용 가능한 컨트롤러를 확인하십시오:

cat /sys/fs/cgroup/cgroup.controllers

다음과 같이 표시되어야 합니다 cpu, memory, io, 그리고 pids 목록에 표시되어야 합니다. 이 컨트롤러들은 기본적으로 자식 cgroup에 대해 활성화되어 있지 않습니다. 이를 활성화하려면 루트 서브트리 제어 파일에 다음과 같이 기록하십시오:

echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control

v2가 내부적으로 v1과 어떻게 다른지에 대한 자세한 설명은 Michael Kerrisk의 NDC TechTown 강연이 가장 훌륭한 자료입니다:

systemd가 cgroup을 구성하는 방식

systemd는 시작하는 모든 서비스에 대해 유닛 이름을 따서 명명된 cgroup을 생성합니다. nginx.service gets /sys/fs/cgroup/system.slice/nginx.service/이 생성되며, 해당 유닛이 생성하는 모든 프로세스는 해당 cgroup 내에 존재합니다. 다음 세 가지 유닛 유형이 계층 구조와 직접적으로 매핑됩니다:

유닛 유형역할설명
.slice내부 노드관련 서비스를 그룹화하고 공유 한도를 정의합니다
.service종단 노드시스템드(systemd)가 시작한 프로세스를 관리합니다
.scope리프 노드외부에서 시작된 프로세스(컨테이너 페이로드, 로그인 세션)를 추적합니다

기본적으로 다음 네 가지 슬라이스가 제공됩니다: -.slice (root), system.slice, user.slice, 및 machine.slice. 슬라이스에 적용된 제한 사항은 해당 슬라이스 내의 모든 서비스에 자동으로 적용됩니다.

기억해 두어야 할 v2 규칙 하나: 프로세스는 리프 노드에서만 실행될 수 있습니다. 자식 cgroup을 가진 cgroup은 프로세스를 직접 호스팅할 수 없기 때문에, systemd는 절대로 서비스를 슬라이스의 트렁크에 배치하지 않습니다.

항상 /sys/fs/cgroup/ 직접 작성하지 마십시오. 수동으로 작성한 내용은 재부팅 후에도 유지되지 않으며, 계층 구조에 대한 systemd의 독점적 소유권과 충돌합니다. 일회성 변경 및 유닛 드롭인(unit drop-ins)의 경우 systemctl set-property 를 사용하고, 영구적인 변경 사항에는systemctl edit nginx.service)을 사용하십시오.

CPU 제한

cgroups v2는 두 가지 CPU 제어 기능을 제공합니다: 하드 캡(cpu.max, systemd에서는 CPUQuota )와 비례 가중치(cpu.weight / CPUWeight).

CPUQuota 는 절대 상한선입니다. CPUQuota=50% 는 코어 절반을 허용합니다; CPUQuota=200% 는 전체 코어 두 개 분량의 시간을 허용합니다. 나머지 CPU가 얼마나 유휴 상태인지와 관계없이, 이보다 더 높은 값을 사용하려고 하면 서비스가 스로틀링됩니다.

CPUWeight 는 경합 상황일 때만 중요합니다. 범위는 1에서 10,000이며, 기본값은 100입니다. 가중치가 각각 150, 100, 50인 세 서비스가 동시에 CPU 시간을 요구할 경우, 대략 50%, 33%, 17%의 CPU 시간을 할당받습니다. CPU가 유휴 상태일 때는 가중치가 아무런 제약을 가하지 않습니다.

지연 시간에 민감한 워크로드의 경우, AllowedCPUs=. 이렇게 하면 컨텍스트 전환이 줄어들고 코어별 캐시가 활성화된 상태를 유지합니다:

[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3

예측 가능한 비용이 필요한 경우(다중 테넌트 과금, 노이즈 이웃 격리)에는 하드 쿼터를 사용하십시오. 하드웨어 활용도를 극대화하고 트래픽 급증 시에만 우선순위 순서가 필요한 경우에는 가중치를 사용하십시오.

cgroups v2의 메모리 제한

메모리는 두 가지 계층으로 나뉩니다: memory.high (소프트, 스로틀링)과 memory.max (하드, OOM)으로 나뉩니다. 스왑, 페이지 회수 및 커널 OOM 킬러에 대한 배경 정보는 Linux 메모리 관리에 관한 관련 게시물을 참조하십시오.

메모리 한도를 memory.high 대략 10~20% 정도 낮게 설정하십시오 memory.max. 이 임계값을 넘으면 커널이 페이지 회수를 시작하고 할당을 제한하며 memory.high 이 값을 초과하면 커널이 페이지 회수 및 할당 제한을 시작하며, 이는 대개 OOM 킬러가 작동하기 전에 워크로드가 회복될 수 있도록 합니다. 사용량이 memory.max에 도달하면 커널은 cgroup 내의 프로세스를 종료합니다.

일반적인 구성:

[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0

MemorySwapMax=0 이 cgroup에 대해 스왑을 비활성화합니다. 스왑 I/O가 테일 지연 시간을 급격히 악화시킬 수 있는 지연 시간에 민감한 워크로드(데이터베이스, 실시간 스트리밍)의 경우 이 설정을 적용하는 것이 좋습니다.

고아 자식 프로세스를 남겨두면 공유 상태가 손상될 수 있는 워커 풀의 경우, 1 cgroup의 memory.oom.group 파일에 기록하십시오. 그러면 하나의 프로세스가 OOM으로 종료될 때 커널이 cgroup 내의 모든 프로세스를 함께 종료합니다.

다음 파일을 확인하여 memory.events 를 확인하여 서비스가 얼마나 자주 스로틀링되거나 OOM으로 종료되었는지 확인하십시오:

cat /sys/fs/cgroup/system.slice/nginx.service/memory.events

다음 highoom_kill 카운터를 확인하면 제한값이 적절하게 설정되었는지 알 수 있습니다. 0이 아닌 값이 지속적으로 나타난다면 워크로드에 더 많은 여유 공간이 필요하다는 의미입니다.

I/O 제한

I/O 컨트롤러는 동일한 2가지 모드 설계 방식을 채택하고 있습니다: io.max 를 통한 비례적 공유 io.weight.

제한은 블록 장치별로 적용되며, major:minor 번호로 식별됩니다. 다음 명령어로 확인할 수 있습니다 lsblk -o NAME,MAJ:MIN를 사용하여 확인할 수 있습니다. 일반적인 systemd 구성:

[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500

io.weight 다음과 같이 작동합니다 cpu.weight: 범위는 1에서 10,000이며, 기본값은 100입니다. 사용자 대상 서비스에는 500을, 야간 백업에는 50을 할당하면 피크 시간대에 백업 작업으로 인해 디스크가 포화되는 것을 방지하면서도, 다른 작업이 필요하지 않을 때는 전체 대역폭을 사용할 수 있게 합니다.

I/O 제한은 올바른 장치를 대상으로 지정했을 때만 적용됩니다. 커널은 블록 장치별로 I/O를 추적하므로, /dev/sda 에 대한 제한은 /dev/nvme0n1로 전송되는 I/O에는 아무런 효과가 없습니다. 디스크가 여러 개 있는 호스트에서는 장치별로 제한을 설정하십시오.

슬라이스를 활용한 멀티 테넌트 격리

공유 환경의 경우, 테넌트당 하나의 슬라이스를 정의하십시오. /etc/systemd/system/tenant-a.slice:

[Slice]
CPUQuota=200%
CPUWeight=150
MemoryHigh=3584M
MemoryMax=4096M
MemorySwapMax=0
IOReadBandwidthMax=/dev/sda 200M
TasksMax=512

TasksMax=512 는 프로세스 및 스레드의 총 수를 제한하여, 한 테넌트에서 발생한 포크 폭탄(fork bomb)이 호스트를 다운시키는 것을 방지합니다. 테넌트 서비스를 이 슬라이스에 배치하면( Slice=tenant-a.slice 유닛 파일 내)에 테넌트 서비스를 배치하면 모든 설정이 자동으로 상속됩니다.

이 패턴은 소음이 많은 백그라운드 작업을 사용자 대상 서비스와 분리하는 데에도 유용합니다. 백업, 로그 순환 및 배치 작업을 background.slice 자원을 적게 CPUWeight 값을 가진슬라이스에 배치하십시오. io.weight 값을 가진 슬라이스에 배치하세요. 시스템이 유휴 상태일 때는 전체 리소스를 할당받고, 프로덕션 트래픽이 유입되면 리소스를 양보합니다.

DockerPodman과 같은 컨테이너 런타임의 경우, Delegate=yes 를 추가하세요. 이렇게 하면 루트 권한 없이도 자체 서브 cgroup을 관리할 수 있으며, 상위 슬라이스에 설정된 제한은 하위 모든 요소에 그대로 적용됩니다.

systemd-cgtop 및 PSI를 사용한 모니터링

cgroup별 CPU, 메모리, I/O에 대한 실시간 top 스타일 보기를 보려면 다음을 실행하십시오:

systemd-cgtop

정적 계층 구조와 각 프로세스의 위치를 확인하려면 다음을 사용하십시오: systemd-cgls.

프로덕션 모니터링에 있어 v2 버전에서 가장 유용한 기능은 압력 스톨 정보(PSI)입니다. PSI는 cgroup 내의 태스크가 리소스를 기다리며 스톨된 시간의 비율을 보고하며, cgroup당 세 개의 파일로 제공됩니다:

cat /sys/fs/cgroup/tenant-a.slice/cpu.pressure
cat /sys/fs/cgroup/tenant-a.slice/memory.pressure
cat /sys/fs/cgroup/tenant-a.slice/io.pressure

사용률이 100%이고 압력(pressure)이 0%인 CPU는 정상 상태입니다. CPU를 원하는 모든 태스크가 CPU를 할당받고 있는 것입니다. 동일한 CPU가 사용률 80%에 압력 30%인 경우, 태스크들이 실행 시간을 기다리며 대기열에 들어와 있음을 의미합니다. 사용률이 아닌 PSI에 대해 경보를 설정하십시오. 사용률 지표로는 전혀 포착하지 못하는 경합 현상을 PSI가 잡아내기 때문입니다.

시스템을 재시작하지 않고도 실시간으로 제한값을 조정할 수 있습니다:

sudo systemctl set-property tenant-a.slice MemoryMax=6144M

변경 사항은 즉시 적용되며 재부팅 후에도 유지됩니다. PSI 기반 경보와 결합하면, OOM(메모리 부족)으로 인한 프로세스 강제 종료나 통제 불능의 지연 시간으로 이어지기 전에 부하 변화에 대응할 수 있습니다.

고밀도 멀티테넌트 워크로드를 실행 중이며 이러한 정책을 원활하게 적용할 수 있는 여유 용량을 갖춘 호스트가 필요하다면, 당사의 전용 서버가 이를 위해 설계되었습니다.

블로그

이번 주 추천

더 많은 기사
강력하고 계량되지 않는 VPS가 중요한 이유

강력하고 계량되지 않는 VPS가 중요한 이유

미터링되지 않은 VPS는 고정된 포트 속도로 정액제 대역폭을 제공합니다. 계량형 요금제와 어떻게 다른지, 언제 요금이 부과되는지, 구매 전에 확인해야 할 사항은 무엇인지 알아보세요.

7분 소요 - 2025년 5월 9일

Linux 메모리 관리: 스왑, OOM 킬러 및 C그룹

12분 소요 - 2026년 5월 31일

더 많은 기사
background image

질문이 있거나 맞춤형 솔루션이 필요하신가요?

icon

유연한 옵션

icon

글로벌 도달 범위

icon

즉시 배포

icon

유연한 옵션

icon

글로벌 도달 범위

icon

즉시 배포