메뉴

문서정보

목차

소개

사용자에게 컨테이너를 제공하는 서비스를 개발하고 있다. 퍼블릭하게 서비스를 하기 때문에, 사용자 별로 cpu와 메모리, 디스크등에 대한 자원을 제한할 수 있어야 한다. 이중 컨테이너별로 cpu와 메모리를 제한하는 방법을 살펴보려고 한다.

환경

CPU

cpu limit

cpu-quota는 cpu-priod와 함께 사용한다. cpu-priod의 기본 값은 100000(100ms)로, cpu-quota를 이용해서 100ms 동안 어느정도의 cpu 할당할 것인지를 설정할 수 있다. cpu-quota가 25000 이라면, 1/4 만큼의 cpu 자원을 사용 할 수 있다.

cpu-quota와 cpu-priod는 아래와 같이 사용한다.
# docker run --cpu-period=100000 --cpu-quota=25000 -it ubuntu /bin/bash

cpu-period를 50000으로 하고 --cpu-quota를 25000으로 할 경우, cpu-quota는 그대로이지만 cpu 주기가 1/2로 줄어들었기 때문에 cpu자원의 1/2만큼이 할당된다.

테스트를 해보자.

기본 옵션

기본 옵션일 경우 cpu-period와 cpu-quota는 0으로 설정된다. 모든 자원을 다 사용하겠다는 이야기. 기본 설정에서 cpu를 벤치마크했다.
# sysbench --test=cpu run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing CPU performance benchmark

Threads started!
Done.

Maximum prime number checked in CPU test: 10000


Test execution summary:
    total time:                          8.2348s
    total number of events:              10000
    total time taken by event execution: 8.2206
    per-request statistics:
         min:                                  0.17ms
         avg:                                  0.82ms
         max:                                  8.77ms
         approx.  95 percentile:               0.99ms

Threads fairness:
    events (avg/stddev):           10000.0000/0.00
    execution time (avg/stddev):   8.2206/0.00
10000개의 프라임넘버를 계산하는데 8초가 걸렸다.

컨테이너의 CPU 사용율
Tasks: 181 total,   1 running, 180 sleeping,   0 stopped,   0 zombie
%Cpu0  : 99.7 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1016804 total,   860208 used,   156596 free,    26824 buffers
KiB Swap:   520188 total,        0 used,   520188 free.   672072 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 8463 root      20   0   19688   1716   1428 S  99.8  0.2   0:04.41 sysbench

cpu-quota를 25000 으로

cpu-peroid를 그대로 두고 cpu-quota를 25000으로 조정했다. 100000ms에서 25000을 쿼터로 줬으니 1/4 만큼의 cpu 자원이 할당될 거다. 예상되로라면 대략 32초의 시간이 걸려야 할 테다.
# docker run --cpu-quota=25000 -it ubuntu /bin/bash

root@0db159fe62ac:/# sysbench --test=cpu run              
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing CPU performance benchmark

Threads started!
Done.

Maximum prime number checked in CPU test: 10000


Test execution summary:
    total time:                          32.6162s
    total number of events:              10000
    total time taken by event execution: 32.6012
    per-request statistics:
         min:                                  0.07ms
         avg:                                  3.26ms
         max:                                 80.82ms
         approx.  95 percentile:               1.43ms

Threads fairness:
    events (avg/stddev):           10000.0000/0.00
    execution time (avg/stddev):   32.6012/0.00
예상 대로 32초 정도의 시간이 걸렸다.

top - 15:29:48 up  2:44,  3 users,  load average: 0.06, 0.07, 0.05
Tasks: 181 total,   2 running, 179 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 25.0 us,  0.0 sy,  0.0 ni, 75.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1016804 total,   833772 used,   183032 free,    26996 buffers
KiB Swap:   520188 total,        0 used,   520188 free.   645976 cached Mem
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 8743 root      20   0   19688   1644   1356 S  24.9  0.2   0:02.58 sysbench
25% 정도의 cpu를 점유하는 걸 알 수 있다.

cpu-period를 50000으로

cpu-quota를 25000으로 고정하고 cpu-period를 50000으로 줄여보자. 50ms의 시간에 25000을 사용한다. 시간이 절반으로 줄었으니, 결국 기본 설정의 1/2 만큼의 cpu를 사용하게 된다.
# docker run --cpu-period=50000 --cpu-quota=25000 -it ubuntu /bin/bash

root@5e9abea2b64e:/# sysbench --test=cpu run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing CPU performance benchmark

Threads started!
1Done.

Maximum prime number checked in CPU test: 10000


Test execution summary:
    total time:                          16.7093s
    total number of events:              10000
    total time taken by event execution: 16.6949
    per-request statistics:
         min:                                  0.15ms
         avg:                                  1.67ms
         max:                                 30.91ms
         approx.  95 percentile:               1.13ms

Threads fairness:
    events (avg/stddev):           10000.0000/0.00
    execution time (avg/stddev):   16.6949/0.00
예상대로 나왔다.

