Container and OS-Level Virtualization
컨테이너 개념, 동작 원리(namespace, cgroup), VM과의 차이
🎯 컨테이너란?
컨테이너(Container): 애플리케이션과 그 실행 환경(라이브러리, 설정 등)을 패키징하여 격리된 공간에서 실행하는 기술
핵심 특징:
- Host OS의 커널을 공유
- 각 컨테이너는 독립된 프로세스 공간
- VM처럼 별도 OS를 부팅하지 않음 → 가볍고 빠름
🆚 VM vs Container
flowchart TB
subgraph VM["Virtual Machine"]
direction TB
VA[App App App]
VB[Bins/Libs]
VC[Guest OS]
VD[Hypervisor]
VE[Host OS]
VF[Hardware]
VA --> VB --> VC --> VD --> VE --> VF
end
subgraph Container["Container"]
direction TB
CA[App App App]
CB[Bins/Libs]
CC["(커널 공유)"]
CD[Container Engine]
CE["Host OS (Kernel)"]
CF[Hardware]
CA --> CB --> CC --> CD --> CE --> CF
end
| 구분 | VM | Container |
|---|---|---|
| 격리 방식 | 하드웨어 가상화 | OS 수준 격리 |
| 커널 | 각자 보유 | Host와 공유 |
| 오버헤드 | 큼 (Guest OS 전체) | 작음 (프로세스 수준) |
| 시작 시간 | 분 단위 | 초 단위 |
| 이미지 크기 | GB | MB |
| 밀도 | 낮음 (수십 개) | 높음 (수백~수천 개) |
| 격리 강도 | 강함 | 상대적으로 약함 |
💡 컨테이너를 "가상화"라고 부르기도 하지만, 엄밀히는 OS 수준 격리(OS-level isolation) 또는 **컨테이너화(Containerization)**가 더 정확한 표현임.
⚙️ 컨테이너의 핵심 기술 (Linux)
컨테이너는 Linux 커널의 두 가지 핵심 기능을 활용함.
Namespace
프로세스가 볼 수 있는 시스템 자원의 범위를 격리하는 메커니즘
각 컨테이너는 자신만의 namespace를 가져서 마치 독립된 시스템처럼 보임.
| Namespace | 격리 대상 | 설명 |
|---|---|---|
| PID | 프로세스 ID | 컨테이너 내부에서 PID 1부터 시작 |
| NET | 네트워크 | 독립된 네트워크 인터페이스, IP, 포트 |
| MNT | 마운트 포인트 | 독립된 파일시스템 뷰 |
| UTS | 호스트명 | 독립된 hostname |
| IPC | IPC 자원 | 독립된 세마포어, 메시지 큐 |
| USER | 사용자 ID | 컨테이너 내부 root ≠ Host root |
# 현재 프로세스의 namespace 확인
ls -la /proc/$$/ns/
# 특정 namespace에서 명령 실행 (unshare)
sudo unshare --pid --fork --mount-proc /bin/bash
# → 새 PID namespace에서 bash 실행, PID 1이 됨
Cgroup (Control Group)
프로세스가 사용할 수 있는 자원의 양을 제한하는 메커니즘
| 자원 | 설명 |
|---|---|
| CPU | CPU 시간 제한 |
| Memory | 메모리 사용량 제한 |
| I/O | 디스크 I/O 대역폭 제한 |
| Network | 네트워크 대역폭 제한 |
# cgroup 정보 확인
cat /proc/$$/cgroup
# Docker 컨테이너의 메모리 제한 설정 예시
docker run -m 512m nginx # 메모리 512MB 제한
Namespace + Cgroup = Container
flowchart TB
subgraph Container["Container"]
subgraph NS["Namespace (격리)"]
NS1["PID: 자체 프로세스 트리"]
NS2["NET: 자체 네트워크 스택"]
NS3["MNT: 자체 파일시스템 뷰"]
end
subgraph CG["Cgroup (자원 제한)"]
CG1["CPU: 50% 제한"]
CG2["Memory: 512MB 제한"]
end
end
Kernel["Host Linux Kernel"]
Container -->|커널 공유| Kernel
🐳 컨테이너 런타임
실제로 컨테이너를 생성하고 실행하는 소프트웨어.
계층 구조
flowchart TB
subgraph High["High-level Runtime"]
Docker["Docker, Podman"]
end
subgraph Mid["Container Runtime"]
Containerd["containerd, CRI-O"]
end
subgraph Low["Low-level Runtime (OCI)"]
Runc["runc, crun"]
end
subgraph Kernel["Linux Kernel"]
NS["namespace, cgroup"]
end
High --> Mid --> Low --> Kernel
style High fill:#e1f5fe
style Mid fill:#fff3e0
style Low fill:#fce4ec
style Kernel fill:#e8f5e9
주요 런타임
| 런타임 | 레벨 | 설명 |
|---|---|---|
| Docker | High | 가장 유명, CLI/이미지 빌드/레지스트리 통합 |
| Podman | High | Docker 호환, 데몬리스, rootless |
| containerd | Mid | Docker에서 분리, Kubernetes 기본 런타임 |
| CRI-O | Mid | Kubernetes 전용 경량 런타임 |
| runc | Low | OCI 표준 구현체, 실제 컨테이너 생성 |
📦 컨테이너 이미지
이미지(Image): 컨테이너 실행에 필요한 모든 것을 패키징한 읽기 전용 템플릿
레이어 구조
이미지는 여러 레이어가 쌓인 형태임. 각 레이어는 변경사항만 저장함.
flowchart TB
subgraph Image["Container Image"]
L4["Layer 4: App code"]
L3["Layer 3: pip install"]
L2["Layer 2: Python"]
L1["Layer 1: Ubuntu"]
L4 --> L3 --> L2 --> L1
end
L4 -.- N4["← 내 애플리케이션"]
L3 -.- N3["← 패키지 설치"]
L2 -.- N2["← 런타임"]
L1 -.- N1["← 베이스 이미지"]
장점:
- 레이어 공유로 저장 공간 절약
- 변경된 레이어만 다시 빌드/전송
- 캐싱으로 빌드 속도 향상
이미지 vs 컨테이너
| 구분 | 이미지 | 컨테이너 |
|---|---|---|
| 상태 | 정적 (읽기 전용) | 동적 (실행 중) |
| 비유 | 클래스 | 인스턴스 |
| 저장 | 레지스트리 | 호스트 메모리/디스크 |
# 이미지 = 템플릿
docker pull nginx
# 컨테이너 = 이미지로부터 생성된 인스턴스
docker run nginx # 이미지에서 컨테이너 생성 및 실행
🔒 컨테이너 보안 고려사항
컨테이너는 커널을 공유하므로 VM보다 격리 수준이 낮음.
주요 고려사항:
| 항목 | 설명 |
|---|---|
| 커널 공유 | 커널 취약점이 모든 컨테이너에 영향 |
| root 권한 | 컨테이너 내 root가 Host에 영향줄 수 있음 |
| 이미지 신뢰 | 검증되지 않은 이미지 사용 위험 |
보안 강화 방법:
- rootless 컨테이너 사용 (Podman 기본)
- User namespace 활용
- seccomp, AppArmor, SELinux 적용
- 신뢰된 베이스 이미지 사용
- 최소 권한 원칙 적용
🤔 언제 VM, 언제 Container?
| 상황 | 선택 | 이유 |
|---|---|---|
| 다른 OS 실행 필요 | VM | 컨테이너는 Host 커널 공유 |
| 강한 보안 격리 필요 | VM | 하드웨어 수준 격리 |
| 레거시 애플리케이션 | VM | 전체 OS 환경 필요 |
| 마이크로서비스 | Container | 빠른 배포, 높은 밀도 |
| CI/CD 파이프라인 | Container | 빠른 시작, 일관된 환경 |
| 클라우드 네이티브 | Container | 오케스트레이션 (K8s) |
💡 현대 환경에서는 VM 위에 컨테이너를 돌리는 경우도 많음. 클라우드 VM(EC2 등) 안에서 Docker/Kubernetes를 실행하는 구조.
🔗 관련 포스트
- Hypervisor & VM — 하이퍼바이저와 가상화
- Linux Kernel Overview — 커널 (namespace, cgroup)
- Proxmox VE LXC Container — LXC vs Docker