Ctrl + Shift + ESC

실습을 통해 Linux namespace 이해하기 본문

Linux

실습을 통해 Linux namespace 이해하기

단축키실행해보세요 2025. 12. 2. 21:04

* 해당 포스트는 실무 초밀착 리눅스: 클라우드 환경 운영부터 성능분석까지 강의를 듣고 정리한 것입니다.

Mount Namespace 격리하기

해당 실습은 리눅스의 Namespace(특히 Mount Namespace) 기능을 직접 체험해보는 실습이다.
실습을 통해 프로세스마다 독립적인 파일시스템 뷰를 갖게 할 수 있다는 것을 확인할 수 있다.

 

echo $$
unshare -m /bin/bash
echo $$

 

echo $$ 명령어를 입력하면 Shell의 PID를 확인할 수 있다.

unshare -m /bin/bash 명령어에서 -m은 mount namespace를 분리한다는 뜻이다. 이렇게 네임스페이스가 분리되었기 때문에, 이 안에서 발생하는 마운트/언마운트는 호스트에는 보이지 않는다. 따라서 unshare 명령어 뒤에 입력한 echo $$의 결과는 이전과 다르게 나타난다.

 

mount -t tmpfs tmpfs /mnt
mount | grep /mnt

 

새로운 mount namespace /bin/bash에 tmpfs를 마운트한다.

따라서 tmpfs가 오직 새로운 mount namespace 안에서만 보이고, 원래 터미널에서는 /mnt 가 마운트되지 않는다.

 

# 테스트 파일 생성
echo "Hello" > /mnt/hello.txt
cat /mnt/hello.txt

# Terminal을 새로 열어서 테스트 파일 내용 확인
cat /mnt/hello.txt
nsenter -t $TARGET_PID -a

 

 

tmpfs가 마운트된 namespace에서 hello.txt를 생성한 뒤 새로운 터미널을 열어 마운트 경로 /mnt를 확인해보면 아무 파일도 나오지 않는다. 다른 터미널은 다른 mount namespace (기본 namespace)에 있기 때문에 tmpfs가 마운트되어 있지 않아 파일이 존재하지 않기 때문이다.

nsenter -t $TARGET_PID -a 명령어로 unshare로 새로 만든 bash의 PID로 접속한 뒤 mount namespace에 진입하면 작성한 hello.txt가 보이는 것을 확인할 수 있다.

 

해당 실습을 통해 네임스페이스를 사용하면 프로세스마다 다른 파일시스템 뷰를 구성할 수 있다는 것을 확인할 수 있다.

 

PID 네임스페이스 격리하기

해당 실습은 리눅스에서 PID Namespace + Mount Namespace + IPC Namespace를 동시에 분리해서, 컨테이너와 동일한 프로세스 격리 환경을 직접 만드는 실습이다.

 

echo $$

 

먼저 자신의 PID를 확인한다.

 

unshare -pmif

 

새로운 PID namespace를 생성한다. 각 옵션은 다음과 같은 의미를 가진다.

  • -p : PID namespace 분리
  • -m : mount namespace 분리
  • -i : IPC namespace 분리
  • -f : 새로운 자식 프로세스를 생성하여 그 안에서 실행

 

 

# 현재 PID 확인
echo $$

# 모든 프로세스 리스트 확인 
ps aux | head -n5

 

자식 프로세스의 PID를 확인하고, 프로세스의 목록을 확인한다.

이 시점에서는 /proc이 호스트의 procfs를 보여주기 때문에 기존 호스트의 모든 프로세스가 보이는 것을 확인할 수 있다.

 

# 새로운 네임스페이스에 맞는 새로운 Proc filesystem 마운트
mount -t proc none /proc

# 모든 프로세스 리스트 확인 
ps aux | head -n5

 

PID namespace 안에서 프로세스 목록을 제대로 보려면 해당 namespace에 맞게 /proc 을 다시 마운트해야 한다.

proc filesystem을 마운트한 뒤 프로세스 리스트를 확인하면 현재 프로세스(자식 프로세스)의 리스트만 출력되는 것을 확인할 수 있다.

 

해당 실습을 통해 도커가 컨테이너를 만들 때 내부적으로 하는 동작(PID namespace 생성, mount namespace 생성, IPC namespace 분리, procfs 재마운트)을 확인할 수 있었다.

 

Network 네임스페이스 격리하기

 