아래는 top 화면
top - 15:43:27 up  2:57,  3 users,  load average: 0.00, 0.03, 0.05
Tasks: 176 total,   1 running, 175 sleeping,   0 stopped,   0 zombie
%Cpu0  : 51.1 us,  0.0 sy,  0.0 ni, 48.5 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  :  0.3 us,  0.3 sy,  0.0 ni, 99.0 id,  0.3 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1016804 total,   862832 used,   153972 free,    27872 buffers
KiB Swap:   520188 total,        0 used,   520188 free.   671752 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 8952 root      20   0   19688   1600   1316 S  50.1  0.2   0:03.93 sysbench
 7484 yundream  20   0   93612   3232   2324 S   0.3  0.3   0:00.36 sshd

cpuset

컨테이너가 사용할 cpu core 갯수를 할당 할 수 있다. 0,1 cpu를 할당해서 sysbench 테스트를 진행하기로 했다.
#  docker run --name cpuset2 --cpuset-cpus=0,1 -it ubuntu /bin/bash

root@38ec5bc41acf:/# sysbench --test=cpu run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing CPU performance benchmark

Threads started!
Done.

Maximum prime number checked in CPU test: 10000


Test execution summary:
    total time:                          8.4802s
    total number of events:              10000
    total time taken by event execution: 8.4663
    per-request statistics:
         min:                                  0.10ms
         avg:                                  0.85ms
         max:                                  4.32ms
         approx.  95 percentile:               1.03ms

Threads fairness:
    events (avg/stddev):           10000.0000/0.00
    execution time (avg/stddev):   8.4663/0.00
8초의 시간이 걸렸다.

top - 00:26:14 up 22 min,  2 users,  load average: 0.19, 0.09, 0.06
Tasks: 191 total,   2 running, 189 sleeping,   0 stopped,   0 zombie
%Cpu0  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1016804 total,   377752 used,   639052 free,    20360 buffers
KiB Swap:   520188 total,        0 used,   520188 free.   218052 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 1925 root      20   0   19688   1624   1336 S 100.1  0.2   0:04.44 sysbench
 1708 root      20   0   24988   3140   2636 R   0.3  0.3   0:00.65 top
    1 root      20   0   35376   5444   3660 S   0.0  0.5   0:00.99 systemd 
하나의 코어를 100% 사용한 걸 확인 할 수 있다.

이제 thread 2개로 테스트를 진행했다.
root@38ec5bc41acf:/# sysbench --test=cpu --num-threads=2 run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 2

Doing CPU performance benchmark

Threads started!
Done.

Maximum prime number checked in CPU test: 10000


Test execution summary:
    total time:                          4.2981s
    total number of events:              10000
    total time taken by event execution: 8.5803
    per-request statistics:
         min:                                  0.36ms
         avg:                                  0.86ms
         max:                                  5.00ms
         approx.  95 percentile:               1.09ms

Threads fairness:
    events (avg/stddev):           5000.0000/8.00
    execution time (avg/stddev):   4.2901/0.00
할당된 cpu 2개를 모두 소모한 결과 시간이 절반으로 줄었다. top을 보면 2개의 cpu를 모두 사용 한 걸 확인 할 수 있다.

Memory

물리 메모리와 스왑 메모리 제한

memory와 memory-swap를 이용해서, 물리메모리와 스왑메모리에 대한 제한을 걸 수 있다. 물리 메모리 200M, 스왑 메모리 300M를 가지는 컨테이너를 실행 했다.
# docker run -m=200m --memory-swap=500m -it ubuntu /bin/bash
WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.
커널 설정의 문제로 swap에 대한 제한을 지원하지 않는다는 경고문구가 뜬다. 지금 사용하는 리눅스 커널은 swap limit capabilities를 지원한다. grub 설정을 바꾸고 재시작하자.
# /etc/default/grub
...
...
GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"
# update-grub 
# reboot 

경고 메시지 없이 도커가 실행된다.
# docker run -m=200m --memory-swap=500m -it ubuntu /bin/bash
# root@3b8fe21aa4c4:/# 

free 명령을 이용해서 컨테이너의 메모리 상태를 확인해보자.
root@3b8fe21aa4c4:/# free
             total       used       free     shared    buffers     cached
Mem:       1016804     275248     741556       5120      14920     140872
-/+ buffers/cache:     119456     897348
Swap:       520188          0     520188
예상과 달리, 제한했던 메모리 정보가 아닌 전체 시스템의 메모리 정보가 출력된다. 실제 컨테이너에 할당된 메모리 정보는 cgroup을 확인해야 한다.
# cd /sys/fs/cgroup/memory/system.slice/docker-<dockerid>.scope
# cat memory.stat
cache 4325376
rss 487424
rss_huge 0
mapped_file 2809856
writeback 0
swap 0
pgpgin 3105
pgpgout 1930
pgfault 3212
pgmajfault 37
inactive_anon 4096
active_anon 512000
inactive_file 843776
active_file 3452928
unevictable 0
hierarchical_memory_limit 209715200
hierarchical_memsw_limit 524288000
...
메모리 제한이 적용된 걸 확인 할 수 있다.

결론

docker cli를 이용해서는 cpu/memory 쿼터 설정이 제한된다. 컨테이너를 만들 때 쿼터를 줄 수 있지만, start 할 때 값을 변경할 수 없다. 제대로 사용하려면 docker api를 이용한 별도의 애플리케이션을 만들어야 한다.
  1. 잘 된다.
  2. 도커 기반의 Paas를 만들 때, 써먹어야 겠다.
  3. cpu-period 와 cpu-quota 와의 관계가 애매모호하다. 더 살펴봐야 겠다.
 이미지