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