목차
컨테이너에 대한 CPU, MEM, Disk에 대한 자원 관리에 대해 공부해보겠다.
CPU 리소스 제한
CPU자원을 어떤 프로세스에 얼마나 할당하는지 정하는 것은 컨테이너를 관리하는데 매우 중요한 부분을 차지한다. MEM, Disk도 마찬가지이다. CPU 사용량 제한을 위해 CFS 스케줄러를 사용한다. 말 그대로 모든 프로세스가 공평하게 CPU사용 시간을 제공 받오록 하는 OS알고리즘이라 한다. 컨테이너 CPU리소스를 제한하기 위한 몇 가지 옵션이 있다.
--cpu-shares
이 옵션은 컨테이너 간 CPU 사용 우선순위를 조절하는 데 사용된다. 기본값은 1024이며, 이 값은 상대적이다.
docker run -d --name cpu_1024 --cpu-shares 1024 leecloudo/stress:1.0 stress --cpu 4
docker run -d --name cpu_512 --cpu-shares 512 leecloudo/stress:1.0 stress --cpu 4
- --cpu-shares 옵션은 컨테이너 간의 CPU 우선순위를 상대적으로 조절한다. cpu_1024와 cpu_512는 각각 1024와 512의 CPU 공유값을 가진다. 이를 통해 CPU가 바쁠 때는 CPU 공유값이 더 큰 cpu_1024가 더 많은 CPU 자원을 사용할 수 있게 된다.
- 결국, 이 설정은 여러 컨테이너가 있을 때 하나의 컨테이너가 CPU를 독점하지 않도록 CPU 자원을 효율적으로 배분하려는 것이 목적이다.
다음과 같이 ps 명령어를 통해 출력된 결과에서 CPU 사용 이 상황에서 cpu_1024와 cpu_512라는 두 컨테이너가 실행되고 있고, 각 컨테이너는 각각 1024와 512의 CPU 공유값을 갖고 있다. 여기서 시간이 차이 나는 이유는 앞서 말한것 과 같이 컨테이너 간의 CPU 리소스 배분과 관련이 있다.
ps -auxf | grep stress
root 29863 0.4 0.0 7480 952 ? Ss 13:24 0:00 | \_ stress --cpu 4
root 29903 70.4 0.0 7480 96 ? R 13:24 0:38 | \_ stress --cpu 4
root 29904 70.0 0.0 7480 96 ? R 13:24 0:38 | \_ stress --cpu 4
root 29905 70.4 0.0 7480 96 ? R 13:24 0:38 | \_ stress --cpu 4
root 29906 70.7 0.0 7480 96 ? R 13:24 0:38 | \_ stress --cpu 4
root 29967 0.5 0.0 7480 816 ? Ss 13:24 0:00 \_ stress --cpu 4
root 30002 33.2 0.0 7480 92 ? R 13:24 0:16 \_ stress --cpu 4
root 30003 33.2 0.0 7480 92 ? R 13:24 0:16 \_ stress --cpu 4
root 30004 33.3 0.0 7480 92 ? R 13:24 0:16 \_ stress --cpu 4
root 30005 32.9 0.0 7480 92 ? R 13:24 0:16 \_ stress --cpu 4
여기서 두 가지 컨테이너가 실행되고 있는 상황이다.
첫 번째 컨테이너 (cpu_1024): 70% 정도의 CPU 사용률 (stress --cpu 4 명령어가 4개의 CPU 스레드를 사용 중).
두 번째 컨테이너 (cpu_512): 약 33%의 CPU 사용률.
CPU 시간 차이의 이유
--cpu-shares 값이 높을수록 해당 컨테이너가 CPU 경쟁 상황에서 더 많은 CPU 시간을 할당받는다. 여기서 cpu_1024는 cpu_512보다 2배 많은 CPU 자원을 사용하므로, cpu_1024는 cpu_512보다 2배 더 많은 CPU 시간을 가지게 되는 것이다.
--cpuset-cpus
역할: Host가 보유한 CPU 수에서 몇 번째를 사용하도록 할 것인지 지정
docker run -d --name cpuset_1 --cpuset-cpus=2 leecloudo/stress:1.0 stress --cpu 1
이 컨테이너는 CPU 코어 2번에서 하나의 CPU 스레드를 사용하여 부하를 발생시키게 된다.
docker run -d --name cpuset_2 --cpuset-cpus=0,3 leecloudo/stress:1.0 stress --cpu 2
이 경우, 두 개의 CPU 스레드가 각각 CPU 코어 0번과 3번에서 실행되어 부하를 준다.
이 설정이 의미하는 것
첫 번째 컨테이너 (cpuset_1): CPU 코어 2번에서만 실행되므로, 이 컨테이너는 하나의 CPU 코어(2번)에서만 부하를 발생
두 번째 컨테이너 (cpuset_2): 이 컨테이너는 두 개의 CPU 코어(0번과 3번)에서 부하를 발생시킨다. 각 스레드가 서로 다른 CPU 코어(0번과 3번)에서 실행되기 때문에, 두 개의 CPU 자원을 효율적으로 사용할 수 있다.
-cpus
cpu사용량 제어할때 사용
docker update --cpus=0.2 cpuset_1
- 기존에 실행 중인 컨테이너의 자원을 조정하고자 한다면 docker update 이용
- 특정 container의 CPU 사용량을 20%로 제한하고자 하면 다음과 같이 -cpus 옵션을 주면 된다.
하지만 CPU가 과부하인 상태에서 cpuset_1 컨테이너의 CPU 사용량을 0.2로 제한하면, 이 컨테이너는 더 적은 CPU 시간을 사용하게 되고, 작업 처리 속도는 크게 감소할 것이다. 그래서 매우 비효율적이다. 처음 부터 자원관리를 잘하는 것이 중요하다.
스레드란?
스레드가 어떤것인지 알지만 뭔가 설명하기 어려워서 한번 정리해본다.
프로세스: 실행 중인 프로그램을 말한다. 하나의 프로세스는 하나 이상의 스레드를 가질 수 있다.
스레드: 프로세스 내부에서 실제로 작업을 수행하는 실행 단위이다. 여러 스레드가 하나의 프로세스에서 동시에 실행될 수 있으며, 이 경우 멀티스레딩이라고 부른다.
Memory 리소스 제한
- 메모리는 프로세스들의 작업 공간으로, Docker 컨테이너와 호스트 운영체제(OS)가 공유하는 자원이다.
- 메모리 최적화를 위해, Docker Host OS의 총 메모리 양과 각 컨테이너의 예상 메모리 사용량을 미리 파악해야 한다.
- 과도한 메모리 사용이 발생하면 메모리 부족(OOM, Out of Memory) 상태가 되어, 특정 프로세스(컨테이너)가 강제 종료될 수 있다.
- 만약 컨테이너들이 과도하게 메모리를 사용하면 Docker 데몬이 커널에 의해 종료될 위험이 있으며, 이는 전체 컨테이너 서비스에 영향을 미칠 수 있다.
--memory (-m)
- 컨테이너가 사용할 수 있는 최대 메모리 양을 제한하는 옵션이다.
- 컨테이너가 이 값을 초과해서 메모리를 사용할 수 없게 된다. 컨테이너가 이 한도를 넘으면, Out of Memory (OOM) 상태가 발생해 해당 컨테이너가 종료될 수 있다.
- EX: docker run -m 512m → 이 명령은 컨테이너가 최대 512MB 메모리만 사용하도록 제한한다.
--memory-reservation
- 소프트 제한으로, 컨테이너가 사용하는 최소 메모리 양을 설정하는 옵션아다. 컨테이너는 이 값보다 적게 메모리를 사용할 수 있지만, 메모리가 부족해질 때 우선순위에 따라 이 값 이상을 확보하려고 한다.
- 컨테이너가 설정된 메모리 양 이상을 사용하려고 할 때, 다른 프로세스보다 낮은 우선순위로 취급되어 메모리 사용량을 줄이도록 유도된다.
- EX: docker run --memory-reservation 300m → 300MB 메모리 사용을 최소로 보장하지만, 필요에 따라 더 사용할 수 있음.
--memory-swap
- 컨테이너가 사용할 수 있는 메모리와 스왑 메모리의 총량을 설정한다. 스왑 메모리는 물리적 메모리가 부족할 때 디스크에 저장되는 임시 메모리 공간을 의미한다.
- 스왑을 사용할지 여부를 결정하고, 메모리 부족 상황에서 디스크 스왑을 통해 추가 메모리를 확보할 수 있다. 기본적으로는 스왑 메모리 = 메모리 양의 2배가 된다.
- EX: docker run --memory 512m --memory-swap 1g → 컨테이너는 512MB 메모리를 사용하고, 추가로 512MB의 스왑 메모리를 사용할 수 있어 총 1GB의 메모리를 확보한다.
Disk 리소스 제한
- Docker image는 기본적으로 Docker Host의 공간을 사용하므로 지속적인 사용량 관찰이 요구된다.
- 컨테이너 I/O 제한을 설정하지 않으면 컨테이너 내부 I/O bandwidth(대역폭)에 제한이 설정되지 않기 때문에 옵션을 통해 Block I/O 제한이 필요하다.
- 단 Direct I/O의 경우에만 Block I/O가 제한되며, Buffered I/O는 해당되지 않는다.
번외
Block I/O
- 데이터를 블록 단위로 읽고 쓰는 방식이다. 블록은 일반적으로 디스크의 기본 데이터 단위로, 크기가 일정하다.
- 데이터는 디스크에서 블록 단위로 읽고 쓰기 때문에, 대량의 데이터를 빠르게 처리할 수 있다.
- 예시: 디스크에서 파일을 읽거나 쓰는 작업.
Buffered I/O
- 데이터가 메모리의 버퍼를 통해 읽고 쓰는 방식이다. 데이터를 버퍼에 저장한 후, 버퍼가 가득 차거나 특정 조건이 충족되면 실제 디스크에 기록한다.
- 데이터의 입력과 출력을 메모리 버퍼를 통해 일괄 처리함으로써, 디스크 접근을 줄여서 성능을 향상시킬 수 있다.
- 예시: 텍스트 파일을 읽거나 쓸 때, 메모리 버퍼를 사용하여 데이터 처리.
Direct I/O와 Buffered I/O의 차이
- Direct I/O: 데이터를 디스크와 직접적으로 읽고 쓰는 방식으로, 메모리 버퍼를 사용하지 않는다. 이로 인해 메모리 사용이 줄어들지만, 디스크 접근이 더 빈번해질 수 있다.
- Buffered I/O: 메모리 버퍼를 통해 데이터를 처리하므로, 디스크 접근이 줄어들고 성능이 개선될 수 있다.
Block I/O와 IOPS 제한
기본 테스트
docker run -it --rm ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB의 데이터를 0.010초 만에 1.0 GB/s 속도로 기록
10485760 bytes (10 MB) copied, 0.0102204 s, 1.0 GB/s
쓰기 IOPS 10 제한
docker run -it --rm --device-write-iops /dev/sdb:10 ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB를 1.906초 만에 5.5 MB/s 속도로 기록
10485760 bytes (10 MB) copied, 1.90632 s, 5.5 MB/s
쓰기 IOPS 1 제한
docker run -it --rm --device-write-iops /dev/sdb:1 ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB를 37.009초 만에 283 kB/s 속도로 기록
10485760 bytes (10 MB) copied, 37.0094 s, 283 kB/s
IOPS (Input/Output Operations Per Second): 초당 디스크에서 수행할 수 있는 입출력 작업 수를 의미 IOPS가 높을수록 디스크가 더 많은 작업을 처리할 수 있다.
--device-write-iops 옵션: 이 옵션으로 디스크에 대한 초당 I/O 작업 수를 제한하여, 컨테이너의 디스크 I/O 성능을 제어할 수 있다.
Block I/O와 MBPS 제한 이해하기
기본 테스트
docker run -it --rm ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB의 데이터를 0.010초 만에 1.0 GB/s 속도로 기록
10485760 bytes (10 MB) copied, 0.0128923 s, 813 MB/s
쓰기 1 MB/s 제한
docker run -it --rm --device-write-bps /dev/sdb:1mb ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB를 10.0274초 만에 1.0 MB/s 속도로 기록
10485760 bytes (10 MB) copied, 10.0274 s, 1.0 MB/s
쓰기 10 MB/s 제한
docker run -it --rm --device-write-bps /dev/sdb:10mb ubuntu:14.04 bash
root@container:/# dd if=/dev/zero of=blkio.out bs=1M count=10 oflag=direct
결과: 10MB를 1.01432초 만에 10.3 MB/s 속도로 기록
10485760 bytes (10 MB) copied, 1.01432 s, 10.3 MB/s
MBPS (Mega Bytes Per Second): 초당 디스크에서 전송할 수 있는 바이트 수를 의미한다. BPS가 낮을수록 디스크 쓰기 속도가 제한된다.
--device-write-bps 옵션: 이 옵션으로 디스크의 초당 바이트 전송 속도를 제한하여 컨테이너의 디스크 I/O 성능을 제어할 수 있다.
Docker Volume
Docker Volume은 컨테이너와 호스트 시스템 간의 데이터를 공유하고 영구적으로 저장하기 위해 사용되는 Docker의 저장소이다. 컨테이너는 기본적으로 데이터를 자체 파일시스템에 저장하지만, 컨테이너가 삭제되면 그 데이터도 사라지기 때문에 볼륨은 이를 방지하고, 여러 컨테이너 간에 데이터를 공유하거나 컨테이너가 삭제되더라도 데이터를 유지할 수 있게 해준다.
볼륨을 사용하는 주된 이유
- 데이터 지속성: 컨테이너를 중단하거나 삭제하더라도 데이터는 사라지지 않는다
- 여러 컨테이너 간 데이터 공유: 동일한 볼륨을 여러 컨테이너에서 동시에 사용할 수 있다.
- 호스트와 데이터 공유: 호스트 파일시스템과 컨테이너 간의 파일을 쉽게 주고받을 수 있다.
볼륨은 docker volume 명령어로 생성, 삭제 및 관리할 수 있고, 컨테이너를 실행할 때 -v 또는 --mount 옵션을 사용해 연결한다.
docker run -d --name my_container -v my_volume:/app/data my_image
이 명령어는 my_volume이라는 볼륨을 /app/data에 마운트하고, 해당 경로에 데이터를 저장한다.
volume 방식
Docker에서 데이터를 영구적으로 저장하는 방식은 Volume, Bind Mount, tmpfs 이렇게 3가지로 나눌 수 있다.

