사전 네임스페이스
개념

네임스페이스

gabury1고친 사람 github-actions[bot]

네임스페이스는 이름이 통하는 구역을 갈라 놓습니다. 구역이 다르면 같은 이름을 써도 서로 부딪히지 않습니다. 이름 하나가 무엇을 가리키는지는 그 이름이 놓인 구역이 정합니다.

쉽고 빠른 이해
  • 무슨 일을 하나 — 이름을 담는 통을 여러 개로 가릅니다. 한 프로그램 안에서 log라는 이름 하나가 구역에 따라 다른 함수를 가리키게 되는 것이 그 일입니다.
  • 왜 있나 — 통이 하나뿐이면 남이 먼저 쓴 이름을 못 쓰고, 따로 만든 두 덩이를 합칠 때 같은 이름이 맞부딪힙니다.
  • 어떻게 도나
    1. 이름을 담을 구역을 만듭니다.
    2. 이름을 그 구역에 등록합니다.
    3. 이름을 쓸 때는 어느 구역에서 찾을지 정하고, 그 구역 안에서만 찾습니다.
  • 대가 — 구역 밖의 이름을 가리키려면 구역 이름까지 앞에 붙여야 해서 이름이 길어집니다. 구역이 하나뿐인 작은 프로그램이라면 이름만 길어지고 얻는 것이 없습니다.

상세

한 아파트 단지에 동이 여럿 있고 동마다 101호가 있습니다. 같은 동 안에서는 "101호"라고만 해도 통하지만, 다른 동 사람에게 말하려면 동 번호를 같이 대야 합니다. 구역이 다른 두 이름은 글자만 같을 뿐, 가리키는 것 사이에 아무 관계가 없습니다.

네임스페이스는 이름에서 대상을 찾는 표를 구역마다 따로 두는 장치입니다. 함수 하나가 실행되는 동안 쓰는 지역 변수 이름 한 벌이 그런 표입니다. 그 함수가 끝나면 그 표도 같이 사라집니다. 옆 함수의 같은 이름과는 처음부터 남남이었습니다.

이름을 대상으로 바꾸는 일을 이름 해석이라고 부릅니다. 이름 해석은 언제나 어떤 구역 하나를 놓고 벌어집니다. 그래서 같은 글자를 읽어도 어느 구역에서 읽었느냐에 따라 다른 것이 나옵니다.

flowchart TD
    N1["이름 log"]
    N2["이름 log"]
    subgraph 구역A["구역 A"]
        N1 --> T1["기록을 남기는 함수"]
    end
    subgraph 구역B["구역 B"]
        N2 --> T2["밑이 10인 로그를 구하는 함수"]
    end

두 이름을 한 통에 두면 하나가 다른 하나를 덮어 쓰지만, 구역이 갈려 있으면 둘 다 제 이름으로 남습니다.

규칙은 둘입니다. 한 구역 안에서 이름은 겹치면 안 됩니다. 그리고 구역이 다르면 같은 이름이 여럿 있어도 됩니다.

구역 안에서는 짧은 이름만으로 통하고, 밖을 가리킬 때는 구역 이름을 앞에 붙인 긴 이름을 씁니다. 짧은 이름은 읽는 쪽이 이미 고른 구역에서 풀리고, 긴 이름은 이름에 적힌 구역에서 풀립니다.

flowchart TD
    N["글에 쓰인 이름"]
    N --> Q{"앞에 구역 이름이 붙었나"}
    Q -- "안 붙었다 · log" --> S["읽는 쪽이 고른 구역에서 찾는다"]
    Q -- "붙었다 · 구역B의 log" --> L["이름에 적힌 구역에서 찾는다"]
    S --> T["대상"]
    L --> T

짧은 이름을 어느 구역에서 풀지 정해 주는 코드 구간을 스코프라고 부릅니다. 함수 본문 안에서 x라고만 써도 그 함수의 지역 이름 표를 뒤지게 되는데, 그 함수 본문이 스코프이고 뒤지는 표가 네임스페이스입니다. 둘은 겹쳐서 도는 짝이지 서로를 대신하지 않습니다.

