| cgroups | |
| 원저자 | Paul Menage, Rohit Seth(v1)[1] |
| 최초 출시 | 2007년[2] |
| 개발 언어 | C |
| 운영체제 | 리눅스 |
| 라이선스 | GPL, LGPL |
| 종류 | 리눅스 커널 기능 |
1. 개요
cgroups(control groups)는 프로세스 집합의 자원 사용량(CPU, 메모리, 디스크 I/O 등)을 제한하고, 집계하고, 격리하는 리눅스 커널 기능이다. chroot가 파일시스템 관점의 격리를 담당한다면, cgroups는 그 반대편에서 프로세스 집합이 실제로 얼마나 많은 하드웨어 자원을 쓸 수 있는지를 통제하는 역할을 한다. 오늘날 Docker, Kubernetes를 비롯한 대부분의 컨테이너 기술이 격리를 위해 네임스페이스와 함께 필수적으로 의존하는 핵심 커널 기능이다.2. 역사
cgroups의 시초는 2006년 구글 엔지니어 Paul Menage와 Rohit Seth가 시작한 작업이다. 이들은 훗날 Borg, 그리고 이어서 Kubernetes로 발전하게 되는 구글 내부의 대규모 공유 리눅스 클러스터 관리 시스템을 운영하고 있었는데, 기존의 `ulimit` 같은 도구는 프로세스 하나하나에 대해서만 자원 제한을 걸 수 있을 뿐, "이 프로세스 무리 전체가 아무리 많은 자식 프로세스를 만들더라도 합쳐서 2GB 메모리를 넘지 못하게 한다"는 식의 그룹 단위 제어는 지원하지 않는다는 한계가 있었다. 이를 해결하기 위해 개발된 기능이 처음엔 "프로세스 컨테이너(process containers)"라는 이름으로 불렸다.그러나 2007년 말, 리눅스 커널 맥락에서 "컨테이너"라는 용어가 이미 여러 의미로 혼용되고 있어 혼란을 준다는 지적에 따라, 명칭이 지금의 "제어 그룹(control groups, 줄여서 cgroups)"으로 바뀌었다. 이렇게 이름을 바꾼 뒤 2008년 1월 출시된 리눅스 커널 2.6.24에 정식으로 병합되었다.
이후 커널 자체의 메모리 할당, netfilter 방화벽, OOM 킬러 등 다양한 부분에 대한 컨트롤러가 추가되며 기능이 계속 확장되었다. 다만 최초 설계(현재는 'v1'이라 부름)는 컨트롤러마다 독립적인 계층 구조를 가지는 방식이었는데, 이 방식이 여러 구조적 문제를 낳는다는 지적이 이어지면서, 이후 개발과 유지보수를 이어받은 Tejun Heo가 단일 통합 계층 구조로 전면 재설계한 cgroup v2를 내놓았다. cgroup v2 문서는 2016년 3월 출시된 리눅스 커널 4.5에서 처음 등장했다.
3. v1과 v2의 구조적 차이
3.1. v1: 다중 계층 구조와 그 문제점
cgroup v1에서는 CPU, 메모리, 블록 I/O(blkio) 등 자원 종류별 컨트롤러가 각각 독립된 계층 구조(트리)를 가졌다. 그 결과 하나의 프로세스가 cpu 계층에서는 `/production` 그룹에 속해 있으면서, 동시에 memory 계층에서는 `/testing` 그룹에 속하는 식의 불일치가 발생할 수 있었다. 이런 유연성은 복잡하고 독립적인 자원 배분을 가능케 하려는 의도로 설계된 것이었지만, 실제로는 "이 애플리케이션에 정확히 어떤 제한이 걸려 있는가"를 하나의 통합된 시각으로 파악하기 어렵게 만드는 부작용을 낳았다. systemd가 가능한 한 많은 컨트롤러를 하나의 트리에 공동 마운트하려는 "하이브리드" 모드를 도입했던 것도 이런 v1의 구조적 한계를 우회하기 위한 임시방편에 가까웠다.3.2. v2: 단일 통합 계층
cgroup v2는 이런 문제를 해결하기 위해 모든 컨트롤러가 하나의 단일한 계층 구조를 공유하도록 전면 재설계되었다. 하나의 cgroup이 여러 컨트롤러(cpu, memory 등)를 동시에 연결해서 가질 수 있게 되어, "cpu 트리 따로, memory 트리 따로" 존재하던 v1의 파편화가 해소되었다. 이 밖에도 다음과 같은 개선이 이루어졌다.- 안전한 하위 트리 위임: 권한이 낮은 프로세스에게도 cgroup 트리의 일부를 안전하게 위임할 수 있게 됨.
- Pressure Stall Information(PSI): 시스템이 CPU·메모리·I/O 부족으로 얼마나 지연을 겪고 있는지 정량적으로 보여주는 신기능.
- 컨트롤러 정리: v1의 `cpu`와 `cpuacct` 컨트롤러가 v2에서는 `cpu` 하나로 통합되었고, `blkio`는 `io`로 이름이 바뀌었다.
- 일관된 파일 기반 API: 컨트롤러마다 제각각이던 인터페이스가 `cpu.max`, `memory.max`, `io.max`처럼 일관된 규칙을 따르는 파일들로 재정비되었다.
4. 동작 방식
cgroups는 `/sys/fs/cgroup/` 아래에 마운트되는 가상 파일시스템 형태로 사용자에게 노출된다. 디렉터리를 만드는 것이 곧 하나의 cgroup을 만드는 것이고, 그 안의 파일에 값을 읽고 쓰는 것만으로 제어가 이루어진다. 예를 들어 어떤 cgroup의 `cpu.max` 파일에 값을 쓰면 해당 그룹에 속한 프로세스들이 쓸 수 있는 CPU 시간의 상한이 정해지고, `memory.max`에 값을 쓰면 메모리 사용량 상한이 정해지는 식이다. 이 때문에 cgroups는 종종 "복잡한 자원 관리 문제를 파일 하나 다루는 것처럼 단순한 인터페이스로 바꿔놓았다"는 평, 즉 "모든 것은 파일이다"라는 유닉스 철학을 자원 관리 영역에 그대로 적용한 사례로 소개되기도 한다.특정 프로세스를 어떤 cgroup에 소속시키려면, 그 cgroup 디렉터리 안의 `cgroup.procs` 파일에 해당 프로세스의 PID를 적어 넣으면 된다.
5. 주요 기능
- 자원 제한(Resource limiting): 그룹이 설정된 메모리 한도(파일시스템 캐시 포함)나 I/O 대역폭, CPU 사용량 한도를 넘지 못하도록 강제한다.
- 우선순위 부여(Prioritization): 여러 그룹이 자원을 두고 경쟁할 때, 특정 그룹에 더 큰 몫을 배분할 수 있다.
- 제어(Control): 프로세스 그룹 전체를 일시 정지(freeze)시키는 등의 제어가 가능하다.
- 계정(Accounting): 각 그룹이 실제로 얼마만큼의 자원을 사용했는지 집계한다.
6. systemd와의 관계
systemd는 cgroups를 서비스 관리의 핵심 축으로 적극 활용한다. 서비스 유닛 파일에 `CPUQuota=`, `MemoryMax=`, `IOReadBandwidthMax=` 같은 속성을 지정하는 것만으로, systemd가 내부적으로 해당 서비스의 cgroup을 만들고 대응하는 컨트롤러 파일에 값을 기록해 주는 식이다. 사용자가 `/sys/fs/cgroup/`을 직접 만지지 않아도, systemd가 서비스 시작·종료에 맞춰 cgroup 생성과 정리(cleanup)를 자동으로 처리해 준다.systemd는 한때 v1 방식을 그대로 쓰는 legacy 모드, v1과 v2를 함께 마운트하는 hybrid 모드, 순수 cgroup v2 로직만 노출하는 unified 모드를 모두 지원했으나, legacy와 hybrid 모드는 이후 지원 중단이 예고되었다가 제거되었다. Ubuntu 22.04 이상, Debian 11 이상, RHEL 9 이상을 비롯한 대부분의 최신 배포판은 기본값으로 unified(순수 v2) 모드를 사용한다.
7. 컨테이너 기술과의 관계
Docker나 Kubernetes 같은 컨테이너·오케스트레이션 도구들은 사실 자체적으로 자원 제한을 발명하는 것이 아니라, 커널의 cgroups 기능을 사용하기 쉽게 감싸는 고수준 관리자에 가깝다. 예를 들어 Kubernetes의 Pod 정의에서 `resources.limits`로 CPU·메모리 한도를 지정하는 것은, 내부적으로는 결국 해당 Pod에 대응하는 cgroup의 `cpu.max`, `memory.max` 파일에 값을 써 넣는 것에 대한 친절한 표현일 뿐이다.한편 격리라는 관점에서 자주 chroot와 짝지어 설명되는데, chroot가 프로세스가 보는 파일시스템의 범위를 제한하는 역할을 한다면, cgroups는 그 프로세스가 실제로 쓸 수 있는 하드웨어 자원의 총량을 제한하는 역할을 담당한다. 여기에 프로세스ID·네트워크·마운트 등을 서로 격리하는 네임스페이스까지 더해지면, 이 세 가지 커널 기능의 조합이 곧 오늘날 흔히 부르는 "컨테이너"의 실체에 해당한다.
8. 여담
- cgroups라는 기능 자체는 컨테이너라는 개념보다 먼저 존재했다. 애초에 "프로세스 컨테이너"라는 이름으로 개발이 시작됐지만 정작 오늘날 이해하는 형태의 "컨테이너"(격리된 애플리케이션 실행 환경)를 완성하려면 cgroups 하나만으로는 부족하고, 네임스페이스 기능까지 함께 필요하다.
- 구글은 cgroups를 자사 내부 클러스터 관리 시스템인 Borg에서 오랫동안 실사용해 왔으며, 이 경험이 훗날 오픈소스로 공개된 Kubernetes의 설계에도 직간접적으로 영향을 준 것으로 여겨진다.