원문 캡처 · lwn.net
원문 캡처 · lwn.net

리눅스 커널은 현재 컨테이너라는 객체를 직접 인식하지 않고, 네임스페이스와 제어 그룹 등 여러 기능을 조합해 컨테이너를 만든다. 조너선 코벳(Jonathan Corbet)이 2026년 10월 29일 LWN.net에 보도한 바에 따르면, 조셉 바식(Josef Bacik)은 이 구조를 바꾸기 위해 프로세스와 자원을 처음부터 격리하는 커널 기능 ‘셀(cells)’을 제안했다. 아직 완성된 구현이 아니라 논의를 위한 코드와 설계다.

컨테이너를 커널이 직접 다루지 않는 방식은 리눅스가 컨테이너를 어떻게 통합해야 할지 충분히 알지 못했던 시기에는 타당했을 수 있다. 지금도 이 접근을 지지하는 의견이 있을 수 있지만, 컨테이너를 만드는 과정이 복잡하고 커널이 각 컨테이너의 속성을 강제하거나 프로세스가 어느 컨테이너에 속하는지 답할 수 없다는 단점이 있다. 현재는 네임스페이스를 만든 뒤 프로세스를 그 안으로 옮기고, 컨테이너 안에서 접근하지 못하도록 파일시스템을 분리하거나 다른 파일시스템으로 덮는 식으로 시스템의 일부를 가린다. 이 과정에서 프로세스가 컨테이너 안에 부분적으로만 들어가는 경우가 생긴다. 바식은 이 상황이 “우울할 만큼 많은 버그의 원인”이라고 설명했다. 현재 방식에서는 원치 않는 자원이 외부에서 컨테이너 안으로 새어 들어가기도 쉽다.

셀을 만들고 설정하기

바식의 제안에서 셀은 생성 시 아무것도 담지 않는다. 프로세스나 파일시스템이 기본으로 들어오지 않으며, 생성자가 원하는 자원을 명시적으로 넣어야 한다. 셀 안에서 만들어진 프로세스와 다른 자원은 셀 밖으로 나갈 수 없다. 제안의 취지는 이를 통해 현재 리눅스의 컨테이너보다 관리와 보안을 쉽게 하는 것이다.

셀은 cell_create() 호출로 만든다.

int cell_create(int parent, unsigned int flags);

parent 매개변수는 새 셀의 부모 프로세스를 지정하는 것으로 보이지만, 현재 구현에서 허용되는 값은 생성자 자신을 뜻하는 CELL_SELF뿐이다. flags는 0이어야 한다. 성공하면 셀을 가리키는 파일 디스크립터인 cellfd를 반환한다. 이 디스크립터는 셀에 대한 모든 후속 작업에 사용하는 핸들이며, 같은 권한을 가진 복제본을 만들 방법은 없다. 셀 생성은 특권 권한 없이 할 수 있다.

cellfd를 닫으면 셀과 그 안의 모든 것이 즉시 종료된다. 셀을 명시적으로 종료하는 시스템 호출도 있다.

int cell_kill(int cellfd, unsigned int flags);

cellfd는 종료할 셀을 가리키고, flags는 0이어야 한다.

빈 셀은 안전하고 버그가 없지만, 그 자체로는 할 수 있는 일이 거의 없다. 셀의 속성을 설정하는 호출은 다음과 같다.

int cell_configure(int cellfd, unsigned int cmd, const char *key,
 const void *value, int aux);

이 호출은 cellfd가 가리키는 셀에 설정 명령 cmd를 적용한다. key 문자열은 변경할 속성을 지정한다. 현재 제안에 포함된 명령은 다음과 같다.

  • CELL_SET_FLAG는 key가 지정한 플래그를 설정한다. 다만 현재 제안에는 정의된 플래그가 없다.
  • CELL_SET_STRING은 key로 지정한 문자열 속성에 value의 문자열을 설정한다. 초기 패치에서 지원하는 값은 셀 안의 호스트 이름을 설정하는 hostname과 네트워크 네임스페이스를 설정하는 net이다. 기본적으로 셀에는 네트워크 접근이 없으며, 셀 안에서는 AF_UNIX 소켓만 만들 수 있다. net을 new로 설정하면 새 네트워크 네임스페이스가 만들어지고, 이후 생성자가 이를 설정한다. own으로 설정하면 셀이 소유하고 내부에서 설정하는 네트워크 네임스페이스가 만들어진다. new 방식에서는 새 네임스페이스의 파일 디스크립터를 얻기 위해 cell_open()이라는 새 시스템 호출도 사용해야 한다. 네트워크 네임스페이스는 아래의 CELL_SET_FD로도 지정할 수 있다.
  • CELL_SET_BINARY는 key가 지정한 속성에 이진 데이터를 설정한다. 현재 허용되는 값은 셀 안에서 사용할 사용자 ID를 정하는 ids뿐이다. 허용되는 값은 두 가지다. 1로 설정하면 셀 안의 프로세스가 생성자와 같은 사용자 ID로 실행되지만, 루트 사용자는 이 방식을 쓸 수 없다. 그 외에는 ids를 65,536으로 설정해야 한다. 그러면 실행 중인 시스템의 32비트 사용자 ID 공간 상위 부분에서 해당 개수만큼의 ID를 할당한다. 이 ID는 시스템에서 고유하며 셀 생성자가 직접 선택할 수 없다. 셀 밖의 파일시스템에는 이 ID가 소유자로 지정된 파일을 만들 수 없다. 이 설계에는 의견이 제기됐으며, 셀 안의 사용자에게 완전히 별도의 사용자 ID 공간을 제공하는 방안 등을 포함해 향후 바뀔 수 있다.
  • CELL_SET_FD는 파일 디스크립터가 필요한 값을 설정한다. 초기 패치에서 구현된 값은 세 가지다. root는 셀 안의 루트 파일시스템을 담는 파일시스템 네임스페이스, net은 셀에서 사용할 네트워크 네임스페이스, budget은 제어 그룹 디렉터리를 가리킨다. budget은 필수이며 셀마다 자체 제어 그룹이 있어야 한다.
  • CELL_CMD_SEAL은 셀 설정을 마친 뒤 실행해야 한다. 이 명령으로 초기화를 끝내면 더는 설정을 변경할 수 없다.