이름이 길어지는 것이 이 장치의 대가입니다. 그리고 어느 구역에서 찾을지를 정해 주는 쪽이 따로 있어야 합니다. 그 쪽이 누구인지가 갈래마다 다릅니다. 구역이 하나뿐인 작은 프로그램이면 이 장치는 이름만 길게 만들고 끝납니다.

배경

이름을 한 통에만 담으면 먼저 쓴 사람이 그 이름을 가져갑니다. 뒤에 오는 사람은 같은 이름을 못 쓰고 다른 이름을 지어야 합니다. 더 아픈 것은 합칠 때입니다. 따로 자란 두 덩이를 한 프로그램에 넣으면, 양쪽이 각자 Config라는 이름을 갖고 있다는 것을 그제야 알게 됩니다.

버티는 방법이 먼저 있었습니다. 이름 앞에 만든 곳을 나타내는 짧은 글자를 손으로 붙이는 것입니다. 이 방법은 규율일 뿐이라 누가 안 지키면 그대로 깨집니다. 붙이는 글자끼리 또 겹치기도 합니다.

그래서 이름을 담는 구역을 언어나 시스템이 직접 나눠 주고, 이름 해석이 그 구역 안에서만 일어나게 하는 장치가 필요해졌습니다. 그 구역에 붙은 이름이 네임스페이스입니다.

운영체제 쪽에서 "프로세스마다 다른 구역"이라는 갈래를 처음 세운 것은 벨 연구소의 Plan 9 논문입니다. 이 시스템의 바탕은 두 가지 생각, 곧 프로세스마다 다른 네임스페이스와 간단한 메시지 기반 파일 시스템 규약 위에 서 있습니다. 자원은 모두 계층적인 파일 시스템으로 나타냅니다. 사용자나 프로세스는 그 자원들을 잇는 파일 이름 구역을 조립해 자기만의 시야를 만듭니다. 다만 이 논문이 내세운 동기는 자기만의 시야를 조립하는 것이지 이름이 부딪히는 문제가 아니었습니다. 이름 충돌을 동기로 적은 원전은 못 찾았습니다(미확인).

예시

프로세스가 지금 들어 있는 커널 네임스페이스

리눅스는 프로세스마다 /proc/pid/ns/ 디렉토리를 두고, 그 안에 시스템 콜 setns(2)로 다룰 수 있는 구역마다 항목 하나를 놓습니다.

터미널
$ readlink /proc/$$/ns/uts
uts:[4026531838]

값은 구역의 종류 이름과 아이노드 번호를 이어 붙인 글자입니다. 두 프로세스의 이 심볼릭 링크가 같은 장치 번호와 같은 아이노드 번호를 가리키면 둘은 같은 구역에 있습니다. 응용 프로그램은 stat(2)가 돌려주는 st_dev와 st_ino 필드로 이것을 확인할 수 있습니다. 리눅스 3.7 이전에는 이 파일들이 하드 링크로 보였고, 3.8부터 심볼릭 링크로 보입니다.

C++의 중첩된 네임스페이스

바깥 구역의 i와 안쪽 구역의 i가 갈리는 대목을 봅니다.

C++
namespace Outer {
  int i;
  namespace Inner {
    void f() { i++; }      // Outer::i
    int i;
    void g() { i++; }      // Inner::i
  }
}

Inner 안에서 i를 쓰는 두 함수가 서로 다른 것을 가리킵니다. f가 쓸 때는 안쪽에 아직 i가 없어서 바깥 구역의 Outer::i가 나오고, g가 쓸 때는 안쪽에 선언된 Inner::i가 나옵니다. 어느 쪽인지는 컴파일할 때 정해집니다.

flowchart TD
    subgraph Outer["구역 Outer"]
        OI["i"]
        subgraph Inner["구역 Inner"]
            II["i"]
            F["함수 f"]
            G["함수 g"]
        end
    end
    F --> OI
    G --> II

XML 문서의 접두사 선언

XML(Extensible Markup Language, 확장 마크업 언어) 문서는 요소와 속성 이름을 URI(Uniform Resource Identifier, 통합 자원 식별자) 참조로 한정합니다. 그 묶음은 xmlns 이거나 xmlns:로 시작하는 예약된 속성으로 선언합니다.

