2.2.1 네임스페이스 (Namespaces)
앞서 컨테이너의 핵심 기술 요소들을 간략히 살펴보았는데요, 이제 그 첫 번째 주자인 네임스페이스(Namespaces)에 대해 좀 더 깊이 알아보겠습니다. 네임스페이스는 컨테이너 기술의 가장 근본적인 격리(Isolation) 기능을 제공하는 리눅스 커널 기능입니다. 마치 한 사무실 공간을 파티션으로 나누어 여러 팀이 독립적으로 일할 수 있도록 하는 것처럼, 네임스페이스는 단일 운영체제 환경 내에서 프로세스들이 서로 격리된 환경을 갖도록 만들어 줍니다. 이것이 가능한 이유는, 네임스페이스가 특정 프로세스에게 시스템의 전역 자원(Global System Resources)을 마치 자신만이 사용하는 것처럼 보이도록 ‘뷰(view)’를 제한하기 때문입니다. 즉, 컨테이너 내의 프로세스는 호스트 시스템 전체가 아닌, 자신에게 할당된 네임스페이스 범위 내의 자원만을 인지하고 접근하게 됩니다. 이 덕분에 여러 컨테이너가 동일한 호스트 머신 위에서 서로 간섭 없이, 마치 별도의 시스템에서 실행되는 것처럼 작동할 수 있는 것입니다. 쿠버네티스에서 파드(Pod) 내의 컨테이너들이 격리되어 실행될 수 있는 것도 바로 이 네임스페이스 기술 덕분입니다.