Volume
- Docker 볼륨은 docker volume create 명령으로 생성할 수 있으며, Docker CLI나 API를 통해 사용할 수 있다.
- 볼륨은 기본적으로 Docker의 루트 디렉토리(/var/lib/docker)에 저장되며, 컨테이너 내부 경로와 연결(mount)되어 공유 가능하다.
- 새로운 볼륨에 데이터를 미리 채워서 컨테이너에 연결하면 즉시 데이터를 사용할 수 있다.
- Docker는 /var/lib/docker 경로를 통해 볼륨 데이터를 직접 관리한다.
docker volume create my-volume
docker run .. -v my-volume:/app ..
docker run .. --mount source=my-volume,target=/app .
예시
Mysql에 Docker volume 연결
docker volume create mydb-data
docker run -d --name mydb \ > -e MYSQL_ROOT_PASSWORD=password1 \
> -e MYSQL_DATABASE=kimdongju \
> -v mydb-data:/var/lib/mysql \
> mysql:5.7-debian
MySQL 컨테이너를 설정하고, 데이터베이스 데이터를 Docker 볼륨을 통해 영구적으로 저장하는 설정이다. 데이터를 영구적으로 보존하면서, 컨테이너를 재시작하거나 삭제하더라도 데이터가 사라지지 않도록 볼륨을 사용할 수 있다.
볼륨 생성 없이 사용하기
볼륨을 create 명령어로 굳이 만들 필요 없이 바로 run 명령을 통해 만들 수 있다.
hostos1:~$ docker run -d --name mydb \
> -e MYSQL_ROOT_PASSWORD=password1 -e MYSQL_DATABASE=kimdongju \ > -v /var/lib/mysql \
> mysql:5.7-debian
이렇게 하면 Host의 /var/lib/docker/volume경로에 랜덤값으로 자동 볼륨이 생성된다.
data container 이용하기

