Docker

[ Docker ] 컨테이너 기술 (LXC 와 Docker)

jogaknabi_1023 2024. 3. 6. 16:50

이전 게시물에서 가상화에서 VM과 Container를 알아보았다. 그럼 이번에는 컨테이너 기술과 가장 널리 쓰이는 오픈소스인 Docker 에 대해서 이 때까지 학습한 내용을 다뤄보겠다. 게시물 하나로 끝내려고 했는데 공부하다보니까 세세한 것까지 작성하다보니 이 페이지에서는 컨테이너 기술의 주요 구성요소들을 다루고 다음 페이지에서 Docker에 대해 자세히 정리하겠다.

 

Linux Container(LXC)와  Docker 

 

Linux Container(LXC)

단일 리눅스 시스템에 동작하고 있는 프로세스를 격리시켜 각 프로세스마다 독자적인 리눅스 시스템 환경을 구축한 것.

'OS 수준에서의 가상화'이다.

 

 일반적으로 가상화는 하이퍼바이저(Hypervisor)라는 논리적 플랫폼을 이용하여 하나의 Host OS 위에 여러 Guest OS를 구동하는 기술이다. 이와 달리 리눅스 컨테이너는 하나의 리눅스 시스템에서 프로세스들을 격리시켜 독자적인 시스템 환경을 구축한다. 그래서 리눅스 컨테이너에서 가상화된 프로세스들은 모두 리눅스 시스템을 호환해야한다.

 

 초기 Docker는 LXC라는 컨테이너 기술을 기반으로 만들어진 상위레벨의 컨테이너 기술이다. 하지만 0.9 버전 이후부터 자체적으로 컨테이너 관리 및 실행을 위한 기술을 개발하여 runC와 같은 컨테이너 실행 엔진을 사용하여 컨테이너를 관리하고 실행한다.

 

 

초기 Docker Engine Model & LXC Architecture

 

컨테이너 기술

LXC(Linux Containers)는 컨테이너 기술을 사용하기 위해 Linux 커널에 내장된 네임스페이스(namespaces) 및 컨트롤 그룹(control groups, cgroups)과 같은 기능을 활용하는데, 이러한 기능은 컨테이너의 격리 및 리소스 제어를 가능하게 한다.

간단히 말하면 namespace는 운영시스템을 쪼개서 각각 고립된 상태로 운영이 되는 개념으로 자원 격리를 의미하며, cgroups는 namespaces로 고립된 환경에서 사용할 자원을 제한하는 역할을 한다.

- cgroup은 해당 프로세스가 쓸 수 있는 사용량을 제한
- namespace는 해당 프로세스가 볼 수 있는 범위를 제한

 

1) Cgroups (control groups)

 Cgroups는 프로세스들이 사용하는 시스템의 자원의 사용 정보를 수집/제한/격리시키는 리눅스 커널 기능을 한다. 모든 프로세스에 대해 리소스 사용 정보(CPU, 메모리, 디스크 I/O, 네트워크 등)를 수집한다.

  • 주요 기능

       Resource limits – 프로세스가 사용할 수 있는 특정 리소스(예: 메모리 또는 CPU)의 양을 제한하도록 cgroup을 구성할 수 있다.

       Prioritization – 리소스 경합이 있을 때 다른 cgroup의 프로세스와 비교하여 프로세스가 사용할 수 있는 리소스(CPU, 디스크 또는 네트워크)의 양을 제어할 수 있다.

       Accounting – 리소스 제한은 cgroup 수준에서 모니터링되고 보고된다.

       Control – 단일 명령으로 cgroup에 있는 모든 프로세스의 상태(고정, 중지 또는 다시 시작)를 변경할 수 있다.

 

  • Cgroups 의 계층구조

 Cgroups는 계층 구조를 사용하여 그룹화된 프로세스를 관리한다. 예를 든다면, 사용자 애플리케이션과 서버와 같은 데몬 프로세스를 나눠, 각각의 그룹에 CPU 할당할 수 있다. Cgroups의 부모 자식 관계에서는 자식이 부모의 제한을 물려받는다. 따라서 자식은 부모 그룹의 제한을 초과하여 할당할 수 없으므로 자식 프로세스의 과도한 CPU 사용이 부모 프로세스나 다른 중요한 프로세스의 성능에 부정적인 영향을 미치는 것을 방지할 수 있다.

cgroups의 계층구조

 

  • Cgroups 의 주요 서브 시스템

        - cpu : cpu 사용량 제한

        - cpuacct : cpu 사용량 통계 정보 제공

        - cpuset : cpu나 메모리 배치를 제어

        - memory : 메모리나 스왑 사용량을 제한

        - devices : 디바이스에 대한 액세스 허가/거부

        - freezer : 그룹에 속한 프로세스 정지/재개

        - net_cls : 네트워크 제어 태그를 부가

        - blkio : 블록 디바이스 입출력량을 제어

 

  • Cgroups 실습 : CPU 사용량 제한 

실습 환경: AWS EC2 ubuntu 22.04

# stress 실험을 위해 stress 패키지 설치
sudo apt update && apt install -y stress

# cgroup 버전 확인. 만약 v1을 지원하면 cgroup2 부분 보이지 않는다. 나는 v2.
grep cgroup /proc/filesystems

# 현재 쉘의 pid가 어떤 cgroup인지 확인
cat /proc/$$/cgroup

/sys/fs/cgroup 에서 부모 디렉토리인 test_cgroup 디텍토리 생성 후 이동.

cpu를 subtree이 추가하여 컨트롤 할 수 있도록 설정 후 cpu.max를 통해 제한을 건다.

 

echo "+cpu" >> /sys/fs/cgroup/test_cgroup/cgroup.subtree_control

 

 