2.2.1.1 PID, Network, Mount, UTS, IPC, User 네임스페이스
리눅스 커널이 제공하는 네임스페이스 기능은 컨테이너 격리의 핵심적인 기반 기술입니다. 이는 마치 물리적인 공간을 나누지 않고도 논리적인 칸막이를 세워 각자의 독립된 영역을 보장하는 것과 같습니다. 시스템의 다양한 자원들을 대상으로 각각 다른 종류의 네임스페이스가 존재하며, 이를 조합하여 컨테이너라는 독립적인 실행 환경을 구축하게 됩니다. 이제 컨테이너 기술을 이해하는 데 필수적인 주요 네임스페이스들에 대해 하나씩 자세히 살펴보겠습니다.
-
PID 네임스페이스 (Process ID Namespace): 프로세스 세계의 격리
가장 먼저 살펴볼 것은 프로세스 ID, 즉 PID를 격리하는 PID 네임스페이스입니다. 우리가 컴퓨터를 사용할 때 실행되는 모든 프로그램은 고유한 번호인 PID를 부여받아 운영체제에 의해 관리됩니다. PID 네임스페이스는 바로 이 PID의 할당과 가시성(visibility)에 대한 독립적인 공간을 제공합니다.
새로운 PID 네임스페이스가 생성되면, 그 안에서 처음으로 실행되는 프로세스는 특별한 의미를 갖는 PID 1번을 할당받습니다. 전통적인 리눅스 시스템에서 PID 1번은 시스템 전체의 부팅 과정을 책임지고 다른 모든 프로세스를 관리하는 ‘init’ 프로세스의 역할입니다. 마찬가지로, PID 네임스페이스 내의 PID 1번 프로세스는 해당 네임스페이스 내부의 ‘init’ 역할을 수행하며, 네임스페이스 내에서 생성된 다른 자식 프로세스들을 관리하고, 고아(orphan)가 된 프로세스들을 거두는 등의 책임을 지게 됩니다. 만약 이 PID 1번 프로세스가 예기치 않게 종료되면, 커널은 해당 네임스페이스 내의 다른 모든 프로세스에게 종료 신호(SIGKILL)를 보내게 됩니다.
PID 네임스페이스의 가장 중요한 효과는 프로세스 가시성의 격리입니다. 컨테이너 내부에서 ps나 top 같은 명령어를 실행하면, 오직 해당 컨테이너(즉, 해당 PID 네임스페이스) 내에서 실행 중인 프로세스들만 보이게 됩니다. 호스트 시스템의 다른 프로세스나, 심지어 같은 호스트에서 실행 중인 다른 컨테이너의 프로세스들은 보이지 않습니다. 이는 컨테이너에게 마치 자신만의 독립된 운영체제 환경을 가지고 있는 듯한 착각을 불러일으키며, 프로세스 관리 측면에서의 완벽한 격리감을 제공합니다. 또한, 각 컨테이너가 자신만의 PID 1번 프로세스를 가짐으로써, 프로세스 간의 시그널 처리나 라이프사이클 관리가 외부의 간섭 없이 독립적으로 이루어질 수 있습니다.
-
Network 네임스페이스 (Network Namespace): 독립적인 네트워크 환경 구축
다음은 네트워크 네임스페이스입니다. 이름에서 알 수 있듯이, 네트워크와 관련된 모든 시스템 자원을 격리하는 역할을 합니다. 여기에는 네트워크 인터페이스(예: eth0, lo), IP 주소, 라우팅 테이블, 포트 번호 공간, 방화벽 규칙(iptables/nftables) 등 네트워크 통신에 필요한 거의 모든 요소가 포함됩니다.
새로운 네트워크 네임스페이스가 생성되면, 마치 새 컴퓨터에 네트워크 카드를 꽂고 처음부터 네트워크 설정을 하는 것처럼, 완전히 비어있고 독립적인 네트워크 스택(Network Stack)을 갖게 됩니다. 기본적으로는 외부와 통신할 수 있는 방법이 없는 고립된 상태입니다. 컨테이너가 외부 네트워크나 다른 컨테이너와 통신하기 위해서는 추가적인 설정이 필요합니다. 가장 흔하게 사용되는 방법은 가상 이더넷 장치 쌍(veth pair)을 이용하는 것입니다. veth pair는 터널처럼 작동하는 두 개의 가상 네트워크 인터페이스로 구성됩니다. 하나는 컨테이너의 네트워크 네임스페이스 내부에 위치시키고(예: eth0), 다른 하나는 호스트 시스템의 네임스페이스에 두어 가상 브릿지(예: docker0 또는 CNI에서 관리하는 브릿지)에 연결합니다. 이를 통해 컨테이너 내부의 트래픽이 veth pair를 통해 호스트의 브릿지로 전달되고, 호스트의 라우팅 및 NAT 설정을 통해 외부와 통신할 수 있게 됩니다.
네트워크 네임스페이스 덕분에 각 컨테이너(또는 쿠버네티스의 파드)는 자신만의 고유한 IP 주소를 가질 수 있습니다. 더 중요한 것은 포트 번호 공간의 격리입니다. 예를 들어, 호스트 시스템에서는 오직 하나의 프로세스만 80번 포트를 사용할 수 있지만, 네트워크 네임스페이스를 사용하면 여러 컨테이너가 각자 자신의 네임스페이스 내에서 80번 포트를 사용하여 웹 서버를 구동할 수 있습니다. 이는 애플리케이션 배포의 유연성을 크게 향상시킵니다. 쿠버네티스에서 각 파드가 고유한 IP를 할당받고, 파드 내의 컨테이너들이 포트를 공유하거나 독립적으로 사용하는 모델은 바로 이 네트워크 네임스페이스 기술에 기반하고 있습니다.
-
Mount 네임스페이스 (Mount Namespace): 파일시스템 뷰의 분리
마운트 네임스페이스는 파일시스템의 구조, 즉 마운트 포인트들의 목록을 격리합니다. 각 마운트 네임스페이스는 자신만의 독립적인 마운트 트리(mount tree)를 가집니다. 이것이 의미하는 바는, 한 네임스페이스 안에서 특정 디렉토리에 장치나 다른 파일시스템을 마운트하거나 언마운트하는 작업이 다른 네임스페이스에는 전혀 영향을 미치지 않는다는 것입니다.
컨테이너 기술에서 마운트 네임스페이스의 가장 중요한 역할은 각 컨테이너에게 고유한 루트 파일시스템(‘/’) 뷰를 제공하는 것입니다. 컨테이너가 시작될 때, 컨테이너 런타임(예: containerd, CRI-O)은 새로운 마운트 네임스페이스를 생성하고, 해당 컨테이너 이미지의 파일 시스템 레이어들을 이 네임스페이스의 루트 디렉토리(‘/’)로 마운트합니다. 이때 종종 OverlayFS와 같은 유니온 파일시스템(Union Filesystem) 기술이 사용됩니다. OverlayFS는 여러 개의 읽기 전용 레이어(이미지 레이어) 위에 쓰기 가능한 레이어(컨테이너 레이어)를 겹쳐서 하나의 통합된 파일시스템 뷰를 제공합니다. 이를 통해 컨테이너 내부에서는 마치 일반적인 파일시스템처럼 파일을 읽고 쓸 수 있지만, 실제 변경 사항은 최상위 쓰기 가능 레이어에만 기록되므로 원본 이미지는 변경되지 않고 유지됩니다.
또한, 마운트 네임스페이스는 과거 유닉스 시스템에서 사용되던 chroot 명령어보다 훨씬 강력하고 안전한 격리 기능을 제공합니다. chroot는 단순히 루트 디렉토리의 위치만 변경할 뿐, 다른 시스템 자원(PID, 네트워크 등)에 대한 격리는 제공하지 않으며 보안적인 우회 방법도 존재했습니다. 반면, 마운트 네임스페이스는 다른 네임스페이스들과 결합하여 훨씬 완전한 형태의 파일시스템 격리를 달성합니다. 컨테이너 내부에 필요한 라이브러리, 설정 파일, 애플리케이션 바이너리 등을 포함하는 독립적인 파일시스템 환경을 제공함으로써, 애플리케이션 실행 환경의 일관성과 이식성을 보장하는 핵심적인 역할을 수행합니다.
-
UTS 네임스페이스 (UNIX Timesharing System Namespace): 호스트 이름과 도메인 이름의 격리
UTS 네임스페이스는 시스템의 호스트 이름(Hostname)과 NIS 도메인 이름(Domain Name)이라는 두 가지 식별자를 격리합니다. UTS는 UNIX Timesharing System의 약자로, 초기 유닉스 시스템의 식별 관련 정보를 가리킵니다.
이 네임스페이스를 사용하면 각 컨테이너는 호스트 시스템과는 다른, 자신만의 고유한 호스트 이름을 가질 수 있습니다. 컨테이너 내부에서 hostname 명령어를 실행하거나 관련 시스템 콜(예: gethostname())을 호출하면, 호스트 시스템의 이름이 아닌 해당 컨테이너에 지정된 이름이 반환됩니다. 예를 들어, 쿠버네티스에서는 파드(Pod)의 이름이 종종 컨테이너의 호스트 이름으로 설정됩니다.
호스트 이름을 격리하는 것은 단순히 식별의 편의성을 넘어서는 의미를 갖습니다. 많은 애플리케이션이나 서비스들은 로그 메시지에 호스트 이름을 포함시키거나, 클러스터 환경에서 자기 자신을 식별하는 데 호스트 이름을 사용합니다. 만약 모든 컨테이너가 동일한 호스트 이름을 공유한다면, 로그 분석이나 서비스 디스커버리에 혼란이 발생할 수 있습니다. UTS 네임스페이스는 이러한 충돌을 방지하고 각 컨테이너가 네트워크상에서 명확하게 자신을 식별할 수 있도록 돕습니다. NIS 도메인 이름은 현재는 널리 사용되지 않지만, UTS 네임스페이스는 이 역시 격리 대상으로 포함합니다.
-
IPC 네임스페이스 (Inter-Process Communication Namespace): 프로세스 간 통신 채널의 분리
IPC 네임스페이스는 프로세스들이 서로 데이터를 교환하거나 작업을 동기화하기 위해 사용하는 프로세스 간 통신(Inter-Process Communication, IPC) 메커니즘과 관련된 자원들을 격리합니다. 구체적으로는 System V IPC 객체(세마포어 세트, 메시지 큐, 공유 메모리 세그먼트)와 POSIX IPC 객체(메시지 큐, 세마포어, 공유 메모리)가 격리 대상입니다.
이러한 IPC 객체들은 종종 시스템 전역적으로 고유한 식별자(키(key) 또는 이름(name))를 사용하여 생성되고 참조됩니다. 만약 IPC 네임스페이스가 없다면, 서로 다른 컨테이너에서 실행되는 애플리케이션들이 우연히 동일한 IPC 키나 이름을 사용하려고 할 때 충돌이 발생하여 예기치 않은 오류나 오작동을 일으킬 수 있습니다. 예를 들어, 두 개의 데이터베이스 인스턴스가 서로 다른 컨테이너에서 실행되면서 동일한 키 값으로 공유 메모리 세그먼트를 생성하려고 시도한다면, 네임스페이스 격리가 없을 경우 문제가 발생할 것입니다.
IPC 네임스페이스는 각 컨테이너에게 독립적인 IPC 식별자 공간을 제공함으로써 이러한 충돌을 원천적으로 방지합니다. 즉, 컨테이너 A에서 생성한 IPC 객체는 컨테이너 B에서는 보이지 않으며, 두 컨테이너가 동일한 식별자를 사용하더라도 서로 간섭하지 않습니다. 이는 특히 데이터베이스, 메시징 시스템, 고성능 컴퓨팅(HPC) 애플리케이션 등 내부적으로 IPC 메커니즘을 활발하게 사용하는 소프트웨어를 컨테이너 환경에서 안정적으로 운영하는 데 매우 중요합니다. 쿠버네티스에서는 기본적으로 파드 내의 컨테이너들은 IPC 네임스페이스를 공유하도록 설정될 수 있어(선택 사항), 같은 파드 내의 컨테이너들끼리는 IPC를 통해 효율적으로 통신할 수 있도록 지원하기도 합니다.
-
User 네임스페이스 (User Namespace): 사용자 및 그룹 ID의 가상화와 보안 강화
마지막으로 살펴볼 User 네임스페이스는 사용자 식별자(UID)와 그룹 식별자(GID)의 범위를 격리하는, 특히 보안 측면에서 매우 중요한 네임스페이스입니다. User 네임스페이스의 핵심 아이디어는 UID/GID 매핑(mapping)입니다. 이를 통해 컨테이너 내부에서의 사용자/그룹 ID와 호스트 시스템에서의 사용자/그룹 ID를 분리하여 서로 다른 의미를 갖도록 할 수 있습니다.
가장 대표적인 활용 사례는 컨테이너 내부의 root 사용자(UID 0)를 호스트 시스템의 권한 없는 일반 사용자(non-privileged user, 예: UID 100000)로 매핑하는 것입니다. 이렇게 설정하면, 컨테이너 내부에서는 프로세스가 UID 0으로 실행되어 root 권한을 가진 것처럼 보입니다. 예를 들어, 패키지를 설치하거나 시스템 설정을 변경하는 등의 작업을 수행할 수 있습니다(물론 다른 네임스페이스와 Capability 제한에 의해 실제 권한은 제한될 수 있습니다). 하지만 이 프로세스가 컨테이너 외부의 자원(예: 호스트 파일시스템의 특정 파일)에 접근하려고 할 때, 커널은 이 프로세스의 실제 호스트 UID(예: 100000)를 기준으로 권한을 검사합니다. 만약 해당 호스트 UID가 접근 권한이 없다면 작업은 실패하게 됩니다.
이는 컨테이너 보안에 혁신적인 발전을 가져왔습니다. 만약 공격자가 컨테이너 내부의 애플리케이션 취약점을 이용하여 root 권한을 획득하더라도, User 네임스페이스 매핑 때문에 호스트 시스템에서는 여전히 제한된 권한만을 가지게 됩니다. 즉, 컨테이너 탈출(Container Escape) 공격을 통해 호스트 시스템 전체를 장악하는 것을 매우 어렵게 만듭니다. 이러한 기능을 활용하여 호스트 시스템에 root 권한 없이도 컨테이너를 생성하고 실행할 수 있게 하는 기술을 루트리스 컨테이너(Rootless Containers)라고 부르며, 보안이 중요한 환경에서 점점 더 많이 채택되고 있습니다. User 네임스페이스는 리눅스 Capabilities와 함께 컨테이너의 권한을 세밀하게 제어하고 최소 권한 원칙(Principle of Least Privilege)을 컨테이너 환경에 적용하는 데 핵심적인 역할을 수행합니다.
이처럼 리눅스 커널이 제공하는 다양한 네임스페이스들은 각각 시스템 자원의 특정 측면을 격리함으로써, 컨테이너가 마치 독립적인 시스템처럼 작동하면서도 호스트 시스템의 자원을 효율적으로 공유할 수 있도록 만들어 줍니다. 이들의 조합과 상호작용을 통해 우리는 가볍고 빠르며 이식성 높은 컨테이너 기술의 이점을 누릴 수 있는 것입니다.
2.2.1.2 네임스페이스를 통한 리소스 격리 원리
지금까지 PID, 네트워크, 마운트 등 다양한 종류의 네임스페이스에 대해 살펴보았습니다. 그렇다면 이 네임스페이스들이 구체적으로 어떤 원리로 시스템 자원을 격리하여 컨테이너라는 독립적인 환경을 만들어내는 것일까요? 그 작동 방식의 핵심을 이해하는 것은 컨테이너 기술의 본질을 파악하는 데 매우 중요합니다.
네임스페이스의 가장 핵심적인 원리는 물리적인 자원을 실제로 복제하거나 분할하는 것이 아니라는 점입니다. 대신, 각 프로세스가 시스템의 전역 자원(Global System Resources)을 인지하고 접근하는 ‘관점(Perspective)’ 또는 ‘시야(View)’를 제한하고 변경하는 방식으로 작동합니다. 마치 가상현실(VR) 헤드셋을 쓰면 실제 공간과는 다른 가상의 환경이 보이는 것처럼, 네임스페이스는 프로세스에게 특정한 ‘가상 환경 뷰’를 제공하는 것입니다.
이것이 어떻게 가능할까요? 리눅스 커널 내부의 작동 방식을 좀 더 들여다볼 필요가 있습니다.
- 프로세스와 네임스페이스의 연결: 리눅스에서 실행되는 모든 프로세스는 내부적으로 자신의 실행 상태와 정보를 담고 있는 task_struct라는 자료 구조를 가집니다. 이 task_struct 안에는 해당 프로세스가 어떤 네임스페이스들에 속해 있는지를 가리키는 포인터(주로 nsproxy라는 또 다른 구조체를 통해 관리됨)가 포함되어 있습니다. 즉, 각 프로세스는 자신이 소속된 PID 네임스페이스, 네트워크 네임스페이스, 마운트 네임스페이스 등의 ‘멤버십 카드’를 가지고 있는 셈입니다. 프로세스가 생성될 때, 부모 프로세스로부터 네임스페이스를 상속받거나, clone() 또는 unshare() 시스템 콜을 통해 새로운 네임스페이스를 생성하고 그곳에 속하게 될 수 있습니다.
- 시스템 콜과 커널의 역할: 프로세스가 운영체제의 기능(예: 파일 열기, 네트워크 소켓 생성, 프로세스 목록 보기 등)을 사용하려면 반드시 시스템 콜(System Call)이라는 인터페이스를 통해 커널에게 요청해야 합니다. 중요한 점은, 커널이 이러한 시스템 콜 요청을 처리할 때, 요청을 보낸 프로세스가 어떤 네임스페이스에 속해 있는지를 반드시 확인한다는 것입니다. 커널 코드는 네임스페이스를 인지하도록 설계되어 있습니다(Namespace-aware).
- ‘관점’에 따른 자원 필터링 및 해석: 커널은 시스템 콜을 처리하면서, 해당 프로세스가 속한 네임스페이스의 ‘맥락(context)’ 안에서 자원을 보여주거나 작업을 수행합니다.
- 예를 들어, 어떤 프로세스가 자신의 PID를 얻기 위해 getpid() 시스템 콜을 호출한다고 가정해 봅시다. 커널은 이 프로세스가 속한 PID 네임스페이스를 확인하고, 해당 네임스페이스 내에서 부여된 지역적인(local) PID 값을 반환합니다. 만약 이 프로세스가 네임스페이스 내의 첫 번째 프로세스라면, getpid()는 1을 반환할 것입니다. 하지만 호스트 시스템 관점에서 보면 이 프로세스는 전혀 다른 (더 큰) PID 값을 가지고 있을 수 있습니다.
- 마찬가지로, 프로세스가 ps 명령어를 실행하여 프로세스 목록을 보려고 할 때(내부적으로 readdir() 등의 시스템 콜 사용), 커널은 해당 프로세스의 PID 네임스페이스를 기준으로, 그 네임스페이스 내에 존재하는 프로세스들만 필터링하여 정보를 제공합니다.
- 프로세스가 ip addr 명령어를 통해 네트워크 인터페이스 정보를 요청하면(내부적으로 netlink 소켓 관련 시스템 콜 사용), 커널은 프로세스의 네트워크 네임스페이스에 할당된 인터페이스와 IP 주소 정보만을 반환합니다.
- 파일시스템 마운트(mount() 시스템 콜) 역시 마찬가지입니다. 특정 마운트 네임스페이스 내에서 수행된 마운트 작업은 해당 네임스페이스의 독립적인 마운트 테이블에만 영향을 미치며, 다른 네임스페이스에서는 이 변경 사항이 보이지 않습니다.
- ‘색안경’ 비유의 확장: 앞서 ‘다른 색깔의 안경’ 비유를 들었습니다. 이를 좀 더 구체화하면, 모든 프로세스는 기본적으로 동일한 ‘풍경'(즉, 공유된 리눅스 커널과 하드웨어 자원) 위에 존재합니다. 하지만 각 프로세스는 자신에게 할당된 ‘네임스페이스 안경 세트'(PID 안경, 네트워크 안경, 마운트 안경 등)를 통해 세상을 봅니다. 이 안경들은 커널이 제공하는 풍경의 특정 부분을 걸러내거나 다르게 해석하여 보여줍니다. 따라서 각 프로세스는 자신만의 고유한 환경에서 작동하는 것처럼 느끼게 됩니다.
이러한 ‘관점의 제한’ 방식은 네임스페이스 기술이 매우 효율적인 이유를 설명해 줍니다. 가상 머신(VM) 기술은 각 VM마다 독립적인 운영체제 커널과 가상 하드웨어를 에뮬레이션해야 하므로 상당한 메모리와 CPU 자원을 소모하고 부팅 시간도 오래 걸립니다. 반면, 네임스페이스 기반의 컨테이너는 호스트 운영체제의 커널을 모든 컨테이너가 공유합니다. 네임스페이스는 커널 자체를 복제하는 것이 아니라 커널의 데이터 구조 내에서 논리적인 구획을 나누는 방식이므로, 추가적인 오버헤드가 매우 적습니다. 이것이 컨테이너가 VM에 비해 훨씬 가볍고(low overhead), 시작 속도가 거의 즉각적이며(fast startup), 동일한 하드웨어에서 훨씬 더 많은 수의 컨테이너를 실행할 수 있는(high density) 결정적인 이유입니다.
쿠버네티스는 바로 이러한 리눅스 네임스페이스의 강력한 격리 기능을 기반으로 동작합니다. 쿠버네티스의 기본 배포 단위인 파드(Pod)는 하나 이상의 컨테이너 그룹으로 구성되는데, 파드는 일반적으로 자신만의 고유한 네트워크 네임스페이스와 IPC 네임스페이스를 공유합니다(파드 내 컨테이너 간 통신 용이). 반면, 각 컨테이너는 별도의 마운트 네임스페이스를 가질 수 있고, 필요에 따라 PID 네임스페이스나 User 네임스페이스도 분리하거나 공유하도록 설정할 수 있습니다. 쿠버네티스는 컨테이너 런타임(CRI 호환 런타임, 예: containerd, CRI-O)에게 이러한 네임스페이스 설정을 지시하여 파드와 컨테이너의 격리 환경을 구성하고 관리합니다.
결론적으로, 네임스페이스는 리눅스 커널이 제공하는 정교한 ‘뷰 필터링’ 메커니즘입니다. 이 원리를 이해하는 것은 컨테이너가 어떻게 가볍고 효율적인 격리를 달성하는지, 그리고 쿠버네티스가 어떻게 이러한 컨테이너들을 효과적으로 오케스트레이션하는지에 대한 근본적인 통찰력을 제공합니다. 클라우드 네이티브 환경을 구축하고 운영하는 데 있어 이 기초적인 이해는 매우 중요하다고 할 수 있습니다.