XML
<x xmlns:edi='http://ecommerce.example.org/schema'>
  <!-- the "edi" prefix is bound to http://ecommerce.example.org/schema
       for the "x" element and contents -->
</x>

edi라는 접두사가 저 URI에 묶입니다. 묶임이 미치는 범위는 x 요소와 그 안의 내용입니다. 접두사는 구역 이름을 대신 세워 두는 표시일 뿐입니다. 그래서 문서 밖까지 통해야 하는 이름을 만들 때는 접두사가 아니라 URI를 쓰기를 권합니다.

flowchart TD
    P["접두사 edi · 이 문서 안에서만 통한다"]
    P --> U["구역 이름 http://ecommerce.example.org/schema · 문서 밖까지 통한다"]
    U --> Q["한정된 이름"]

쿠버네티스 매니페스트의 네임스페이스

development라는 구역 하나를 만드는 매니페스트입니다.

YAML
apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    name: development

만든 뒤에는 어느 구역을 기본으로 삼을지 접속 설정에 적어 둘 수 있습니다.

kubectl config set-context dev --namespace=development \
  --cluster=lithe-cocoa-92103_kubernetes \
  --user=lithe-cocoa-92103_kubernetes

이렇게 해 두면 그 뒤로 이름만 대는 명령이 development 구역 안에서 풀립니다.

갈래

무엇의 이름을 나누고 그 나눔을 누가 지키는가가 축입니다. 네 갈래가 이 축 위에서 서로 다른 곳에 섭니다.

언어 네임스페이스

나누는 것은 변수·함수·타입처럼 프로그램 안에서 부르는 이름입니다. 지키는 쪽은 컴파일러나 런타임입니다. C++에서 Outer::i처럼 ::로 한정한 이름을 어느 구역에서 찾을지는 컴파일할 때 정해집니다.

파이썬에서 네임스페이스는 이름에서 객체로 가는 매핑입니다. 내장 이름 한 벌, 모듈의 전역 이름 한 벌, 함수가 호출될 때 생기는 지역 이름 한 벌이 각각 따로 있습니다. 생기고 사라지는 때도 각각입니다.

내장 이름 구역은 인터프리터가 시작할 때 생겨 지워지지 않습니다. 함수의 지역 구역은 호출할 때 생깁니다. 함수가 돌아오거나 처리되지 않은 예외를 낼 때 사라집니다.

지금은 대부분 파이썬 딕셔너리로 구현되어 있습니다. 다만 그 구현은 성능 말고는 겉으로 드러나지 않고, 앞으로 바뀔 수도 있습니다.

문서 네임스페이스

나누는 것은 문서에 적힌 요소와 속성의 이름입니다. 지키는 쪽은 그 문서를 읽는 처리기입니다. XML 네임스페이스는 이름을 URI 참조로 식별되는 구역에 묶어서 한정하는 간단한 방법을 줍니다. 선언에 적는 값은 구역 이름이 되는 URI 참조이거나 빈 글자여야 합니다. 문서 안에서 짧게 부르는 접두사와, 밖까지 통하는 구역 이름이 갈려 있는 것이 이 갈래의 특징입니다.

커널 네임스페이스

나누는 것은 프로세스 번호나 마운트 지점처럼 원래 기계 전체에 하나뿐이던 이름들입니다. 지키는 쪽은 커널입니다. 커널 네임스페이스는 전역 시스템 자원을 감싸서, 그 구역 안의 프로세스에게는 그 자원의 인스턴스를 자기가 따로 가진 것처럼 보이게 합니다. 전역 자원을 바꾸면 같은 구역의 다른 프로세스에게는 보이고 밖의 프로세스에게는 안 보입니다. 이것을 쓰는 한 가지 용도가 컨테이너를 만드는 일입니다.

리눅스가 가르는 종류는 여덟입니다. 이름 가운데 셋은 줄임말입니다 — IPC(Inter-Process Communication, 프로세스 사이 통신) · PID(Process Identifier, 프로세스 번호) · UTS(Unix Time-sharing System, 유닉스 시분할 시스템)입니다.

