본문 바로가기
  • 오늘처럼
소프트웨어 아키텍처

Container 기반기술 : Namespace

by bluefriday 2021. 9. 2.
반응형

 

도커 컨테이너에 대한 소개에서 언급하였듯이, 도커 서비스는 2013년에 처음 등장하였다. 하지만 컨테이너 기술 자체는 이미 훨씬 예전부터 시스템 개발자들 사이에서 사용되었으며, 결국 도커는 리눅스 컨테이너와 관련된 기술들을 패키징하여 더 사용하기 쉽게 만든 하나의 솔루션으로 볼 수 있다. 

이러한 컨테이너 서비스는 chroot, cgroup, namespace 와 같은 리눅스 기반 기술들로 구성되어 있는데 여기서는 namespace 에 대해서 조금 더 알아보기로 한다.

리눅스 namespace 는 하나의 운영체제 내부에서, 프로세스들이 각각 별개의 독립된 공간을 사용하는 것처럼 느낄수 있도록 격리된 환경을 제공하는 경량의 프로세스 가상화 기술. H/W를 가상화하는 hypervisor와 달리 동일한 OS 에서 작동하되, 실행 환경 자체를 구분하는 기술이다.

기술적으로 구현할 때는 unshare()와 setns() 시스템콜을 이용하며, 후술할 6개의 상수 플래그를 clone(), unshare(), setns() 시스템콜에 전달하여 구현한다.

  • clone(): 새로운 프로세스를 만들고 이를 새로 지정된 네임스페이스에 연결
  • unshare(): 현재 프로세스를 새로 지정된 네임스페이스에 연결
  • setns(): 이미 존재하는 네임스페이스에 프로세스를 연결

LXC에는 아래의 6개의 네임스페이스가 사용되고 있다. (/usr/include/linux/sched.h 파일에서 확인가능하다) 각각의 네임스페이스에 대해서 간단히 알아보고 실제 리눅스 환경에서 사용되는 예를 알아보자.

namespace  describe 
 mnt   CLONE_NEWNS 플래그에 의해 지정 
 uts   CLONE_NEWUTS 플래그에 의해 지정
 ipc  CLONE_NEWIPC 플래그에 의해 지정 
 pid   CLONE_NEWPID 플래그에 의해 지정
 usr   CLONE_NEWUSER 플래그에 의해 지정
 net   CLONE_NEWNET 플래그에 의해 지정 

 

1. Mount namespace

2002년 커널 2.4.19 부터 사용되었으며, 프로세스와 그 자식 프로세스가 각기 다른 파일 시스템 마운트 포인트를 사용하도록 지정한다. 기본적으로 프로세스들은 동일한 네임스페이스를 공유하므로, 파일시스템이 마운트 되거나 마운트 해제 되는 것을 모든 프로세스가 인지 가능하다.

하지만 clone() 시스템콜을 통해 프로세스를 생성할 때 CLONE_NEWNS 플래그가 전달되면, 새로 생성되는 프로세스는 호출한 프로세스가 갖고 있는 마운트 트리의 사본을 가져온다. 이 사본은 부모 프로세스에 영향을 미치지 않고 새로 생성된 프로세스가 파일 시스템을 마운트하거나 해제 등의 변경을 할수 있도록 하는데,  이렇게 사본이 생성된 시점에서는 기본 네임스페이스의 파일시스템에 대한 마운트 및 마운트 해제는 모든 프로세스에서 볼 수 있지만 각각의 프로세스별 마운트 네임스페이스에서의 변경은 해당 프로세스의 네임스페이스 밖에서는 알 수 없다.

Exam) 먼저 아래와 같이 디렉토리를 생성하고 unshare 명령어를 사용하여 mount namespace 를 분리한다.

[root@localhost /]# mkdir /tmp/mount_ns
[root@localhost /]#
[root@localhost /]# unshare -m /bin/bash

* unshare : namespace를 분리하는 명령어
* -m : mount namespace를 의미

 

readlink 명령어를 사용하여 현재프로세스의 mount 정보를 확인해 볼 수 있다.

[root@localhost /]# readlink /proc/$$/ns/mnt
mnt:[4026532190]
[root@localhost /]#

* readlink  : symbolic link의 원본을 찾는 명령어
* $$  : 현재 bash shell의 process id
* /proc/  : 시스템의 여러 실시간 정보들을 디렉토리와 파일 형태로 저장.
              linux 에서 가상 파일시스템에 위치하는 가상 디렉토리이며 실제 존재하지 않고 메모리에 저장된다.

 

그리고 tmpfs 파일시스템을 해당 디렉토리에 mount 한 뒤 파일시스템 정보를 확인하자.

[root@localhost /]# mount -n -t tmpfs tmpfs /tmp/mount_ns
[root@localhost /]#
[root@localhost /]# df -h | grep mount_ns
tmpfs                1.9G     0  1.9G   0% /tmp/mount_ns