이를 통해 test_cgroup에 속한 프로세스들은 일반적인 CPU 시간의 단위로써 1초 안에 100,000분의 1부터 1초까지의 CPU 시간을 사용할 수 있게 된다.

 

test_cgroup에 속한 프로세스 생성 전, 원래 cpu 사용량 확인

먼저, 해당 쉘에서 stress를 주고 새로운 터미널에서 top로 확인해보면 2289인 stress 주는 해당 프로세스가 cpu를 99.7% 사용하는 것을 볼 수 있다. echo $$ 명령어는 현재 쉘의 PID를 나타낸다.

 

그렇다면 부모 test_cgroup에 속한 자식 test_group_child 디렉토리 생성하고 CPU 사용량을 확인해보자.

# 부모(test_cgroup)위치에서 자식 디렉토리 생성 && 자식으로 이동
mkdir test_cgroup_child && cd test_cgroup_child

# 현재 실행 중인 셸의 PID를 가져와서 test_cgroup/test_cgroup_child cgroup에 속한 프로세스로 추가
echo $$ > /sys/fs/cgroup/test_cgroup/test_cgroup_child/cgroup.procs

 

 현재 쉘 PID였던 1680이 test_cgroup/test_cgroup_child 에 속한 프로세스로 추가되었으며, 현재 쉘 pid가 어떤 cgroup에 속하는지 다시 한 번 확인해보면 test_cgroup_child인 것을 확인할 수 있다.

 그럼, 비교를 위해 자식 디렉토리(test_group_child) 안에서 stress 주게 되면 9.6%의 CPU 사용량을 볼 수 있다.

 

실습 후 삭제

# cgroup 삭제할 때는 깊은 곳부터 순차적으로 (부모 -> 자식)
sudo rmdir /sys/fs/cgroup/test_cgroup/test_cgroup_child
sudo rmdir /sys/fs/cgroup/test_cgroup

 

 

2) Namespace

Namespace는 프로세스별로 별도의 커널 자원을 분할하는 리눅스 커널의 기능이다.

 

  • namespace 종류

- Process ID(pid) : pid 정보를 격리함. 네임스페이스 외 다른 프로세스에 접근이 불가.
- Network(net) : 네트워크 장치, IP주소, 포트, 라우팅 테이블 등 네트워크 리소스를 격리하고 가상 네트워크 장치를 할당함.
- Filesystem/mount(mnt) : 프로세스별로 마운트되는 파일시스템을 격리함.
- inter-proc comms(ipc) : inter-process communication을 격리. 다른 프로세스의 접근이나 제어를 방지함.
- UTS : 호스트명, 도메인명을 격리
- User : 프로세스 별로 UID, GID 정보를 격리

 

  • Namespace 실습 : PID 격리 해보기

새로운 네임스페이스를 생성하여 PID를 격리하면 마치 하나의 시스템에서 PID가 두개인 것처럼 프로세스를 만들어보는 것이다.

실습 순서 ) unshare 명령어로 새로운 네임스페이스를 생성해보고, 쉘을 실행하여 PID를 확인한다.

  • --user: 유저 네임스페이스를 생성. 새로운 유저 네임스페이스가 생성되며, 새로운 UID와 GID 매핑이 이루어진다.
  • --pid: PID 네임스페이스를 생성. 현재 프로세스와 그 자식 프로세스들이 새로운 PID 네임스페이스 안에서 실행된다.
  • --map-root-user: 새로운 유저 네임스페이스에서 root 권한을 매핑한다. 즉, 새로운 유저 네임스페이스에서도 현재 사용자가 root 권한을 가질 수 있다.
  • --mount-proc: 새로운 프로세스 네임스페이스에 /proc 파일 시스템을 마운트한다. /proc 파일 시스템은 프로세스 및 시스템 정보에 대한 인터페이스를 제공한다.
  • --fork: 새로운 프로세스를 생성한다.
  • bash: 새로운 프로세스로 실행할 쉘을 지정하며, 새로운 쉘을 실행합니다.

 

 

다음 게시물에서는 LXC의 단점들이 보완된 Docker에 대해서 자세히 알아보겠다.

 

 

더보기

아 그리고 이건 실습 중 내 개인적인 궁금증이다.

 

아까 cgoups 에서 stress 주는 PID 와 top 명령어로 stress 주는 pid를 확인했을 때 같을 때도 있었고 다를 때도 있었다. 왜 그런지 찾아보니 stress 명령어는 보통 두 종류의 프로세스를 사용한다고 한다.

1. 실제 작업을 수행하는 프로세스: 이 프로세스는 CPU, 메모리, 디스크 등의 시스템 리소스에 부하를 가하거나, 특정 작업을 수행하여 시스템을 테스트하는 역할을 한다.
2. 작업 상태를 모니터링하고 출력하는 프로세스: 이 프로세스는 첫 번째 프로세스의 상태를 확인하고 출력하여 사용자에게 제공하며, 주로 진행 상황을 모니터링하거나 결과를 표시하는 데 사용된다.

 

따라서 첫 번째 프로세스는 실제로 작업을 수행하고 시스템에 부하를 가하지만, 두 번째 프로세스는 단순히 작업의 상태를 모니터링하고 출력하는 역할만 담당한다. 그래서 두 프로세스가 실행되기 때문에 다른 PID가 보일 수 있다는 것이다.

 

 

자료 출처 : 

https://kimjingo.tistory.com/38

https://velog.io/@whattsup_kim/Linux-Kernel-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%9D%98-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B2%A9%EB%A6%AC-%EA%B8%B0%EB%8A%A5-cgroup-namespace-union-mount

 

 

 

 

 

 

 

 

'Docker' 카테고리의 다른 글

[ Docker ] Container & Docker 작동 원리  (0) 2024.03.07