컨테이너 = 리눅스 커널의 Control groups, Namespaces, Union mount filesystem 기능을 조합한 것.
따라서 이 세가지 커널의 기능은 도커와 쿠버네티스에서 나중에 겪을 현상을 설명해주는 열쇠가 됨
전체 지도
커널이 제공하는 것
| 기술 | 담당 |
|---|---|
| Namespaces (2002~) | 뭐가 보이는지 |
| Cgroups (2008) | 얼마나 쓸 수 있는지 |
| Union mount filesystem (2014) | 어떤 파일 위에 서 있는지 |
⇒ 이 셋이 함께 조합된 프로세스 = 컨테이너
그 위에서 이걸 대신 만들어주는 도구
Docker (2013)
위 기능을 발명한게 아니라 위 기능 가져다가 명령어로 켜고 이미지로 만들어준 도구 ⇒ 컨테이너 자체가 아니라 컨테이너를 만들어주는 도구. 그 결과로 격리된 프로세스가 생기는데, 이게 컨테이너. 도커 없이도 컨테이너는 만들 수 있음 (podman, containerd 등)
1. Namespace : 보이는 범위
프로세스 별로 커널 자원을 분할하는 리눅스 커널의 기능. 볼 수 있는 범위를 제한하는, 프로세스가 소속되는 공간.
종류
PID
Process ID 정보를 격리. 같은 PID 네임스페이스에 속하지 않은 프로세스는 보이지도 않고 접근도 안 됨.
컨테이너 하나 안에 프로세스가 여러개 돌 수 있는 이유. nginx 컨테이너를 띄우면 마스터 프로세스 1개와 워커 프로세스 여러개가 도는데 이들이 전부 같은 네임스페이스 세트를 공유해서 서로는 보이고 바깥은 안 보이는 것임.
컨테이너는 같은 네임스페이스 세트를 공유하는 프로세스 무리. 그 무리가 1개일 수도 있고 여러 개일 수도 있음.
(도커 문서에 나오는 한 컨테이너에 프로세스 하나 원칙은 운영 권장 사항)
Network
네트워크 장치, IP 주소, 포트, 라우팅 테이블 등 네트워크 리소스 격리. + 가상 네트워크 장치 할당. 실제로 패킷이 오가는 길.
User
프로세스 별로 UID, GID 정보 매핑. 컨테이너 안에서는 root로 보이지만 호스트에서 보면 아무 권한 없는 일반 사용자로 매핑. 컨테이너가 탈출해도 호스트에서 root가 아니게 만드는 보안장치.
UID (User Identifier), GID (Group Identifier) : 생성된 프로세스가 속한 사용자 또는 그룹의 아이디. 보통 부모 프로세스로부터 상속.
Mount
프로세스 별로 마운트 되는 파일 시스템 격리
mount는 연결이라기보다 트리를 특정 지점에 끼워넣는 것.
윈도우는 USB를 꽂으면
E:드라이브가 새로 생김. 트리가 하나 더 생기는 방식. 리눅스는 트리가/하나뿐이고, usb를 꽂으면/media/usb라는 기존 폴더 자리에 그 내용이 나타남. 이때/media/usb를 마운트 포인트라고 함. 커널은 어느 저장장치가 어느 지점에 붙어 있는지 마운트 목록을 가지고 있음.mount명령을 치면 그 목록이 나옴.
mount 네임스페이스는 이 마운트 목록을 프로세스 별로 따로 갖게 하는 것. 그래서 컨테이너는 자기 이미지가 만든 파일 시스템을 자기 /에 붙여놓고 호스트의 목록은 건드리지 않음
IPC
inter-process communication 격리. 다른 프로세스 접근 or 제어 방지. 다시 말해 프로세스끼리 데이터를 주고 받는 통로.
프로세스는 기본적으로 서로의 메모리에 접근할 수 없음. (ex. 크롬이 카톡의 메모리를 읽지 X) 근데 같은 프로그램의 프로세스끼리는 데이터를 주고 받아야 할 때가 있음. 그래서 커널이 제공하는 몇가지 통로가 있음
| 통로 | 설명 |
|---|---|
| 공유 메모리 | 여러 프로세스가 같이 쓰는 메모리 구역 |
| 메시지 큐 | 편지함 |
| 세마포어 | 동시에 못 쓰게 막는 자물쇠 |
이것들은 번호(key)로 서로를 찾는데, 그 번호 목록이 컴퓨터 전체에 한벌임. 다른 앱이 같은 번호를 쓰면 충돌하거나 남의 데이터를 엿볼 수 있게 됨.
IPC 네임스페이스는 이 공유 메모리/메시지 큐 목록을 컨테이너 별로 따로 만듦.
UTS (UNIX Time Sharing System)
Host 명이나 도메인 명 격리
Network가 아닌 이유 : hostname은 통신에 쓰이는 정보가 아니라서. 호스트명은 그냥 이 기계가 자기를 부르는 이름표 문자열이고, 실제 통신은 전부 IP로 함. hostname 자체는 통신 경로에 등장하지 않음
Cgroup
프로세스가 보는 cgroup 계층을 격리. 컨테이너 안에서 호스트의 cgroup 구조가 안 보이게 함
Time
시스템 부팅 시각 등 시간 정보를 격리
2. Cgroups (Control Groups) : 쓸 수 있는 양
프로세스를 그룹으로 묶어, 그 그룹이 쓸 수 있는 자원의 양을 커널이 강제로 제한·측정하는 기능 ⇒ CPU, 메모리, 디스크 I/O, 네트워크 대역폭 등을 특정 프로세스 그룹에 할당하고 제어할 수 있음.
2-1. 주요 기능
| 기능 | 설명 |
|---|---|
| 리소스 제한 | 특정 프로세스 그룹이 사용할 수 있는 CPU, 메모리, I/O, 네트워크 대역폭 등을 제한 |
| 리소스 할당 | 특정 프로세스 그룹에 더 많은 CPU시간을 부여하는 등 우선순위 조정 가능 |
| 격리 | 서로 다른 프로세스 그룹 간에 리소스 간섭을 최소화 ⇒ 컨테이너 환경 구현 가능 |
| 모니터링 | 특정 프로세스 그룹이 얼마나 많은 리소스를 소비하는지 추적 가능 |
2-2. 버전 : 세대 교체
cgroup v1. (2008)
- 각 리소스가 개별적인 하위 시스템으로 관리됨
- 서로 다른 리소스 컨트롤러를 독립적으로 사용 가능
cgroup v2. (2016)
위 장점이 사실 v1의 문제. 지금은 사실상 거의 v2를 사용하고, v1은 옛날 시스템 호환용으로 남아있다고 보면 됨.
- 단일 계층에서 모든 리소스 통합 관리 가능
- 리소스 제어가 더욱 일관되게 이루어짐
- 컨테이너 환경(도커, 쿠버네티스 등)에서 점점 더 많이 사용됨
2-3. 주요 컨트롤러 (v2에서 정착한 용어) = 사용가능한 서브 시스템 (v1 시절에 쓰던 용어)
cgroup는 뼈대만 제공함. 그룹을 만들고, 프로세스를 그 그룹에 넣고, 그룹끼리 계층 구조를 만드는 것까지가 cgroups 본체
CPU를 제한하는 방법과 메모리를 제한하는 방법은 완전히 다름. 그래서 자원 종류마다 담당 모듈을 따로 두고 그 모듈을 컨트롤러라고 부름
cgroup가 계약서 양식이라면 컨트롤러는 직접 실무에 나서는 담당자
| 컨트롤러 | 하는 일 | 어디서 만나? |
|---|---|---|
| cpu | CPU 사용 시간 제한 | docker run --cpus=1.5, K8s resources.limits.cpu |
| memory | 메모리 사용량 제한 | docker run --memory=512m, K8s resources.limits.memory |
그외
pids: 프로세스 개수 제한, 포크 폭탄 방어blkio / io: 디스크 읽고쓰기 속도 제한cpuset: 특정 CPU 코어에 고정freezer: 일시정지, docker pause가 이것devices: 장치 접근 허용·차단
2-4. 한도를 넘으면 어떻게 되는가
CPU 10% 제한을 초과하면? → 안 죽음. 기다리게 만듦.
CPU는 시간을 쪼개 쓰는 자원이라, 할당량을 다 쓰면 커널이 다음 차례까지 그냥 재워둠. 이걸 스로틀링이라고 함. 프로세스는 살아 있고 느려질 뿐.
메모리 128MB를 초과하면? → 죽음
메모리는 이미 쓴 걸 잠깐 기다리게 할 방법이 없어서 한도를 넘으면 커널이 캐시를 좀 비워보고, 그래도 안 되면 OOM Killer가 그 그룹의 프로세스를 강제 종료.
이 차이를 부르는 이름이 있음
| 성격 | 한도 초과 시 | |
|---|---|---|
| CPU | 압축 가능 (compressible) | 스로틀링. 느려짐 |
| 메모리 | 압축 불가능 (incompressible) | OOM Kill. 죽음 |
3. Union Mount filesystem : 어떤 파일 위에 서 있는지
읽기 전용 층 여러 개와 쓰기 층 하나를 겹쳐, 프로세스에게는 하나의 파일시스템으로 보이게 하는 기능. 같은 이름은 위층이 이기고, 아래층은 지워지지 않고 가려질 뿐.

Dockerfile로 치면 이렇게 대응함
Layer 1 ← COPY app.py (마지막에 쌓임, 맨 위)
Layer 2 ← RUN apt install
Layer 3 ← FROM ubuntu (맨 처음, 바닥)
규칙 두개가 있음
- 여러 층에 흩어진 파일은 전부 합쳐서 하나처럼 보임 (그래서 이름이 union 합집합.)
- 같은 이름이 여러 층에 있으면 위층이 이김 (아래층 파일은 지워지는 게 아니라 가려짐)
쓰기 층
위 그림은 읽기만 설명함. 실제 컨테이너는 층이 하나 더 있음.
┌──────────────────────────────────────┐
│ 쓰기 층 (Container Layer) │ ← 컨테이너마다 하나씩, 읽고 쓰기 가능
├──────────────────────────────────────┤
│ Layer 1 │ ┐
│ Layer 2 │ ├ 읽기 전용, 여러 컨테이너가 공유
│ Layer 3 │ ┘
└──────────────────────────────────────┘