* mount -n option           : /etc/mtab에 쓰지 않도록 설정 (/etc/mtab : 현재 시스템의 마운트 정보)
* mount -t tmpfs option   : mount할 file system 확인
* df                              : display filesystem
* df -h                          : human-readable option

 

마운트 된 파일시스템의 정보를 추가로 확인한다.

[root@localhost /]# cat /proc/mounts | grep mount_ns
tmpfs /tmp/mount_ns tmpfs rw,seclabel,relatime 0 0
[root@localhost /]#

 

이제 새로 세션을 열어 다시 확인해본다. 분리한 마운트 파일시스템들이 보이지 않는 것을 확인할 수 있다.

[root@localhost ~]#
[root@localhost ~]# readlink /proc/$$/ns/mnt
mnt:[4026531840]

[root@localhost ~]# cat /proc/mounts | grep mount_ns
[root@localhost ~]# df -h | grep mount_ns
[root@localhost ~]#

 

2. UTS namespace

Unix Timesharing Namespace 를 의미하며, 각각의 리눅스 컨테이너가 자신의 식별자 (hostname -f 로 확인)를 유지관리하기 위해 호스트 이름과 도메인 이름을 네임스페이스별로 격리한다. 호스트 이름에 종속적인 대부분의 어플리케이션에 중요한 기능이며, unshare 명령어에 -u 옵션을 부여해서 구현 가능하다.

Exam) 먼저 최초 접속한 세션에서의 호스트네임을 확인해보자.

[root@localhost ~]# hostname
localhost.localdomain
[root@localhost ~]#

 

그 후에 unshare -u 명령어를 이용하여 UTS namespace를 분할한다. 분할 후 새로운 hostname으로 setting 후에 호스트네임이 변경된 것을 확인한다.

[root@localhost ~]# unshare -u /bin/bash
[root@localhost ~]# hostname uts-namespace
[root@localhost ~]#
[root@localhost ~]# hostname
uts-namespace
[root@localhost ~]# cat /proc/sys/kernel/hostname
uts-namespace
[root@localhost ~]#

 

다시 새로운 세션을 열어서 호스트네임을 확인해보면 방금 전에 분리한 namespace에서의 호스트네임이 보이지 않는 것을 알 수 있다.

[root@localhost ~]#
[root@localhost ~]# hostname
localhost.localdomain

[root@localhost ~]#

 

03. IPC namespace

프로세스간 데이터 교환 및 프로세스와 스레드간의 작업을 동기화하는 기능을 제공하는 namespace이다. semaphore, file locking, mutex 와 같은 자원에 대한 접근제어(primitives)를 제공하며, 컨테이너에서 실제 프로세스를 분리하기 위해서도 필요하다.

 

04. PID namespace

프로세스 ID를 분할하여 관리하기 위해 필요한 namespace이다. init 프로세스만이 가질 수 있는 PID 1번을 독립적으로 추가할당하며, 동일한 OS에서 init 프로세스 뿐만 아니라 여러 프로세스가 PID 충돌없이 실행 가능하게 해준다.

 

05. USR namespace

프로세스가 namespace 내부와 기본 namespace 간에 각기 다른 사용자 및 그룹 ID를 가질 수 있도록 지원한다. kernel v3.8에서 소개되어 아직 많은 시스템에서 지원하지는 않으며, 외부에서는 권한이 없는 ID를 갖고 있는 프로세스가, 자신이 생성한 컨테이너 내부에서는 루트 권한으로 실행 가능하다.

 

06. NET namespace

네트워크 장치, 주소, 경로 및 방화벽 규칙 같은 네트워킹 자원을 격리한다. 네트워크 스택의 논리적 복사본을 효과적으로 생성하여 여러 네임스페이스가 동일 포트로 다수의 서비스 제공 가능하며, iproute2 package 등을 이용하여 구현 가능하다.

Exam) ip 명령어를 사용하여 현재 인터페이스를 조회하면 lo와 enp0s3 인터페이스를 확인할 수 있다.

[root@localhost ~]# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
...(생략)
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
...(생략)
[root@localhost ~]#

 

루트 네트워크 네임스페이스로부터의 경로는 아래와 같이 확인 가능하다.

[root@localhost ~]#
[root@localhost ~]# ip r s
default via 10.0.2.2 dev enp0s3 proto static metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100
...(생략)
[root@localhost ~]#

 

이제 새로운 네트워크 네임스페이스를 2개 생성한다.

[root@localhost ~]#
[root@localhost ~]# ip netns add ns1
[root@localhost ~]# ip netns add ns2
[root@localhost ~]#
[root@localhost ~]# ip netns
ns2
ns1
[root@localhost ~]#

 

이후 새로운 네임스페이스 안에서 새로 네트워크 명령어를 실행한다. 새 네임스페이스 네트워크 안에서는 한 개의 loopback 인터페이스만 있는 것을 확인 가능하다.

[root@localhost ~]# ip netns exec ns1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@localhost ~]#

 

참조

 

 

댓글