OS는 하나지만 경계만 여럿입니다.
컨테이너는 단순한 가벼움이 아니라 다른 경계가 됩니다.
그 경계를 만드는 것이 네임스페이스와 cgroup입니다.

처음 컨테이너를 접했을 때의 잘못된 질문

컨테이너를 처음 접했을 때, 저는 자연스럽게 VM과 비교하기 시작했습니다. 그리고 이런 식으로 정리했습니다.

"VM은 OS까지 통째로 올리니까 무겁고, 컨테이너는 OS를 공유하니까 가볍다."

틀린 말은 아닙니다. 하지만 이 비교에서 출발하면 정작 중요한 부분을 놓치고 있었습니다. 가벼움은 결과이지, 컨테이너의 본질이 아니기 때문입니다.

파고들다 보니 질문이 바뀌었습니다.

"VM과 컨테이너는 무게가 다른 게 아니라, 경계를 어디에 그었느냐가 다른 것 아닐까?"

이 질문에서 출발하면 컨테이너의 구조가 훨씬 선명하게 보이기 시작했습니다.


VM과 컨테이너: 경계의 위치가 다릅니다

2편에서 VM은 OS 단위로 경계를 세운다고 정리했습니다. 게스트 OS 전체가 격리의 단위였습니다. 커널이 분리되기 때문에 한 VM에서 발생한 사고가 다른 VM으로 번지지 않았습니다. 강한 격리였지만, 그만큼 운영 단위가 컸습니다.

컨테이너는 다른 선택을 합니다.

OS 커널은 공유합니다. 대신 그 위에서 돌아가는 프로세스(집합)를 격리합니다.

격리의 단위가 머신(OS)에서 프로세스로 내려온 것입니다. 커널은 하나지만, 각 컨테이너는 자신만의 독립된 세계가 있는 것처럼 동작합니다.

여기서 제가 막혔던 지점이 있습니다.

"커널을 공유하면 격리가 제대로 되는 건가? VM보다 경계가 약한 것 아닌가?"

이 의문을 풀려면 컨테이너가 경계를 어떻게 만드는지 들여다봐야 했습니다.


경계를 만드는 첫 번째 도구: 네임스페이스(Namespace)

컨테이너의 격리는 네임스페이스라는 Linux 커널 기능에서 시작됩니다.

네임스페이스가 하는 일은 단순합니다. 프로세스가 볼 수 있는 범위를 제한합니다.

예를 들어 컨테이너 A 안에서 실행 중인 프로세스는 이렇게 동작합니다.

  • 자신의 PID가 1번이라고 알고 있습니다. (실제로는 호스트에서 다른 번호입니다.)
  • 자신만의 네트워크 인터페이스가 있다고 알고 있습니다.
  • 자신만의 파일 시스템 루트(/)를 가진 것처럼 동작합니다.

이것은 2편에서 본 VM의 게스트 OS와 비슷한 구조입니다. 차이는 커널이 분리된 것이 아니라, 커널이 제공하는 뷰(View)만 분리됐다는 점입니다.

프로세스는 자신이 격리된 세계에 있다고 믿습니다. 실제로는 같은 커널 위에 있지만, 네임스페이스가 그 뷰를 잘라냅니다. 저는 이것이 Virtual Memory에서 봤던 구조와 같다고 느꼈습니다. 주소가 가짜였던 것처럼, 여기서는 프로세스가 보는 세계가 가짜입니다.

네임스페이스는 컨테이너의 경계(Boundary) 입니다.

커널 하나에 여러 개의 뷰


경계에 의한 자원 배분: cgroup

네임스페이스로 프로세스가 보는 세계를 격리했다고 해서 끝이 아니었습니다. 여기서 또 막혔습니다.

"보이는 것만 격리하면 자원은 어떻게 나누나? 컨테이너 하나가 CPU를 다 써버리면?"

이 문제를 해결하는 것이 cgroup(Control Group)입니다.

cgroup은 프로세스 집합이 사용할 수 있는 자원의 양을 제한합니다.

  • 이 컨테이너는 CPU의 몇 %까지만 사용할 수 있다.
  • 이 컨테이너는 메모리를 얼마까지만 점유할 수 있다.
  • 이 컨테이너의 네트워크 대역폭은 여기까지다.

2편에서 하이퍼바이저가 VM들의 자원 충돌을 정책으로 조정했던 것처럼, cgroup은 컨테이너 수준에서 같은 역할을 합니다.

cgroup은 컨테이너의 정책(Policy) 입니다.


컨테이너에서 경계 + 매핑 + 정책

이전 편들에서 반복됐던 프레임을 컨테이너에 대입하면 이렇게 정리됩니다.

  • 경계(Boundary): 네임스페이스가 프로세스가 볼 수 있는 범위를 자릅니다.
  • 매핑(Mapping): 컨테이너 안의 가상 경로, 네트워크, PID가 호스트의 실제 자원과 연결됩니다.
  • 정책(Policy): cgroup이 각 컨테이너의 자원 사용량을 제한하고 조정합니다.

VM에서는 하이퍼바이저가 이 세 가지를 담당했습니다. 컨테이너에서는 Linux 커널 자체가 이 역할을 수행합니다. 별도의 하이퍼바이저 레이어 없이, 커널 기능만으로 경계와 매핑과 정책이 작동합니다. 이것이 컨테이너가 가벼운 이유입니다. 하지만 본질은 가벼움이 아니라, 경계를 어디에 그었느냐의 차이입니다.


"약한 격리 아닌가?"라는 질문에 대한 답

컨테이너가 커널을 공유한다는 사실은 분명히 VM보다 격리가 약하다는 의미이기도 합니다. 커널 취약점이 있으면 컨테이너 경계가 뚫릴 수 있습니다. VM이라면 커널이 분리되어 있어서 같은 상황에서 더 강하게 막아냅니다.

이 트레이드오프는 실제로 존재합니다. 컨테이너는 격리를 포기하고 효율을 택한 것이 아닙니다. 격리의 단위를 바꾼 것입니다.

VM은 "다른 머신"을 만듭니다. 컨테이너는 "같은 머신 안의 다른 세계"를 만듭니다.

어느 쪽이 옳다는 이야기가 아닙니다. 운영하려는 단위가 무엇이냐에 따라 적합한 경계가 달라집니다.


요약

  • 컨테이너는 VM보다 가벼운 기술이 아니라, 경계를 OS에서 프로세스로 내린 기술입니다.
  • 네임스페이스가 프로세스가 볼 수 있는 범위를 격리합니다. (경계)
  • 컨테이너 안의 가상 자원은 호스트의 실제 자원과 매핑됩니다. (매핑)
  • cgroup이 컨테이너별 자원 사용량을 제한하고 조정합니다. (정책)
  • 커널을 공유하기 때문에 격리 수준이 다르며, 이는 트레이드오프입니다.

다음 편: Docker, 경계를 배포 단위로 굳히다

컨테이너라는 경계가 만들어졌지만, 그것을 어떻게 묶고 옮기고 재현할 것인가는 별개의 문제였습니다. Docker는 컨테이너에 이미지라는 포장을 씌워 배포 경험을 표준화했습니다. 다음 편에서는 Docker가 컨테이너의 어떤 문제를 풀었는지 따라가보겠습니다.