cgroup의 CPU·메모리 제어 방식
linux kernel에서 cgroup(control groups)은 단순히 프로세스를 묶는 바구니가 아니다.
시스템 리소스를 추상화해 분배하고 격리하는 하부 시스템 핵심이다.
커널 내에서 계츠 구조를 가지고 각 프로세스는 특정 cgroup에 속하며 그룹들은 트리 구조로 연결된다.
- cgroup v1 vs v2: 과거에는 리소스별로 계층 구조가 분리되어 관리 효율성이 떨어졌으나, 현재 표준인 v2 Unified Hierarchy를 사용하여 모든 리소스 컨트롤러가 하나의 트리 아래에서 관리된다.
- 컨트롤러: cpu, memory, io등 특정 자원을 관리하는 커널 모듈이다.
CFS, Scheduling #
리눅스는 기본적으로 CFS(Completely Fair Scheduler)를 사용해 cpu 시간을 분배한다.
cgroup은 이 CFS의 동작 방식에 개입하여 자원을 할당한다.
Weight 기반 할당 (Shares) #
절대적인 수치가 아닌 상대적인 비율로 cpu를 할당한다.
- 메커니즘: 각 cgroup은
cpu.weight값을 가진다. 전체 가중치 합산 중 해당 그룹이 차지하는 비중만큼 cpu 시간을 점유한다. - 특징으로는 시스템이 한가할 때 제한없이 cpu를 쓰다가, 경합(contention)이 발생할때만 설정된 비율로 제한한다. 이를 work-conserving 방식이라 한다.
Quota/Period 기반 할당 #
특정 시간 동안 사용할 수 있는 절대 시간을 강제한다.
period 동안 quota만큼 다 사용하면, 해당 cgroup의 모든 프로세스는 다음 주기가 올 때까지 throttling 상태가 된다.
이는 실시간성이 중요한 서비스에서 주의해야할 지점이다.
Memory 제어 메커니즘: RSS, Page Cache #
메모리는 cpu와 달리 시간을 나눠쓰는것이 아니라 공간을 점유하는 자원이므로 제어 방식이 더 복잡하고 위험하다.
메모리 회계 accounting #
커널은 프로세스가 메모리를 할당받을때마다 해당 cgroup의 카운터를 올린다.
- 익명 메모리 RSS, 페이지 캐시 page cache, 커널 메모리 slab 등이 모두 포함된다.
memory.max(hard limit),memory.low/high(soft limit, protection)으로 구분된다.
메모리 회수 및 oom killer #
- Reclaim: cgroup이 제한값에 도달하면 커널은 직시 page reclaim을 시도한다. 주로 페이지 캐시를 비우고
- OOM: 회수할 메모리가 없는데 더 요구하면 oom killer를 커널이 호출한다.
- 선별: cgroup 내에서 oom_score가 가장 높은 프로세스를 종료시킨다. v2에서는 그룹 전체를 한꺼번에 종료시키는 memory.oom.group 설정도 가능하다.
커널 내부 동작 원리 #
cgroup은 /sys/fs/cgroup 이라는 VFS(Virtual File System)을 통해서 인터페이스를 제공한다.
우리가 이 디렉토리의 파일을 수정하면 커널 내부의 cgroup_subsys_state 구조체 값이 변경되고 스케줄러와 메모리 관리자 MM가 이 값을 참조하여 동작을 결정한다.
Resource Counter와 가상 파일 시스템으로 기억해두면 된다.
추가로 cpu quota를 너무 타이트하게 잡으면, 프로세스가 작업을 채 마치기 전에 중단되어
Tail Latency가 급증할 수 있다. 이를 모니터링 하기위해 cpu.stat 파일의 nr_throttled 수치를 반드시 확인해야한다.
현대의 쿠버네티스나 도커 컨테이너 격리 실체는 바로 이 cgroup과 namespace 격리의 조합이다.