해당 실습은 리눅스 네트워크 네임스페이스, Bridge, veth(Virtual Ethernet Pair)를 이용해 도커 컨테이너의 네트워크 구조를 수작업으로 직접 구축하는 과정이다.

  • Bridge: 네트워크 스위치와 같은 역할. 연결된 인터페이스로 패킷을 전달함.
  • VETH (Virtual Ethernet): 로컬 이더넷 터널. 네트워크 네임스페이스간 연결을 가능하게 함. 항상 페어로 구성됨

 

# 새로운 네트워크 네임스페이스 생성 
ip netns add ns0
ip netns add ns1

# 네트워크 네임스페이스 생성 확인
ip netns list

 

먼저 네트워크 네임스페이스를 생성한다. 

각 네임스페이스는 서로 다른 네트워크 스택을 가진 독립적인 네트워크 환경이며, ns0, ns1, host 세 개의 네트워크가 생긴다.

이것은 Docker 컨테이너가 각각 독립된 네트워크 네임스페이스를 갖는 구조와 동일하다.

 

# 네임스페이스 내의 모든 인터페이스 정보 확인
ip netns exec ns0 ip link
ip netns exec ns1 ip link
ip netns exec ns0 ip link set lo up
ip netns exec ns1 ip link set lo up

 

네트워크 네임스페이스 안에 어떤 인터페이스가 있는지 살펴보았다.

loopback 디바이스가 기본적으로 설치되어 있는 것을 확인할 수 있다.

 

# 브릿지 정보 확인
ip link show type bridge

# 호스트에 새로운 브릿지 네트워크 인터페이스 생성
ip link add br0 type bridge
ip link set br0 up

# 브릿지 네트워크에 IP 설정
ip addr add 192.168.2.1/24 dev br0
ip addr
ping -c 2 192.168.2.1

 

Root namespace(호스트)에 br0이라는 새로운 bridge 네트워크 인터페이스를 생성하고, 구동시킨다.

bridge는 생성 시 ip가 없기 때문에 라우팅이 되지 않는다. 따라서 bridge에 192.168.2.1/24 ip 주소를 추가한다.

ip addr로 확인해보면 br0에 inet으로 192.168.2.1/24가 추가된 것을 확인할 수 있다.

 

192.168.2.1 주소로 ping도 잘 전달되는 것을 확인할 수 있다.

 

# veth*, ceth*를 위한 veth 페어 생성
ip link add veth0 type veth peer name ceth0
ip link add veth1 type veth peer name ceth1

# veth0를 br0에 연결
ip link set veth0 master br0
ip link set veth1 master br0
# veth0 시작
ip link set veth0 up 
ip link set veth1 up 

# ceth*를 ns* 네임스페이스에 연결
ip link set ceth0 netns ns0
ip link set ceth1 netns ns1

# ceth* 시작 
ip netns exec ns0 ip link set ceth0 up
ip netns exec ns1 ip link set ceth1 up

 

namespace 간에 연결을 하기 위한 버추얼 이더넷 디바이스 veth와 페어인 ceth를 생성한다.

veth는 bridge에 연결하고, ceth은 네임스페이스에 연결한 뒤 veth와 ceth를 구동시킨다.

 

구동 상태를 확인해보면 이더넷 스페이스가 up 상태로 올라와 있지만, ip는 할당되지 않은 것을 확인할 수 있다.

 

# ns*의 ceth* 인터페이스에 IP 할당
ip netns exec ns0 ip addr add 192.168.2.2/24 dev ceth0
ip netns exec ns1 ip addr add 192.168.2.3/24 dev ceth1

# ns1의 네트워크 인터페이스 확인
ip netns exec ns1 ip addr
ceth 인터페이스에 각각 192.168.2.2/24와 192.168.2.3/24 ip 주소를 할당한다.
 
 
namespace1의 ip addr를 확인해 보면 ip 주소가 192.168.2.3/24가 추가된 것을 확인할 수 있다.
 
# ns0에서 ns1로 연결
ip netns exec ns0 ping -c 2 192.168.2.3

 

 

모든 네트워크 구성이 끝났다. namespace 0은 virtual ethernet으로 root namespace의 bridge와 연결되었고, namespace1도 동일하게 연결이 되었다. 따라서 namespace 0에서 namespace 1로 통신을 보내면 bridge를 통해 통신이 전달될 것이다.

ns0에서 ns1로 ping을 보내보면 네트워크가 잘 연결되어 핑이 전달되는 것을 확인할 수 있다.

 

해당 실습을 통해 Docker 엔진이 컨테이너 네트워크를 만들 때 내부에서 수행하는 작업을 동일하게 수행해볼 수 있었다.