여러컨테이너가 데이터용 컨테이너를 통해 볼륨을 공유할 수 있다. "--volumes-from 공유 컨테이너명" 옵션을 추가해주면 된다.
~$ docker create -v $(pwd)/share-volume:/share-data --name=share-container ubuntu:14.04
~$ docker run -it --volumes-from share-container --name=data-1 ubuntu:14.04 bash
이렇게 볼륨을 설정하고 컨테이너에서 볼륨을 추가해도 이 볼륨을 공유하는 다른 컨테이너에서도 변경사항을 같이 공유할 수 있다. 이것도 마찬가지로 ro,rw 옵션을 지정할 수 있다.
Nginx log 관리
Nginx 컨테이너의 접근 및 에러 로그 영역을 볼륨으로 구성하여 실시간 접근 기록 확인 및 장애 시 에러에 대한 정보를 컨테이너가 중지되어도 확인 가능하도록 한다.
docker run -d --name=myweb -v /home/kevin/nginx-log:/var/log/nginx - p 8011:80 nginx:1.25.0-alpine
이미지 버전 에러 해결 과정
docker exec -it mydb bash
root@2d140a8879fe:/# mysql -uroot -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
# /var/lib/mysql/mysql.sock 라는 원본파일에 /tmp/mysql.sock 파일에 Symbolic Link 연결
root@2d140a8879fe:/# ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock
MySQL 이미지 버전을 변경하면 소켓 파일 경로가 달라질 수 있다, 그래서 클라이언트가 서버와 연결하지 못하는 문제가 발생한다. 이를 해결하려면, 심볼릭 링크(ln -s)를 사용해 소켓 파일의 경로를 올바르게 연결해주면 된다. 예를 들어, /var/lib/mysql/mysql.sock을 /tmp/mysql.sock으로 연결하면, 클라이언트가 정상적으로 MySQL 서버에 접속할 수 있다.
Bind Mount
- 호스트의 특정 디렉토리나 파일을 컨테이너에 직접 연결해서 사용하는 방식이다. 호스트 파일시스템의 경로를 명시적으로 지정할 수 있다.
- 호스트의 특정 파일이나 디렉토리와 직접 연결되므로 개발 중 코드 디렉토리나 설정 파일을 실시간으로 수정하고 반영할 수 있다.
- Docker가 아닌 사용자(또는 외부 애플리케이션)가 관리하는 파일/디렉토리를 사용할 수 있다.
docker run -d --name my_container -v /host/path:/container/path my_image
예시
다음과 같이 읽기, 쓰기 권한을 줄 수 있다.
docker run -it --name=bind-mount \ > -v /home/kdj/fastcampus/ch08/bind01:/bind01:ro \
> -v $(pwd)/bind02:/bind02:rw \
> ubuntu:14.04 bash
만약 읽기 권한만 주었다면 실제 컨테이너 안에서 Read Only역할만을 가진다.
- Redis DB의 기본 설정 파일을 볼륨에 작성하여 redis-cli 접근 시 인증 암호를 설정할 수 있다.
- 컨테이너 내부의 특정 파일과 호스트의 파일을 직접 연결(mount)할 수 있다.
docker run -it -v ~/.bash_history:/root/.bash_history --rm centos:8 /bin/bash
다음과 같이 history영역을 바인딩해주면 실제 컨테이너안에서의 history정보를 Host에서도 볼 수 있다.
tmpfs Mount
- 데이터를 호스트 메모리에 저장하는 방식이다. 볼륨이나 바인드 마운트처럼 디스크에 저장되지 않고, 메모리 내에서만 저장되기 때문에 휘발성 데이터를 다룰 때 적합하다.
- 데이터를 디스크에 쓰지 않기 때문에 매우 빠르고, 데이터가 컨테이너가 종료되면 사라진다.
- 민감한 정보를 임시로 저장할 때 주로 사용한다.
docker run -d --name my_container --tmpfs /app/tmp my_image
DB 컨테이너를 볼륨으로 관리하는 이유
MySQL과 같은 DB 컨테이너를 볼륨으로 관리하는 가장 큰 장점은 백업(Backup)과 마이그레이션(Migration) 등의 작업에 매우 유용하다는 것이다.
동일한 볼륨을 이용하여 다른 MySQL 컨테이너에 마운트해도, MySQL의 기본적인 파일뿐만 아니라 테이블과 데이터도 그대로 사용할 수 있다. Docker API를 통해 데이터의 무결성을 확인하고 유지할 수 있다. 볼륨에는 익숙하니까 흔히 아는 내용일 것이다.
컨테이너 rootfs(/)영역 제한
Docker는 컨테이너의 rootfs(/) 영역의 개별적인 공간 할당 제한을 위해 --storage-opt 옵션을 제공한다. 이 옵션을 사용하려면, 해당 disk partition이 xfs로 지정되어야 하고, 추가 기능으로 pquota(project quota)가 설정되어 있어야 한다.
컨테이너의 rootfs (root filesystem)
컨테이너는 이미지를 기반으로 생성되는 격리된 환경인데, 이때 컨테이너가 사용하는 파일 시스템을 rootfs라고 한다.
- rootfs는 컨테이너 내에서 / (root 디렉토리)로 마운트되는 파일 시스템이며, 컨테이너가 실행될 때 필요한 모든 디렉터리 구조와 파일들이 들어 있다. 예를 들면 /bin, /lib, /etc 같은 시스템 디렉터리들이 포함됀다.
- 그래서 볼륨만들어 Bind Mount를 하였을때 rootfs와 컨테이너의 mount된 볼륨이 구분된다.
그래서 rootfs 영역을 제한하기 위해 다음 작업을 진행해 주어야한다.
~$ sudo vi /etc/default/grub
10 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash rootflags=uquota,pquota"
~$ sudo vi /etc/fstab
UUID=b2f0ef34-291f-411d-aa75-9a46d72c7401 /var/lib/docker xfs defaults,pquota 0 0
~$ sudo reboot
그리고 다음 과같이 -storage-opt size=1G 명령어를 주어 제한을 해주면 된다.
docker run -it -v /home/kdj/myvolume:/webapp --name=mycontainer --storage-opt size=1G ubuntu:14.04 bash
모든 컨테이너의 rootfs(/)용량을 500m으로 제한하려면 docker/daemon.json에 "storage-opts": ["overlay2.size=500m"]명령어를 추가해주면 된다.
sudo vi /etc/docker/daemon.json
{ "insecure-registries": ["192.168.56.101:5000"],
"log-driver": "json-file",
"log-opts": {
"max-size": "30m",
"max-file": "10"
},
"storage-opts": ["overlay2.size=500m"]
'Docker' 카테고리의 다른 글
| [Docker] Docker Swarm (0) | 2024.10.24 |
|---|---|
| [Docker] Dockerfile & Build & Docker-Compose (0) | 2024.10.23 |
| [Docker] Docker Networking & Proxy (0) | 2024.10.21 |
| [Docker] Docker Lifecycle & Image & CLI (0) | 2024.10.20 |