아래 표에는 낯선 이름이 셋 더 나옵니다. cgroup은 프로세스를 계층으로 묶어 여러 종류의 자원을 얼마나 쓰는지 제한하고 감시하게 해 주는 커널 기능입니다. POSIX(Portable Operating System Interface, 이식 가능 운영체제 인터페이스)는 유닉스 계열 운영체제가 따르는 인터페이스 규격이고, NIS(Network Information Service, 네트워크 정보 서비스)는 여러 기계가 계정 같은 설정을 함께 보는 옛 방식입니다.

종류 플래그 가르는 자원
Cgroup CLONE_NEWCGROUP cgroup 루트 디렉토리
IPC CLONE_NEWIPC System V 프로세스 사이 통신, POSIX 메시지 큐
Network CLONE_NEWNET 네트워크 장치, 스택, 포트 등
Mount CLONE_NEWNS 마운트 지점
PID CLONE_NEWPID 프로세스 번호
Time CLONE_NEWTIME 부팅 시계와 단조 시계
User CLONE_NEWUSER 사용자 번호와 그룹 번호
UTS CLONE_NEWUTS 호스트 이름과 NIS 도메인 이름

네임스페이스가 가르는 것은 보이는 범위이고, cgroup이 다루는 것은 쓰는 양입니다.

클러스터 네임스페이스

나누는 것은 클러스터에 등록된 오브젝트의 이름입니다. 지키는 쪽은 클러스터를 관리하는 서버입니다. 쿠버네티스에서 네임스페이스는 클러스터 하나 안에서 자원 묶음을 격리하는 장치입니다. 자원의 이름은 한 네임스페이스 안에서 겹치지 않아야 하고, 네임스페이스가 다르면 같은 이름을 써도 됩니다. 이름을 위한 구역을 주는 것과, 클러스터의 한 부분에 권한과 정책을 붙일 수 있게 하는 것이 이 장치가 하는 일입니다.

경계

"격리"라는 말이 여러 장치에 같이 붙어 있어 선이 헷갈립니다. 케이스 하나를 판정합니다.

chroot(2)로 루트 디렉토리를 바꾼 프로세스는 마운트 네임스페이스를 따로 가진 것인가. 아닙니다. 이 호출은 경로 해석 과정의 한 재료를 바꿀 뿐 그 밖의 일은 하지 않습니다. 현재 작업 디렉토리도 열려 있는 파일 서술자도 그대로 두어서, 호출한 뒤에도 바깥 파일에 닿는 길이 남습니다. 마운트 지점의 이름을 구역으로 가르는 것과는 다른 일입니다.

네임스페이스에 안 들어가는 것도 있습니다. 쿠버네티스에서 네임스페이스에 기반한 범위 지정은 네임스페이스에 속하는 오브젝트에만 적용됩니다. 클러스터 전체를 두고 도는 오브젝트에는 적용되지 않습니다. 노드와 퍼시스턴트볼륨 같은 낮은 수준의 자원이 그런 것들이고, 네임스페이스 자원 자신도 네임스페이스 안에 있지 않습니다.

관련 항목

리눅스가 가르는 네임스페이스의 종류

마운트 네임스페이스 · PID 네임스페이스 · 네트워크 네임스페이스 · 사용자 네임스페이스 · UTS 네임스페이스 · IPC 네임스페이스 · cgroup 네임스페이스 · 타임 네임스페이스

네임스페이스를 만들고 드나드는 시스템 콜

clone · unshare · setns · mount · stat · 시스템 콜

네임스페이스와 헷갈리는 이웃

스코프 · cgroup · chroot · 샌드박스 · 캡슐화

네임스페이스를 써서 격리를 만드는 제품

컨테이너 · 도커 · 쿠버네티스 · LXC · Podman · 컨테이너 런타임

이름을 대상으로 바꿀 때 쓰는 구성 요소

심볼 테이블 · 링커 · 이름 맹글링 · 해시 테이블 · 딕셔너리

언어와 문서가 이름을 나누는 단위

모듈 · 패키지 · XML · URI · DNS

네임스페이스가 속한 상위 분류

운영체제 · 커널 · 프로세스 · 가상화 · 격리

다른 이름: namespace · 이름 공간 · namespaces