도커 컨테이너의 자원과 로컬의 자원
도커(Docker) 컨테이너는 가상머신(VM)과 달리 호스트 OS의 커널을 직접 공유하면서 애플리케이션을 격리하는 방식으로 동작함.
이로 인해 가볍고 빠른 실행 환경을 제공하지만, 동시에 호스트 머신(로컬) 자원과 긴밀히 연결되어 있음.
도커 컨테이너의 기본 아키텍처
1. 호스트 커널 공유
컨테이너는 호스트 OS(또는 VM 내부)의 커널을 그대로 공유함.
가상머신(VM)처럼 별도의 OS 커널을 갖지 않으므로, 프로세스 수준에서 격리가 이루어짐.
이를 통해 오버헤드가 적고, 리소스를 효율적으로 사용할 수 있음.
2. Namespaces (네임스페이스)
리눅스 커널의 네임스페이스 기술을 활용해 프로세스, 네트워크, 파일시스템, 사용자, IPC, UTS 등을 격리함.
예를 들면,
PID Namespace: 프로세스 ID 격리(컨테이너 내부의 PID 1은 호스트에서 보면 다른 PID)
Network Namespace: 네트워크 인터페이스, IP, 포트 등을 분리(컨테이너마다 독립된 네트워크 스택 할당)
Mount Namespace: 파일시스템 경로를 컨테이너별로 다른 계층 구조로 표시
3. Control Groups (cgroups)
cgroups는 리눅스 커널의 리소스 사용량을 제한하거나 관리하는 기능임.
CPU, 메모리, 블록 I/O, 네트워크 I/O 등 다양한 자원에 대해 제한(limit), 우선순위(priority), 모니터링을 할 수 있음.
도커는 컨테이너를 생성할 때 각 컨테이너에 대해 cgroup을 생성하거나 할당하여 자원 사용량을 격리 및 제어함.
4. Union File System
도커 이미지는 여러 레이어(layer)의 합으로 구성되며, 이를 합치는 과정에 Union FS(OverlayFS 등)가 사용됨.
이미지를 실행(run)하면 읽기 전용 레이어 + 읽기/쓰기 가능한 레이어(RW layer)가 합쳐져 실제 컨테이너 파일시스템을 구성함.
이러한 구조 덕분에 이미지 재사용이 용이하고, 컨테이너별로 파일 변경 사항을 격리할 수 있음.
CPU와 메모리 사용 관계
1. CPU 사용
각 컨테이너는 기본적으로 호스트의 CPU를 공유하며, OS 스케줄러에 의해 프로세스 단위로 CPU 시간을 할당받음.
docker run --cpus, docker update --cpus 명령 등을 사용해 특정 컨테이너에 할당할 수 있는 CPU 양을 제한 가능.
예를 들어, docker run --cpus="2.5" ... → 해당 컨테이너에 2.5 코어 분량만큼의 CPU 리소스 사용을 제한
cgroups를 이용해 CPU 공유 비율(CPU shares)을 조정하거나 CPU 코어를 할당(pinning)할 수도 있음.
예를 들어, docker run --cpuset-cpus="0-2" ... → 0~2번 코어만 사용
2. 메모리 사용
기본적으로 컨테이너는 호스트 메모리를 공유하지만, cgroups 설정을 통해 최대 사용 가능 메모리를 제한할 수 있음.
docker run -m or --memory 파라미터로 메모리 상한(리밋)을 지정 가능
예를 들어, docker run -m 512m ... → 해당 컨테이너는 최대 512MB 메모리 사용
SWAP 사용 제한도 설정할 수 있으며(--memory-swap), 이를 통해 컨테이너 별로 OOM(Out-Of-Memory) 킬러가 어떻게 동작할지도 제어할 수 있음.
스토리지와 파일 시스템
1. OverlayFS (Union FS)
도커는 기본 스토리지 드라이버로 Overlay2(또는 AUFS, Btrfs 등)를 사용하여 레이어를 합치는 방식으로 이미지와 컨테이너의 파일시스템을 구성함.
컨테이너는 읽기 전용 레이어 + 읽기/쓰기 레이어를 사용하므로, 어떤 컨테이너가 파일을 수정해도 원본 이미지는 변하지 않음.
2. 볼륨(Volume)과 바인드 마운트(Bind Mount)
도커 볼륨: 도커가 관리하는 별도의 파일시스템 영역. /var/lib/docker/volumes 경로 등에 존재하며, 컨테이너와는 독립적인 라이프사이클을 가짐.
볼륨을 사용하면 컨테이너가 재시작되거나 삭제되어도 데이터가 유지됨.
여러 컨테이너 간 데이터 공유 가능.
바인드 마운트: 호스트 디렉터리를 컨테이너 내부 디렉터리에 연결.
예를 들어, docker run -v /path/on/host:/path/in/container
호스트의 실제 디렉터리를 그대로 사용하므로, 파일 수정이 실시간 반영됨.
볼륨과 바인드 마운트 둘 다 호스트의 스토리지를 사용하지만, 볼륨은 도커에서 직접 관리하고, 바인드 마운트는 호스트 사용자가 직접 관리하는 구조 차이가 있음.
3. 레이어 캐시와 이미지 최적화
각 컨테이너는 이미지를 기반으로, 최종적으로는 AUFS/OverlayFS 등을 통해 호스트의 디스크를 사용함.
이미지 빌드 시 레이어를 최소화하고 불필요 파일을 제거하면, 디스크 사용량과 빌드 시간을 크게 줄일 수 있음.
네트워크 지원
1. Network Namespace & 가상 인터페이스
각 컨테이너는 독립된 Network Namespace를 가짐으로써 자체 IP 주소, 네트워크 인터페이스를 가짐.
기본적으로 호스트에는 docker0라는 브릿지(Bridge)가 생성되며, 컨테이너마다 veth pair 가상 인터페이스가 연결됨.
호스트 머신에서 외부로 트래픽이 나갈 때, 컨테이너의 네트워크 트래픽은 NAT나 포트포워딩을 통해 외부와 통신함.
2. 포트 매핑
docker run -p 호스트포트:컨테이너포트 형태로 사용
예를 들어, docker run -p 8080:80 nginx → 호스트 8080번 포트로 들어온 트래픽이 컨테이너 80번 포트로 전달.
여러 컨테이너 간 통신은 docker network create로 만든 사용자 정의 네트워크를 통해 보다 세부적으로 관리 가능.
3. 네트워크 리소스 격리
컨테이너 간 네트워크 트래픽은 별도의 네트워크 네임스페이스로 격리되며, 필요 시 호스트 네트워크를 직접 사용(--network host)할 수도 있음.
cgroups에서도 네트워크 I/O 트래픽에 대해 제한(bandwidth), 우선순위(qdisc) 등을 설정할 수 있지만 일반적으로 많이 사용되지는 않음.
프로세스와 사용자 권한
1. 프로세스 격리
PID 네임스페이스로 인해, 컨테이너 내부에서 보면 자신만의 PID 공간을 가짐.
실제로는 호스트 프로세스 목록 중 하나(예: docker inspect 시 볼 수 있는 PID)와 매핑됨.
도커 컨테이너가 죽으면 내부 프로세스가 종료되지만, 호스트 커널에서는 일반 프로세스가 종료되는 것과 동일한 원리로 작동.
2. 사용자 네임스페이스 (User Namespace)
컨테이너 내부의 root 사용자가 호스트에서는 비루트 사용자 UID로 매핑될 수도 있음(사용자 네임스페이스).
이를 통해 보안 강화를 할 수 있지만, 기본 설정에서는 비활성화되어 있는 경우가 많음.
3. 보안 권장사항
컨테이너 내부에서 root 권한으로 실행하면 호스트 보안에 위험이 될 수 있으므로, 도커 이미지 빌드 시 USER 지시어를 활용해 비루트 사용자로 동작하도록 설정하는 것을 권장함.
RUN useradd -m myuser
USER myuser
도커 컨테이너 자원 제한 및 모니터링
1. 자원 제한 방법
CPU 제한: --cpus, --cpu-shares, --cpuset-cpus 등
메모리 제한: --memory, --memory-swap
블록 I/O 제한: --blkio-weight, --device-read-bps, --device-write-bps
PID 제한: --pids-limit
2. 모니터링
docker stats 명령을 통해 실행 중인 컨테이너의 실시간 CPU, 메모리, 네트워크, 블록 I/O 사용량 확인 가능.
더욱 세부적인 모니터링을 위해 cgroups 메트릭을 직접 확인하거나, Prometheus/Grafana 등으로 수집 및 시각화 할 수 있음.
도커 컨테이너와 로컬 자원 관계 정리
1. CPU
호스트 CPU를 컨테이너들이 경쟁적으로 사용.
cgroups로 제한 또는 우선순위 설정 가능.
2. 메모리
호스트 메모리를 기반으로 동작.
할당량을 초과하면 OOM 킬러 발동.
--memory 등으로 강제 제한 가능.
3. 스토리지(파일시스템)
OverlayFS를 통해 호스트 디스크 사용.
볼륨 및 바인드 마운트를 통해 데이터 영속성 확보.
이미지 레이어는 캐싱(Union FS) 구조를 활용.
4. 네트워크
Network Namespace로 격리, NAT 및 포트 매핑 사용.
호스트 브릿지(docker0) 또는 사용자 정의 네트워크로 연결.
호스트 네트워크를 직접 사용(옵션) 가능.
5. 프로세스 및 권한
호스트의 프로세스 관리와 같은 커널 리소스 공유.
네임스페이스로 PID를 격리하지만, 실질적으로는 호스트 프로세스와 동일 선상.
User Namespace를 통해 권한 격리 가능.
정리
도커 컨테이너는 “별도의 OS”가 아니라, 호스트 커널을 공유하는 “격리된 프로세스 세트”임.
리소스 격리를 위해 Namespaces와 cgroups가 핵심 역할을 하며, 이를 통해 CPU, 메모리, 네트워크, PID 등을 독립적으로 보이게 하고 제한할 수 있음.
스토리지 계층(Union FS)을 통해 이미지와 컨테이너 파일시스템을 효율적으로 관리하며, 볼륨과 바인드 마운트를 통해 로컬 디스크와 유연하게 연결할 수 있음.
VM보다 훨씬 가볍고 빠르지만, 실제로는 호스트와 긴밀히 연결되어 있으므로 보안과 자원 제한 설정에 신경 써야 함.
궁극적으로 호스트 자원을 필요한 만큼만 컨테이너별로 나누어 쓰면서, 환경 격리와 재현성(Repeatability)을 확보하는 것이 도커 컨테이너의 목표라 할 수 있음.
도커 컨테이너는 로컬 자원을 호스트 커널 수준에서 공유하고, cgroups와 네임스페이스를 통해 필요한 만큼만 격리하여 활용함.
이를 적절히 구성하고 제한, 모니터링, 최적화함으로써 가볍고 일관성 있는 배포 환경을 구축할 수 있음.
'Operating System > Docker' 카테고리의 다른 글
[Docker] 도커 볼륨 구조 (1) | 2025.01.24 |
---|---|
[Docker] 컨테이너 실시간 모니터링 방법 (2) | 2025.01.24 |
[Docker] Dockerfile 작성하는 방법 (0) | 2025.01.24 |
[Docker] 도커와 도커의 네트워크 (0) | 2024.06.08 |
[Docker] 컨테이너가 실행중인지 확인하고 종료 및 삭제하는 쉘스크립트 (0) | 2023.05.31 |