Search

네임스페이스 격리

Tags
unix
namespace
isolation
Created
2024/08/31 19:17
Created time
2024/08/31 10:17
category
unix

개요

컨테이너 환경 구축을 위해서 새로운 루트로의 전환은 이뤄냈지만, 여전히 시스템 상의 자원이 새로 전환된 루트에서 접근이 가능
시스템의 6가지 네임스페이스 자원들을 격리하여, 아래처럼 시스템에 영향을 끼칠 수 있는 문제를 해결
컨테이너임에도 시스템의 루트 권한을 가짐
컨테이너가 호스트의 포트를 그대로 이용
컨테이너가 호스트의 프로세스를 포함

네임스페이스

특징

네임스페이스는 프로세스에 격리된 환경을 커널의 기능으로 제공
자식 프로세스는 부모 프로세스의 네임스페이스를 그대로 상속
모든 프로세스는 타입 별 네임스페이스가 존재 → /proc/<pid>/ns

종류

mount
uts
ipc
net
pid
user
cgroup
time

관련 명령어

# 현재 프로세스의 pid echo $$ # 현재 프로세스의 네임스페이스 조회 ls -al /proc/$$/ns # 심볼릭 링크 조회 readlink /proc/$$/ns/<type> # https://man7.org/linux/man-pages/man8/lsns.8.html # pid와 타입으로 네임스페이스 목록 조회 lsns -t <type> -p <pid>
Shell
복사

격리

mount

# 마운트 네임스페이스 격리 unshare -m # 컨테이너 환경의 네임스페이스 확인 lsns -p $$ # 격리 환경 종료 exit # 호스트 환경의 네임스페이스 확인 lsns -p $$
Shell
복사
격리된 마운트 네임스페이스만 pid가 다르고, 나머지 네임스페이스는 1번 프로세스의 네임스페이스를 이용

uts

# uts 네임스페이스 격리 unshare -u # 컨테이너 환경의 네임스페이스 확인 lsns -p $$ # 격리 환경 종료 exit # 호스트 환경의 네임스페이스 확인 lsns -p $$
Shell
복사
격리된 uts 네임스페이스만 pid가 다르고, 나머지 네임스페이스는 1번 프로세스의 네임스페이스를 이용

ipc

# ipc 네임스페이스 격리 unshare -i # 컨테이너 환경의 네임스페이스 확인 lsns -p $$ # 격리 환경 종료 exit # 호스트 환경의 네임스페이스 확인 lsns -p $$
Shell
복사
격리된 ipc 네임스페이스만 pid가 다르고, 나머지 네임스페이스는 1번 프로세스의 네임스페이스를 이용

pid

호스트 네임스페이스에서는 컨테이너 네임스페이스의 pid 조회 가능
컨테이너 네임스페이스는 컨테이너 환경에서 격리된 pid가 있고, 외부적으로 호스트에서 사용하는 pid가 존재

PID 1

pid 0번은 커널 프로세스 (커널 레이어)인데 커널이 만든 pid 1번 프로세스가 최상위 프로세스 (유저 레이어)가 되고, 나머지 프로세스들일 1번의 자식 프로세스

동작 방식

pid의 격리는 다른 네임스페이스들과 달리 unshare 호출 시점에 즉시 격리되는 것이 아니라, fork 과정을 거치게 됨
unshare의 인자로 실행할 명령어를 기입할 수 있었는데, -f 옵션을 통해 unshare에서 해당 명령어로의 fork가 이뤄지면서 격리된 네임스페이스에 1번 pid를 차지하게 됨
격리된 1번 프로세스는 시그널이나 좀비 및 고아 프로세스 처리를 하고, 해당 프로세스가 죽으면 컨테이너를 종료하는 식으로 동작
1번 프로세스는 컨테이너의 라이프사이클과 밀접한 관계

격리 확인

# pid 격리 # -f는 fork # -p는 pid # --mount-proc은 proc 파일 시스템을 마운트 unshare -fp --mount-proc /bin/sh # 컨테이너 내외부에서 확인 ps -ef | grep /bin/sh # 컨테이너 내부에서의 네임스페이스 확인 lsns -t pid -p 1 # 호스트에서의 네임스페이스 확인 lsns -t pid -p <container-pid> # 호스트에서 container를 죽이면, 격리 공간이 사라짐 kill -9 <container-pid>
Shell
복사

net

네트워크 네임스페이스를 생성하여 가상 인터페이스를 이용
네임스페이스를 삭제 시 가상 인터페이스도 삭제

네트워크 셋업

# veth0, veth1을 양 끝으로 갖는 veth 생성 ip link add veth0 type veth peer name veth1 # ip link로 생성된 veth 인터페이스를 확인 (veth1@veth0 / veth0@veth1) ip link # RED, BLUE 네임스페이스 생성 ip netns add RED ip netns add BLUE # RED-veth0 / BLUE-veth1 연결 ip link set veth0 netns RED ip link set veth1 netns BLUE # RED, BLUE 네임스페이스의 장치 활성화 ip netns exec RED ip link set veth0 up ip netns exec BLUE ip link set veth1 up # RED, BLUE 네임스페이스의 IP 주소 추가 ip netns exec RED ip addr add 11.11.11.2/24 dev veth0 ip netns exec BLUE ip addr add 11.11.11.3/24 dev veth1
Shell
복사

