지난달 저는 ApacheCon에서 발표를 진행했습니다. 주제는 Cgroups에 관한 것이었습니다. 아주 적은 수의 Linux 사용자들만이(얼마 전까지의 저를 포함해서) Cgroups와 그 강력한 기능에 대해 잘 알고 있는 것 같습니다. 이는 참 아쉬운 일인데, 왜냐하면 Cgroups는 매우 강력하며, Linux 툴킷에서 제공하는 그 어떤 도구보다 훨씬 더 세부적인 방식으로 서버의 리소스를 할당할 수 있게 해주기 때문입니다. 게다가 커널에 직접 내장되어 있으며 대부분의 Linux 배포판에서 기본으로 제공됩니다.
이 글에서는 텍사스 오스틴에서 열린 Docker 미트업에서 했던 또 다른 발표의 후속 격으로 Docker와 함께 Cgroups를 사용하는 방법을 다루겠습니다.
이 글을 읽으려면 Cgroups에 대한 기본적인 이해가 필요합니다. Cgroups를 처음 접하신다면, 제 슬라이드 자료 (ApacheCon 발표 자료)와 마지막에 있는 참고 자료들을 살펴보시는 것을 추천합니다. 하지만 너무 걱정하지 마세요, 너무 깊이 들어가지는 않을 테니까요. 슬라이드 자료를 가볍게 훑어보기만 해도 기본적인 개념은 파악하실 수 있을 것입니다.
Docker와 Cgroups
Docker는 LXC와 libcontainer라는 두 가지 서로 다른 드라이버를 제공합니다. LXC 드라이버는 레거시 드라이버이고, libcontainer는 새로운 기본 드라이버입니다. 대부분의 경우 libcontainer가 선호되는 드라이버인데, 혁신이 일어나는 곳이 바로 여기이기 때문입니다 (예를 들어 docker exec은 LXC 드라이버에서 작동하지 않습니다).
하지만 두 가지 드라이버가 있다는 점을 유의하는 것이 중요합니다. 모든 Cgroup 기능이 아직 libcontainer로 포팅되지 않았거나(또는 적어도 Docker에 노출되지 않았기) 때문입니다. LXC 드라이버를 사용할 때는 단순히 LXC 인수를 직접 전달하는 반면, libcontainer를 사용할 때는 Docker에 노출된 명시적인 Cgroup 정책 인수가 있습니다. Docker 데몬을 실행할 때 드라이버를 명시적으로 설정해야 하므로 두 드라이버를 동시에 실행할 수는 없습니다.
확인하는 방법의 예는 다음과 같습니다:
[bash light=”true”] # LXC 드라이버 사용 시$ docker run -d –name=’lxc_test’ \
–lxc-conf="lxc.cgroup.cpu.shares=50" \
busybox
# libcontainer 드라이버 사용 시
$ docker run -d –name=’libcontainer_test’ \
–cpu-shares=50 \
busybox
[/bash]
다음은 몇 가지 Cgroup 기능과 두 드라이버 간의 매핑 방식에 대한 개요입니다:
| 기능 | Libcontainer | LXC |
|---|---|---|
| 상대적 CPU 점유율 | -c, –cpu-shares | –lxc-conf=”lxc.cgroup.cpu.shares” |
| CPU 코어에 고정 | –cpuset-cpus | –lxc-conf=”lxc.cgroup.cpuset.cpus” |
| 메모리 제한 | -m, –memory | –lxc-conf=”lxc.cgroup.cpuset.mems” |
LXC
Docker에서 LXC 드라이버를 사용하려면 먼저 이를 활성화해야 합니다. 활성화 방법은 Linux 배포판에 따라 다르지만, 다음은 Ubuntu 14.04에서 LXC 드라이버를 활성화하는 방법.
에 대한 안내입니다. 위에서 언급했듯이, 이렇게 하면 여러 기능을 포기하게 됩니다. 따라서 libcontainer를 사용하는 Docker에서 아직 노출되지 않은 기능이 꼭 필요한 경우가 아니라면 기본 드라이버를 그대로 사용하는 것이 좋습니다.
아직 libcontainer에 노출되지 않은 유용한 Cgroup 정책으로는 특정 애플리케이션에 매우 유용할 수 있는 I/O 스로틀링(발표 자료에서 다룸) 등이 있습니다.
LXC 드라이버를 사용하기로 결정했다면 인수를 추가하는 방법은 간단합니다. 단지 --lxc-conf 인수를 추가하고 설정하려는 Cgroup 정책을 전달하기만 하면 됩니다.
Libcontainer
위의 표에서 볼 수 있듯이, 기본적인 Cgroup 정책은 현재 버전의 Docker(이 글을 쓰는 시점 기준 1.6)에 이미 노출되어 있습니다.
이러한 정책을 사용하는 것은 매우 간단합니다. 예를 들어 Docker 컨테이너를 첫 번째 CPU 코어에 고정하려면 --cpuset-cpus=0을 docker run 명령어에 추가하면 됩니다.
또한 libcontainer와 함께 --cgroup-parent 인수를 사용하여 더 세부적인 리소스 제한을 수동으로 설정할 수도 있습니다. 그런 다음 해당 인수를 사용하여 해당 그룹에 매핑하게 됩니다.
데모: Docker와 Cgroups
아래 스크린캐스트에서는 두 개의 Docker 컨테이너(‘low_prio’ 및 ‘high_prio’)를 사용합니다. 우리는 ‘busybox’ 기본 컨테이너를 사용하고 md5sum /dev/urandom을 실행하여 CPU를 많이 소모하는 프로세스를 시뮬레이션합니다. 기본적으로 이는 사용 가능한 모든 CPU 리소스를 소모합니다. 하지만 리소스를 관리하기 위해 두 가지 Cgroup 정책을 적용할 것입니다. 먼저 ‘cpuset.cpus’를 사용하여 컨테이너를 동일한 CPU 코어(코어 0)로 고정합니다.
다음으로 ‘cpu.shares’를 사용하여 상대적인 CPU 점유율을 할당합니다. ‘low_prio’ 컨테이너에는 20의 값을, ‘high_prio’에는 80의 값을 부여합니다. 이는 CPU의 20%가 ‘low_prio’ 컨테이너에 할당되고, CPU의 80%가 ‘high_prio’ 컨테이너에 할당됨을 의미합니다. 하지만 상대적 점유율은 임의의 척도라는 점에 유의하세요(값으로 2와 8을 사용할 수도 있었습니다).
리소스 관리가 실제로 작동하는지 확인한 후, Cgroup 정책 없이 동일한 컨테이너 세트를 실행하여 어떻게 작동하는지 살펴봅니다.
참고로, 컨테이너를 실행하는 데 사용된 명령은 다음과 같습니다.
[bash light=”true”] $ docker run -d \–name=’low_prio’ \
–cpuset-cpus=0 \
–cpu-shares=20 \
busybox md5sum /dev/urandom
$ docker run -d \
–name=’high_prio’ \
–cpuset-cpus=0 \
–cpu-shares=80 \
busybox md5sum /dev/urandom
[/bash]
요약
동일한 호스트에서 여러 Docker 컨테이너를 관리하는 경우, Cgroups를 사용하여 컨테이너 간의 리소스를 관리하는 것은 매우 합리적입니다. 예를 들어, 한 컨테이너에서는 백그라운드 처리 작업이 실행되고 있고 다른 컨테이너에서는 사용자 콘텐츠를 제공하고 있을 수 있습니다. 이 경우, 새로 습득한 지식을 활용하여 백그라운드 작업보다 사용자 대면 컨테이너에 우선순위를 부여할 수 있습니다.
댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.