Writings/Linux kernel2017. 12. 11. 05:22

다음으로 소개할 방법은 cgroup을 이용하는 방법이다.

Cgroup (control group)은 리눅스 커널이 제공하는 시스템의 자원 사용률을 그룹별로 제어하기 위한 방법이다.

이 또한 sysfs 인터페이스를 통해 사용 가능한데, 기존 cgroup v1과 Linux 4.5.x 때부터(정확한지는 모르겠지만 이때쯤) 지원하기 시작한 cgroup v2 두 종류가 있다. 사용방법이 사뭇 다르므로 정확한 용법은 커널 도큐먼트(Documentation/cgroup-v2.txt)를 확인해 보는 것이 좋다. 

일단 아무데나 원하는 위치에 디렉토리를 만들고, 다음과 같이 cgroup sysfs를 마운트하자.


# mkdir cgroup

# mount -t cgroup2 none ./cgroup


해당 마운트포인트에서 ls -l 명령을 실행시키면 다음과 같은 결과가 나올 것이다.


-r--r--r-- 1 root root 0 Dec  9 03:20 cgroup.controllers

-rw-r--r-- 1 root root 0 Dec  9 03:20 cgroup.procs

-rw-r--r-- 1 root root 0 Dec  9 03:21 cgroup.subtree_control


여기서 cgroup.controllers는 현재 cgroup v2가 사용가능한 컨트롤러들을 뜻한다. 부트 파라미터를 통해 cgroup v1을 효과적으로 disable 했다면, (혹은 애초에 cgroup v1을 사용하지 않는다면) 다음과 같은 결과를 볼 수 있다.


# cat cgroup.controllers

io memory pids


I/O와 memory, 그리고 pid에 대한 컨트롤러를 제공하며, 이 말은 cgroup 인터페이스를 통해 원하는 그룹에 저 세 가지 자원에 대한 제한을 걸 수 있다는 뜻이 된다.

cgroup.procs 는 현재 해당 컨트롤 그룹에 속해있는 프로세스를 의미하며, 현재 시스템에 등록되어 동작하고 있는 대부분의 프로세스가 여기에 등록되어 있을 것이다. 즉, 마운트포인트의 루트 디렉토리는 시스템의 루트 컨트롤 그룹에 대한 정보가 등록되어 있는 것이다. 

cgroups.subtree_control 은 하위 컨트롤 그룹에 대해 어떤 컨트롤러를 사용할 것인지 지정하는 것이다. 현재는 아무런 정보도 없을 것이다.


이제 새로운 컨트롤 그룹을 생성할 시간이다. 아래의 명령을 입력해보자.


# mkdir cgroup_child


생성된 디렉토리 아래로 가게 되면, 부모 디렉토리와 마찬가지로 cgroup.controllers, cgroup.subtree_control, cgroup.procs, 그리고 cgroup.events 파일이 존재할 것이다. 

생성된 그룹에 프로세스를 추가하기 위해서는, 해당 프로세스의 pid를 알아내어 다음과 같이 입력하면 된다.


# echo "pid" > ./cgroup/cgroup_child/cgroup.procs


그리고 원하는 컨트롤러를 "부모" 디렉토리의 cgroup.subtree_control에 입력하자. 여기서는 페이지 캐시 사용량을 제한하려고 하니, memory 컨트롤러를 입력하면 된다.


# echo "+memory" > ./cgroup/cgroup.subtree_control


여기서 +는 해당 컨트롤러를 추가한다는 의미이며, -로 입력할 경우 해당 컨트롤러를 제거한다는 의미가 된다. 

해당 명령을 수행하면, 자녀 디렉토리에 다음과 같은 파일들이 추가로 생성된다.


-r--r--r-- 1 root root 0 Dec  9 03:21 cgroup.controllers

-r--r--r-- 1 root root 0 Dec  9 03:21 cgroup.events

-rw-r--r-- 1 root root 0 Dec  9 03:21 cgroup.procs

-rw-r--r-- 1 root root 0 Dec  9 03:21 cgroup.subtree_control

-r--r--r-- 1 root root 0 Dec  9 03:21 memory.current

-r--r--r-- 1 root root 0 Dec  9 03:22 memory.events

-rw-r--r-- 1 root root 0 Dec  9 03:21 memory.high

-rw-r--r-- 1 root root 0 Dec  9 03:22 memory.low

-rw-r--r-- 1 root root 0 Dec  9 03:21 memory.max

-r--r--r-- 1 root root 0 Dec  9 03:21 memory.stat

각각의 의미는 다음과 같다.


 File 

Description 

 memory.current

컨트롤 그룹의 현재 메모리 사용량 

 memory.high

메모리 사용량의 soft limit 

 memory.low

메모리 사용량의 하한 

 memory.max

메모리 사용량의 hard limit 

 memory.events 

low, high, max, oom 에 대한 이벤트 횟수

 memory.stat

컨트롤 그룹의 메모리 사용량 통계 


메모리 사용량이 soft limit(memory.high)에 도달할 경우, 해당 그룹의 프로세스들을 throttling 하며, memory 회수를 빈번하게 수행시키기 위한 압박을 주게 된다. 만약, 메모리 사용량이 hard limit (memory.max)를 넘어서게 될 경우, 커널은 OOM killer를 호출하여 해당 프로세스를 종료시키게 된다.

두 항목에 적절한 값을 설정하고, free 명령을 통해 메모리 사용량 변화의 추이를 살펴보자.

root@ubuntu:/home/ubuntu# free -c 10

              total        used        free      shared  buff/cache   available

