리소스 제약
리소스 제약 (Resource constraints)
기본적으로 컨테이너에는 리소스 제약이 없어서 호스트 커널 스케줄러가 허용하는 만큼 주어진 리소스를 사용할 수 있어요. Docker는 docker run 명령의 런타임 구성 플래그를 설정해 컨테이너가 사용할 수 있는 메모리나 CPU 양을 제어하는 방법을 제공해요. 이 섹션에서는 언제 그러한 제한을 설정해야 하는지와 설정할 때의 가능한 영향에 대한 세부 사항을 설명해요.
출처: 문서
본문
이러한 기능 중 상당수는 커널이 Linux capabilities를 지원해야 해요. 지원 여부를 확인하려면 docker info 명령을 사용할 수 있어요. 커널에서 capability가 비활성화되어 있으면 출력 끝에 다음과 같은 경고가 보일 수 있어요:
WARNING: No swap limit support
활성화하는 방법은 운영 체제의 문서를 참고하세요. 자세한 내용은 Docker Engine 트러블슈팅 가이드도 참고하세요.
메모리 (Memory)
메모리 부족의 위험 이해하기 (Understand the risks of running out of memory)
실행 중인 컨테이너가 호스트 머신의 메모리를 너무 많이 소비하도록 두지 않는 것이 중요해요. Linux 호스트에서 커널이 중요한 시스템 기능을 수행할 메모리가 충분하지 않다고 감지하면 OOME(Out Of Memory Exception)를 던지고 메모리를 확보하기 위해 프로세스를 죽이기 시작해요. 어떤 프로세스든 죽임의 대상이 될 수 있으며, Docker와 기타 중요한 애플리케이션도 포함돼요. 잘못된 프로세스가 죽으면 사실상 전체 시스템이 다운될 수 있어요.
Docker는 Docker 데몬의 OOM 우선순위를 조정해 시스템의 다른 프로세스보다 죽임을 당할 가능성이 낮아지도록 함으로써 이러한 위험을 완화하려 해요. 컨테이너의 OOM 우선순위는 조정되지 않아요. 이로 인해 개별 컨테이너가 Docker 데몬이나 다른 시스템 프로세스보다 죽임을 당할 가능성이 더 높아져요. 데몬이나 컨테이너에서 --oom-score-adj를 극단적인 음수로 수동 설정하거나, 컨테이너에서 --oom-kill-disable을 설정해 이러한 안전장치를 우회하려 해선 안 돼요.
Linux 커널의 OOM 관리에 대한 자세한 내용은 Out of Memory Management를 참고하세요.
다음 방법으로 OOME로 인한 시스템 불안정 위험을 완화할 수 있어요:
- 애플리케이션을 프로덕션에 배치하기 전에 테스트를 수행해 애플리케이션의 메모리 요구 사항을 파악해요.
- 애플리케이션이 충분한 리소스를 가진 호스트에서만 실행되도록 해요.
- 아래 설명대로 컨테이너가 사용할 수 있는 메모리 양을 제한해요.
- Docker 호스트에서 swap을 구성할 때 주의하세요. Swap은 메모리보다 느리지만 시스템 메모리 부족에 대한 버퍼를 제공할 수 있어요.
- 컨테이너를 서비스로 전환하고 서비스 수준 제약과 노드 레이블을 사용해 애플리케이션이 충분한 메모리를 가진 호스트에서만 실행되도록 하는 것을 고려해요.
컨테이너의 메모리 접근 제한 (Limit a container's access to memory)
Docker는 하드 또는 소프트 메모리 제한을 강제할 수 있어요.
- 하드 제한은 컨테이너가 고정된 양보다 많은 메모리를 사용하지 못하게 해요.
- 소프트 제한은 특정 조건(예: 커널이 호스트 머신의 낮은 메모리나 경합을 감지할 때)이 충족되지 않는 한 컨테이너가 필요한 만큼 메모리를 사용하게 해요.
이 옵션 중 일부는 단독으로 사용할 때와 여러 옵션을 설정할 때 효과가 다를 수 있어요.
대부분의 옵션은 양의 정수 뒤에 b, k, m, g 접미사를 붙여 바이트, 킬로바이트, 메가바이트, 기가바이트를 나타내요.
| Option | Description |
|---|---|
-m or --memory= |
컨테이너가 사용할 수 있는 최대 메모리 양. 이 옵션을 설정하면 허용되는 최소값은 6m(6 메가바이트)이에요. 즉 값을 최소 6 메가바이트로 설정해야 해요. |
--memory-swap* |
이 컨테이너가 디스크로 swap할 수 있는 메모리 양. --memory-swap 세부 사항 참고. |
--memory-swappiness |
기본적으로 호스트 커널은 컨테이너가 사용하는 익명 페이지(anonymous pages)의 일정 비율을 스왑아웃할 수 있어요. --memory-swappiness를 0과 100 사이의 값으로 설정해 이 비율을 조정할 수 있어요. --memory-swappiness 세부 사항 참고. |
--memory-reservation |
Docker가 호스트 머신에서 경합이나 낮은 메모리를 감지할 때 활성화되는 --memory보다 작은 소프트 제한을 지정할 수 있게 해줘요. --memory-reservation을 사용하면 우선권을 가지려면 --memory보다 낮게 설정해야 해요. 소프트 제한이므로 컨테이너가 제한을 초과하지 않음을 보장하지 않아요. |
--oom-kill-disable |
기본적으로 OOM(메모리 부족) 오류가 발생하면 커널이 컨테이너의 프로세스를 죽여요. 이 동작을 바꾸려면 --oom-kill-disable 옵션을 사용해요. -m/--memory 옵션도 설정한 컨테이너에서만 OOM killer를 비활성화하세요. -m 플래그가 설정되지 않으면 호스트에서 메모리가 부족해져 커널이 호스트 시스템의 프로세스를 죽여 메모리를 확보해야 할 수도 있어요. |
cgroups와 메모리에 대한 일반적인 자세한 내용은 Memory Resource Controller 문서를 참고하세요.
--memory-swap 세부 사항
--memory-swap은 --memory도 설정된 경우에만 의미를 갖는 수정(modifier) 플래그예요. swap을 사용하면 컨테이너가 사용 가능한 모든 RAM을 소진했을 때 초과 메모리 요구를 디스크에 쓸 수 있어요. 메모리를 디스크로 자주 swap하는 애플리케이션에는 성능 저하가 있어요.
그 설정은 복잡한 효과를 가질 수 있어요:
--memory-swap이 양의 정수로 설정되면--memory와--memory-swap둘 다 설정해야 해요.--memory-swap은 사용할 수 있는 메모리와 swap의 총량을 나타내고,--memory는 비-swap 메모리가 사용하는 양을 제어해요. 그래서--memory="300m"와--memory-swap="1g"면 컨테이너는 300m의 메모리와 700m(1g - 300m)의 swap을 사용할 수 있어요.--memory-swap이0으로 설정되면 그 설정은 무시되고 값이 설정되지 않은 것으로 취급돼요.--memory-swap이--memory와 같은 값으로 설정되고--memory가 양의 정수로 설정되면 컨테이너는 swap에 접근할 수 없어요. Prevent a container from using swap 참고.--memory-swap이 설정되지 않고--memory가 설정되면 호스트에 swap 메모리가 구성되어 있다면 컨테이너는--memory설정만큼의 swap을 사용할 수 있어요. 예를 들어--memory="300m"이고--memory-swap이 설정되지 않으면 컨테이너는 총 600m의 메모리와 swap을 사용할 수 있어요.--memory-swap이 명시적으로-1로 설정되면 컨테이너는 호스트 시스템에서 사용 가능한 양까지 무제한 swap을 사용할 수 있어요.- 컨테이너 안에서
free같은 도구는 호스트의 사용 가능한 swap을 보고하지, 컨테이너 안에서 사용 가능한 것을 보고하지 않아요. swap이 있는지 판단하려고free나 유사한 도구의 출력에 의존하지 마세요.
컨테이너가 swap을 사용하지 못하게 하기 (Prevent a container from using swap)
--memory와 --memory-swap이 같은 값으로 설정되면 컨테이너가 어떤 swap도 사용하지 못하게 돼요. 이는 --memory-swap이 사용할 수 있는 결합된 메모리+swap 양인 반면 --memory는 사용할 수 있는 물리 메모리 양이기 때문이에요.
--memory-swappiness 세부 사항
- 값이 0이면 익명 페이지 스와핑을 끄요.
- 값이 100이면 모든 익명 페이지를 swap 가능하게 설정해요.
- 기본적으로
--memory-swappiness를 설정하지 않으면 값은 호스트 머신에서 상속돼요.
CPU
기본적으로 각 컨테이너의 호스트 머신 CPU 사이클 접근은 무제한이에요. 여러 제약을 설정해 주어진 컨테이너의 호스트 머신 CPU 사이클 접근을 제한할 수 있어요. 대부분의 사용자는 기본 CFS 스케줄러를 사용·구성해요. 실시간 스케줄러도 구성할 수 있어요.
기본 CFS 스케줄러 구성 (Configure the default CFS scheduler)
CFS는 일반 Linux 프로세스를 위한 Linux 커널 CPU 스케줄러예요. 여러 런타임 플래그로 컨테이너의 CPU 리소스 접근량을 구성할 수 있어요. 이 설정들을 사용하면 Docker가 호스트 머신에서 컨테이너의 cgroup 설정을 수정해요.
| Option | Description |
|---|---|
--cpus=<value> |
컨테이너가 사용할 수 있는 사용 가능한 CPU 리소스의 양을 지정해요. 예를 들어 호스트 머신에 CPU가 2개 있고 --cpus="1.5"로 설정하면 컨테이너는 최대 1.5개의 CPU를 보장받아요. 이는 --cpu-period="100000"과 --cpu-quota="150000"을 설정하는 것과 동일해요. |
--cpu-period=<value> |
--cpu-quota와 함께 사용되는 CPU CFS 스케줄러 주기(period)를 지정해요. 기본값은 100000 마이크로초(100 밀리초)예요. 대부분의 사용자는 이 기본값을 바꾸지 않아요. 대부분의 사용 사례에서 --cpus가 더 편리한 대안이에요. |
--cpu-quota=<value> |
컨테이너에 CPU CFS 할당량을 부과해요. 스로틀되기 전에 --cpu-period당 컨테이너가 제한되는 마이크로초 수. 따라서 효과적인 상한선으로 작용해요. 대부분의 사용 사례에서 --cpus가 더 편리한 대안이에요. |
--cpuset-cpus |
컨테이너가 사용할 수 있는 특정 CPU 또는 코어를 제한해요. CPU가 둘 이상일 때 컨테이너가 사용할 수 있는 CPU의 쉼표 구분 목록 또는 하이픈 구분 범위. 첫 번째 CPU는 0으로 번호가 매겨져요. 유효한 값은 0-3(첫·둘·셋·넷째 CPU 사용) 또는 1,3(둘째·넷째 CPU 사용)일 수 있어요. |
--cpu-shares |
기본값 1024보다 크거나 작은 값으로 이 플래그를 설정해 컨테이너의 가중치를 늘리거나 줄이고 호스트 머신 CPU 사이클의 더 크거나 작은 비율에 접근하게 해요. 이는 CPU 사이클이 제약될 때만 강제돼요. CPU 사이클이 충분할 때는 모든 컨테이너가 필요한 만큼 CPU를 사용해요. 그런 의미에서 이것은 소프트 제한이에요. --cpu-shares는 Swarm 모드에서 컨테이너가 스케줄링되는 것을 막지 않아요. 사용 가능한 CPU 사이클에 대해 컨테이너 CPU 리소스의 우선순위를 정해요. 특정 CPU 접근을 보장하거나 예약하지는 않아요. |
CPU가 1개라면 다음 각 명령은 컨테이너가 매초 CPU의 최대 50%를 보장해요.
$ docker run -it --cpus=".5" ubuntu /bin/bash
이것은 --cpu-period와 --cpu-quota를 수동으로 지정하는 것과 동등해요:
$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash
실시간 스케줄러 구성 (Configure the real-time scheduler)
CFS 스케줄러를 사용할 수 없는 작업을 위해 컨테이너가 실시간 스케줄러를 사용하도록 구성할 수 있어요. Docker 데몬을 구성하거나 개별 컨테이너를 구성하기 전에 호스트 머신의 커널이 올바르게 구성되어 있는지 확인해야 해요.
Warning CPU 스케줄링과 우선순위화는 고급 커널 수준 기능이에요. 대부분의 사용자는 이 값들을 기본값에서 바꿀 필요가 없어요. 이 값들을 잘못 설정하면 호스트 시스템이 불안정해지거나 사용할 수 없게 될 수 있어요.
호스트 머신의 커널 구성 (Configure the host machine's kernel)
zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED를 실행하거나 /sys/fs/cgroup/cpu.rt_runtime_us 파일의 존재를 확인해 Linux 커널에서 CONFIG_RT_GROUP_SCHED가 활성화되어 있는지 확인해요. 커널 실시간 스케줄러 구성에 대한 지침은 운영 체제의 문서를 참고하세요.
Docker 데몬 구성 (Configure the Docker daemon)
실시간 스케줄러를 사용하는 컨테이너를 실행하려면 --cpu-rt-runtime 플래그를 런타임 주기당 실시간 작업에 예약된 최대 마이크로초 수로 설정해 Docker 데몬을 실행해요. 예를 들어 기본 주기 1000000 마이크로초(1초)에서 --cpu-rt-runtime=950000으로 설정하면 실시간 스케줄러를 사용하는 컨테이너가 1000000 마이크로초 주기마다 950000 마이크로초 실행되도록 보장하며, 비실시간 작업에 최소 50000 마이크로초를 남겨요. systemd를 사용하는 시스템에서 이 구성을 영구적으로 만들려면 docker 서비스용 systemd unit 파일을 만들어요. 예를 들어 systemd unit 파일로 데몬이 프록시를 사용하도록 구성하는 방법에 대한 지시를 참고하세요.
개별 컨테이너 구성 (Configure individual containers)
docker run으로 컨테이너를 시작할 때 컨테이너의 CPU 우선순위를 제어하기 위해 여러 플래그를 전달할 수 있어요. 적절한 값에 대한 정보는 운영 체제의 문서 또는 ulimit 명령을 참고하세요.
| Option | Description |
|---|---|
--cap-add=sys_nice |
컨테이너에 CAP_SYS_NICE capability를 부여해, 프로세스 nice 값을 높이고, 실시간 스케줄링 정책을 설정하고, CPU affinity를 설정하는 등의 작업을 허용해요. |
--cpu-rt-runtime=<value> |
Docker 데몬의 실시간 스케줄러 주기 내에서 컨테이너가 실시간 우선순위로 실행할 수 있는 최대 마이크로초 수. --cap-add=sys_nice 플래그도 필요해요. |
--ulimit rtprio=<value> |
컨테이너에 허용되는 최대 실시간 우선순위. --cap-add=sys_nice 플래그도 필요해요. |
다음 예시 명령은 debian:jessie 컨테이너에 이 세 플래그를 모두 설정해요.
$ docker run -it \
--cpu-rt-runtime=950000 \
--ulimit rtprio=99 \
--cap-add=sys_nice \
debian:jessie
커널이나 Docker 데몬이 올바르게 구성되지 않으면 오류가 발생해요.
GPU
컨테이너에서 NVIDIA GPU에 접근하는 방법에 대한 정보는 GPU 접근을 참고하세요.