“컨테이너는 가벼운 VM”이라고들 하지만, 사실 둘은 원리가 완전히 다릅니다. 컨테이너는 가상 머신이 아니라 “격리된 프로세스”일 뿐입니다. 그 격리를 만드는 두 가지 리눅스 커널 기능 — namespaces와 cgroups — 를 이해하면 LXC·Docker가 왜 그렇게 가벼운지, 그리고 왜 Docker-in-LXC에 특별한 설정이 필요한지가 보입니다.
1. 컨테이너 vs VM — 근본 차이
| VM | 컨테이너 | |
|---|---|---|
| 격리 방식 | 하드웨어 가상화 | 커널 기능(격리된 프로세스) |
| 커널 | 게스트마다 별도 | 호스트와 공유 |
| 부팅 | OS 부팅(수십 초) | 프로세스 시작(즉시) |
| 오버헤드 | 큼 | 거의 없음 |
컨테이너는 호스트 커널을 그대로 공유하면서, “이 프로세스는 자기만의 세상에 있는 것처럼” 보이게 속입니다. 그 속임수의 두 축이 namespaces와 cgroups입니다.
2. namespaces — “무엇을 볼 수 있는가”
namespace는 프로세스가 보는 시야를 격리합니다. 종류별로 각각 다른 자원을 가립니다.
| namespace | 격리 대상 |
|---|---|
| PID | 프로세스 목록(자기 PID 1) |
| NET | 네트워크(자기 IP·인터페이스) |
| MNT | 파일시스템 마운트 |
| UTS | 호스트명 |
| IPC | 프로세스 간 통신 |
| USER | 사용자·권한(UID 매핑) |
그래서 컨테이너 안에서 ps를 치면 자기 프로세스만 보이고, 자기만의 IP를 갖습니다. 실제로는 호스트의 한 프로세스일 뿐인데도요. USER namespace가 바로 “unprivileged 컨테이너”의 핵심 — 컨테이너의 root를 호스트의 비특권 사용자로 매핑해 보안을 높입니다.
3. cgroups — “얼마나 쓸 수 있는가”
namespace가 “시야”라면, cgroups(control groups)는 “자원 사용량 제한”입니다.
- CPU: 이 컨테이너는 코어 2개까지
- 메모리: 512MB 넘으면 제한
- I/O: 디스크 대역폭 제한
Proxmox에서 LXC에 cores·memory를 지정하면, 내부적으로 cgroups가 그 한도를 강제합니다. vCPU 오버커밋이 가능한 것도 cgroups가 실제 사용량을 조율하기 때문입니다.
4. 그래서 Docker-in-LXC가 까다롭다
컨테이너가 이 커널 기능들에 의존하다 보니, 컨테이너 안에서 또 컨테이너(LXC 안 Docker)를 돌리려면 커널 기능 접근을 열어줘야 합니다.
nesting=1: 중첩된 namespace 생성 허용keyctl=1: Docker가 쓰는 커널 키링 접근 허용
원리를 알고 나면 이 플래그들이 왜 필요한지 자연스럽게 이해됩니다 — 컨테이너의 격리 메커니즘을 한 겹 더 쌓는 것이니까요.
5. 정리
컨테이너 = namespaces(시야 격리) + cgroups(자원 제한)로 만든 “격리된 프로세스”입니다. VM처럼 커널을 통째로 복제하지 않으니 가볍고 빠른 거죠. 이 원리 하나를 잡으면 LXC·Docker의 동작, unprivileged의 의미, nesting 플래그의 이유가 전부 하나로 꿰어집니다.