Mem:       65722960      410052    59913708        9736     5399200    64511200

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410612    59913580        9736     5398768    64510436

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410908    59913108        9736     5398944    64510268

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      411000    59913268        9736     5398692    64510304

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410824    59913176        9736     5398960    64510368

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410348    59913612        9736     5399000    64510896

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410980    59913340        9736     5398640    64509924

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410580    59913672        9736     5398708    64510560

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      411080    59912944        9736     5398936    64509680

Swap:      33326076           0    33326076


              total        used        free      shared  buff/cache   available

Mem:       65722960      410504    59913820        9736     5398636    64510612

Swap:      33326076           0    33326076


모든 가용영역을 캐시용도로 쓰던 과거에서 벗어나, 제한된 만큼 사용하는 것을 확인할 수 있다.


엄밀히 따지자면, 이 방법은 페이지 캐시를 제한하기 위해 존재하는 방법은 아니다.

하지만 내 용도가 페이지 캐시를 제한하는 것 이었던 만큼 이 항목으로 포스팅을 남긴다.

'Writings > Linux kernel' 카테고리의 다른 글

페이지 캐시의 크기 제한걸기 (1/2)  (0) 2017.12.10
Enabling Cgroup v2  (0) 2017.12.09
Posted by 곰푼
Writings/Linux kernel2017. 12. 10. 00:10

리눅스 커널은 I/O 성능을 높이기 위해 페이지 캐시를 제공한다. 


페이지 캐시는 파일 시스템의 각 오퍼레이션들에 삽입되어 (엄밀히 말하자면 VFS 인터페이스겠지만,) 블록 디바이스의 데이터 페이지를 메모리에 캐싱해 둔다. 

이후 요청이 들어왔을 때 해당 블록 어드레스에 대한 페이지가 메모리 내에 있을 경우, 캐시 히트로 간주하여 디바이스에 대한 I/O를 발생시키지 않고 메모리에 캐싱되어 있는 페이지를 제공한다. 

Write가 발생했을 때도 이를 바로 블록 디바이스로 보내는 것이 아니라  메모리에 캐싱해두며, 특정 시점 혹은 fsync() 호출이 발생할 때까지 동일한 블록 어드레스에 대한 모든 write를 흡수한다. 


이런 캐시는 I/O가 빈번이 발생하는 경우에는 도움이 되겠지만, 커널의 메모리 사용량을 늘리는 결과를 야기시키기도 한다.


커널은 이를 제한할 수 있는 인터페이스를 sysfs를 통해 제공하게 되는데, 그 안을 보면 꽤 많은 항목들이 있다. 일단 경로는 다음과 같다.


/proc/sys/vm


이 디렉토리 중, 다음의 두 항목을 살펴보자.


/proc/sys/vm/drop_caches

/proc/sys/vm/vfs_cache_pressure


drop_caches는 입력에 따라 서로 다른 수준으로 시스템의 캐시를 비워주게 된다. 


1) echo 1 > /proc/sys/vm/drop_caches // 페이지 캐시만을 비운다.

2) echo 2 > /proc/sys/vm/drop_caches // dentry 캐시와 inode 캐시를 비운다.

3) echo 3 > /proc/sys/vm/drop_caches // inode, dentry, 페이지 캐시 모두를 비운다.


페이지 캐시는 실제 파일에 저장되는 데이터 페이지를 캐싱하며, dentry 및 inode 캐시는 파일시스템에서 사용하는 메타데이터를 캐싱한다. 

즉, 1번 명령은 데이터 캐시를 비우는 명령이며, 2번 명령은 메타데이터 캐시를 비우는 명령인 것이다.

참고로, 위의 명령들은 캐시를 디스크에 flush 시키는 명령이 아니기 때문에 먼저 sync를 실행시킨 뒤에 실행 시킬 것을 권장하고 있다. 


두번째는 vfs_cache_pressure 이다.

해당 항목에 대한 커널 도큐먼트의 설명을 살펴보면, 메모리 회수를 위해 inode 및 dentry 캐시에 압박을 준다고 되어 있다. 

100을 기준으로 그 위의 값은 일반적인 것보다 더 큰 압박을 주는 것이고, 그 아래의 값은 압박을 덜 줌으로써 좀더 메타데이터를 캐싱할 수 있도록 하는 것이다.

특별히, 0은 절대 주지 말라고 한다. 해당 메모리가 회수가 안되서 OOM 킬러에 의한 오작동이 나타날 수 있다고 한다.

'Writings > Linux kernel' 카테고리의 다른 글

페이지 캐시의 크기 제한걸기 (2/2)  (0) 2017.12.11
Enabling Cgroup v2  (0) 2017.12.09
Posted by 곰푼
Writings/Linux kernel2017. 12. 9. 03:29

Cgroup을 사용할 일이 생겨서 뒤적거리던 중, cgroup v2가 나왔단 사실을 알게 되었다.


지금 사용중인 커널 (4.8.x) 버전에서는 두 cgroup이 모두 탑재되어 있는데, 그냥 부팅하면 커널이 알아서 모든 cgroup 컨트롤러를 기존 cgroup에 줘버린다. 따라서 cgroup v2로는 할 수 있는 일이 없다.


이를 해결할 수 있는 방법은, 커널 파라미터를 통해 컨트롤러의 권한을 해제하는 것이다.


/etc/default/grub 파일을 열어서, GRUB_CMDLINE_LINUX_DEFAULT= 항목에 다음과 같이 파라미터를 작성하자.

GRUB_CMDLINE_LINUX_DEFAULT="cgroup_no_v1=all"


혹은 cpu, memory 와 같은 특정 컨트롤러를 지정해도 무방한 듯 하다.


참고자료

https://lkml.org/lkml/2016/2/11/603



Posted by 곰푼