원문 캡처 · kenmuse.com
원문 캡처 · kenmuse.com

Docker는 docker run hello-world를 실행할 때 가상 머신을 띄우는 대신 리눅스 커널 기능으로 호스트 안에 격리된 환경을 만든다. 이 글은 네임스페이스가 프로세스가 볼 수 있는 자원을 분리하고 cgroups가 사용할 수 있는 자원을 제한하는 방식을 명령줄 도구로 설명한다. 두 기능을 설정하려면 대체로 root 권한이 필요하다.

네임스페이스는 프로세스 테이블, 파일시스템 마운트, 네트워크 인터페이스, 호스트 이름, 사용자·그룹 ID, 프로세스 간 통신(IPC), cgroup 계층에 대한 프로세스의 관점을 분리한다. 예를 들어 새 PID 네임스페이스 안의 프로세스는 자기만의 프로세스 ID를 1부터 부여받고 호스트의 다른 프로세스를 볼 수 없다. 새 네트워크 네임스페이스에는 비활성 상태의 루프백 인터페이스만 있으며, 외부 네트워크와 연결하려면 호스트와 컨테이너 네임스페이스를 잇는 가상 이더넷 쌍을 만들어야 한다.

글은 sudo unshare로 PID·UTS·네트워크·마운트 네임스페이스를 각각 만들어 보는 예제를 제시한다. 이어 unshare에 여러 네임스페이스 옵션을 함께 지정해 컨테이너와 비슷한 환경을 구성한다. 이 환경은 자체 호스트 이름과 프로세스 테이블, 격리된 네트워크를 갖지만, Docker가 제공하는 계층형 파일시스템과 cgroup 기반 자원 제한, 관련 도구는 포함하지 않는다.

네임스페이스가 프로세스가 볼 수 있는 범위를 나눈다면 cgroups는 CPU·메모리·입출력 자원 사용량을 제한한다. 글의 예제에서는 cgroups v2의 /sys/fs/cgroup/ 아래에 그룹을 만들고 memory.max에 50M을 기록해 메모리 한도를 설정한다. 해당 그룹의 프로세스가 50MiB를 넘게 사용하려 하면 커널의 OOM 처리기가 프로세스를 종료한다. cgroup 제어 파일은 root 소유이며, 그룹 생성과 한도 설정에는 root 권한이 필요하다.

Docker는 --memory=256m --cpus=1.5 같은 옵션을 컨테이너별 cgroup 설정으로 바꾼다. 글의 Linux 예제에서 메모리 한도는 268435456바이트이고, CPU 설정은 100밀리초마다 150000마이크로초의 CPU 시간을 허용하는 값으로 표시된다. 이처럼 네임스페이스와 cgroups는 각각 가시성과 자원 사용을 다루며, Docker는 이 커널 기능에 파일시스템 계층과 실행 도구를 더해 컨테이너를 구성한다.

글은 unshare와 cgroup 조작에 필요한 권한이 리눅스 커널의 보안 경계와 관련 있다고 설명한다. 일반 사용자가 임의로 cgroup 한도를 설정하거나 프로세스를 옮길 수 있다면 다른 프로세스의 자원을 고갈시키거나 서비스 거부를 일으킬 수 있다는 것이다. 다음 글에서는 Docker나 Kubernetes가 이런 기능을 런타임으로 구성하고 권한을 낮은 권한의 사용자와 분리하는 방법을 다룰 예정이라고 밝혔다.

관련 글: Docker Privilege 시리즈, 리눅스 커널부터 컨테이너 격리 구성하기, Docker가 root 권한을 사용하는 방식, Rootless Docker와 숨겨진 보안 절충점.