Skip to main content

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
구분VMContainer
격리 방식하드웨어 가상화OS 수준 격리
커널각자 보유Host와 공유
오버헤드큼 (Guest OS 전체)작음 (프로세스 수준)
시작 시간분 단위초 단위
이미지 크기GBMB
밀도낮음 (수십 개)높음 (수백~수천 개)
격리 강도강함상대적으로 약함

💡 컨테이너를 "가상화"라고 부르기도 하지만, 엄밀히는 OS 수준 격리(OS-level isolation) 또는 **컨테이너화(Containerization)**가 더 정확한 표현임.


⚙️ 컨테이너의 핵심 기술 (Linux)

컨테이너는 Linux 커널의 두 가지 핵심 기능을 활용함.

Namespace

프로세스가 볼 수 있는 시스템 자원의 범위를 격리하는 메커니즘

각 컨테이너는 자신만의 namespace를 가져서 마치 독립된 시스템처럼 보임.

Namespace격리 대상설명
PID프로세스 ID컨테이너 내부에서 PID 1부터 시작
NET네트워크독립된 네트워크 인터페이스, IP, 포트
MNT마운트 포인트독립된 파일시스템 뷰
UTS호스트명독립된 hostname
IPCIPC 자원독립된 세마포어, 메시지 큐
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)

프로세스가 사용할 수 있는 자원의 양을 제한하는 메커니즘

자원설명
CPUCPU 시간 제한
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

주요 런타임

런타임레벨설명
DockerHigh가장 유명, CLI/이미지 빌드/레지스트리 통합
PodmanHighDocker 호환, 데몬리스, rootless
containerdMidDocker에서 분리, Kubernetes 기본 런타임
CRI-OMidKubernetes 전용 경량 런타임
runcLowOCI 표준 구현체, 실제 컨테이너 생성

📦 컨테이너 이미지

이미지(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를 실행하는 구조.


🔗 관련 포스트


🔗 참고 자료