네임스페이스 생성 확인

tree /var/run/netns
Shell
복사
네트워크 네임스페이스 생성 시 /var/run/netns에 파일이 생성

통신 확인

# 각각의 터미널에서 네트워크 네임스페이스 접속 nsenter --net=/var/run/netns/<namespace> # 네트워크 inode 값 상이함을 확인 lsns -t net -p $$ # 각각의 IP 주소 및 인터페이스 확인 ip a # 라우팅 테이블을 확인하여 각자의 IP 주소로부터 디폴트로 나가는 것을 확인 ip route # 다른 네임스페이스로 핑을 확인 ping <other-namespace-ip-address>
Shell
복사
좌측 RED 접속, 우측 BLUE 접속하여 통신 테스트

네임스페이스 및 링크 삭제

# 네임스페이스 삭제 ip netns del RED ip netns del BLUE # netns 삭제 확인 tree /var/run/netns # 링크 삭제 확인 (네임스페이스 삭제 시 자동 정리) ip link del veth0
Shell
복사

user

UID/GID를 Remap으로 격리
컨테이너가 시스템의 루트 권한을 갖는 문제 해결

도커로 확인

# for docker) 실행 중인 프로세스 목록에서 /bin/sh가 존재하지 않음 확인 ps -ef | grep /bin/sh # for docker) ubuntu 사용자로 ubuntu 컨테이너 생성 및 접속 docker run -it ubuntu /bin/sh # for docker) 컨테이너에서 사용자 확인, root id # for ubuntu) 호스트에서 사용자 확인, ubuntu id # for ubuntu) 실행 중인 프로세스 목록에서 호스트와 컨테이너 각각의 /bin/sh 존재 확인 # 같은 프로세스지만 격리되어 2개로 표시 # 각 프로세스의 사용자가 다른데 컨테이너에서 실행된 root가 시스템의 루트인지 확인 필요 ps -ef | grep /bin/sh # for docker) 네임스페이스의 심볼릭 링크를 통해 네임스페이스 inode 확인 readlink /proc/$$/ns/user # for ubuntu) 네임스페이스의 심볼릭 링크를 통해 네임스페이스 inode 확인 readlink /proc/$$/ns/user
Shell
복사
도커 컨테이너에서 실행된 root라는 사용자가 시스템의 root와 동일한 사용자라는 사실을 알 수 있음 (따라서 UID/GID Remap이 되도록 별도 설정이 필요)

격리 확인

# for unshare) 실행 중인 프로세스 목록에서 /bin/sh가 존재하지 않음 확인 ps -ef | grep /bin/sh # for unshare) 사용자 격리 # --map-root-user는 --map-user=0 --map-group=0와 동일 unshare -U --map-root-user /bin/sh # for unshare) 컨테이너에서 사용자 확인, root id # for ubuntu) 호스트에서 사용자 확인, ubuntu id # for ubuntu) 실행 중인 프로세스 목록에서 /bin/sh 존재 확인 # docker와 달리 pid 격리되지 않아 1개로 보임 ps -ef | grep /bin/sh # for unshare) 네임스페이스의 심볼릭 링크를 통해 네임스페이스 inode 확인 readlink /proc/$$/ns/user # for ubuntu) 네임스페이스의 심볼릭 링크를 통해 네임스페이스 inode 확인 readlink /proc/$$/ns/user
Shell
복사
도커 컨테이너로 실행한 것과 다르게 사용자 네임스페이스의 inode 값이 다름 (컨테이너 안에서의 root는 UID/GID의 Remap으로 시스템 상에서의 root와 다름)

cgroup

컨테이너 별로 자원을 분배하여 정해진 만큼의 기준으로 운용
v1은 request 및 limit으로만 동작하지만, v2는 qos 지정 가능
자원의 할당과 제한 등 관리 기능을 파일 시스템으로 제공 (/sys/fs/cgroup)

패키지 설치

# 루트 사용자 전환 sudo -i # cgroup 관련 도구와 스트레스 도구 설치 apt install -y cgroup-tools stress
Shell
복사

cgroup 생성

# https://linux.die.net/man/1/cgcreate # https://docs.redhat.com/ko/documentation/red_hat_enterprise_linux/6/html/resource_management_guide/sec-creating_cgroups#sec-Creating_Cgroups # cgcreate -a <owner> -g <subsystem>:<path> cgcreate -a root -g cpu:jseo # 컨트롤 그룹 조회 tree /sys/fs/cgroup/jseo
Shell
복사

자원 제한 및 스트레스

# https://linux.die.net/man/1/cgset # cpu 사용률 42% 제한 cgset -r cpu.max=42000 jseo # https://linux.die.net/man/1/cgexec # 컨트롤 그룩으로 stress 실행 cgexec -g cpu:jseo stress -c 1 # 다른 터미널에서 cpu 부하 확인 htop
Shell
복사

결론

컨테이너의 리소스를 격리하는 방식을 탐구했는데, 이전에 실습한 루트 디렉토리 격리와 루트 디렉토리를 오버레이 파일 시스템으로 구성하는 것까지 종합하여 도커 없이 컨테이너를 구성할 수 있음