셀을 봉인하면 파일시스템 트리와 제어 그룹 소속, 시스템과의 상호작용을 제어하는 나머지 네임스페이스가 설정된다. 그래도 셀 안에는 실행 중인 프로세스가 없다. 바식의 설계에서는 이미 존재하는 프로세스가 셀에 들어갈 수 없으며, setns()로 셀의 네임스페이스에 진입하는 것도 허용되지 않는다. 프로세스는 처음부터 셀 안에서 생성해야 하고, 셀 밖으로 나올 수 없다.

셀 안에서 프로세스 만들기

셀 안에 계속 머무는 프로세스를 생성하려면 별도의 프로세스 생성 API 제안도 필요하다. 직전 시도는 8월에 LWN.net에서 다뤘다. 이번 제안에서는 먼저 pidfd_open() 시스템 호출의 변형을 사용해 셀 안에 프로세스를 만든다. 일반적인 pidfd_open()처럼 프로세스 ID를 전달하는 대신, 셀 생성자는 cellfd와 PIDFD_EMPTY 플래그를 전달한다. 반환되는 pidfd는 아직 실행 중인 작업을 가리키지 않고 ‘배아(embryo)’ 상태다.

pidfd를 만든 다음에는 실제로 실행할 수 있도록 새 시스템 호출로 설정해야 한다.

int pidfd_configure(int pidfd, unsigned int cmd, const char *key,
 const void *value, int aux);

인터페이스는 cell_configure()와 비슷하며 대체로 같은 방식으로 동작한다. 설정 대상 프로세스는 배아 상태여야 하며 pidfd로 지정한다. 사용할 수 있는 cmd는 다음과 같다.

  • PIDFD_SET_STRING은 문자열을 설정한다. 현재 지원되는 유일한 키 exec는 프로세스가 실행할 파일을 지정하며, 경로는 셀의 마운트 네임스페이스 안을 기준으로 한다.
  • PIDFD_SET_BINARY는 키에 이진 데이터를 설정한다. argv는 프로세스의 인자 벡터를, envp는 환경 변수를, cred는 셀의 사용자 네임스페이스 안에서 프로세스가 사용할 사용자 ID와 그룹 ID를 지정한다.
  • PIDFD_SET_FD는 배아 프로세스에 파일 디스크립터를 설정한다. key는 문자열로 표현한 파일 디스크립터 번호이고, aux는 호출자의 파일 테이블에 있는 열린 파일 디스크립터다. 프로세스가 가지는 열린 파일 디스크립터는 PIDFD_SET_FD로 지정한 것뿐이므로, 파일 디스크립터가 실수로 셀 안으로 유입되는 문제를 막을 수 있다.
  • PIDFD_CMD_START는 필요한 설정을 모두 마친 뒤 프로세스를 실행한다. 실행되면 더는 배아 상태가 아니므로 해당 pidfd에 pidfd_configure()를 사용할 수 없다.

셀에서 처음 실행한 프로세스는 셀의 초기(init) 프로세스가 된다. 이 프로세스가 종료되면 셀도 종료된다. 이 API로 실행 중인 셀에 다른 프로세스를 추가할 수도 있다. 예를 들어 소유자는 셀 안에서 셸을 실행할 수 있다. 이때도 새 프로세스를 만들어야 하며, 기존 프로세스가 경계를 넘어가는 것은 허용되지 않는다.

경계와 제안의 한계

셀 경계에는 다른 제한도 적용된다. 셀 안의 모든 프로세스에는 ‘새 권한 없음(no new privileges)’ 플래그가 설정돼 새로운 권한을 얻을 수 없다. 유닉스 도메인 소켓을 이용해 열린 파일 디스크립터를 경계 안팎 어느 방향으로든 전달할 수 없다. 셀은 다른 프로세스와 제어 그룹을 공유할 수 없다. 이 밖에도 셀의 격리를 엄격하고 통제된 상태로 유지하기 위한 제한이 있다.

제안에는 여기서 다루지 않은 세부 사항이 많고, 해결되지 않은 문제와 부족한 점도 길게 열거돼 있다. 바식은 완성된 구현을 제공하는 것이 목적이 아니라 “논쟁할 코드가 있도록” 제안했다고 설명했다. 자세한 내용은 전체 제안과 GitHub 브랜치의 설계 문서에서 확인할 수 있다. 코드는 GitHub 브랜치에 있다.

이 제안은 지금까지 논의가 거의 없었다. linux-fsdevel 메일링 리스트에만 게시됐기 때문일 수 있다. 다만 10월 중순에 올라온 첫 버전 이후 제안은 어느 정도 발전했다. 작은 제안이 아니며 과거에도 논란이 됐던 변화를 시도하므로, 앞으로 논의가 본격화될 가능성이 있다. 그 논의가 끝나면 리눅스 커널이 오랫동안 받아들이지 않았던 컨테이너 추상화를 갖게 될 수도 있다.