컨테이너 기술의 진화와 Linux 격리 기술
0. 참고 자료
- OCI Specifications - 컨테이너 표준 명세
- Linux Kernel Documentation - 커널 기능 문서
- containerd Documentation - containerd 공식 문서
- CRI-O Documentation - CRI-O 공식 문서
- Kubernetes CRI Documentation - CRI 표준 문서
쿠버네티스를 제대로 이해하기 위해서는 먼저 기반 기술들에 대한 이해가 필요하다. 컨테이너는 애플리케이션과 그 실행에 필요한 모든 의존성을 패키징한 경량화된 실행 단위다. 가상머신과 달리 호스트 OS의 커널을 공유하면서도 프로세스 수준에서 격리를 제공한다.
1. 컨테이너란 무엇인가?
컨테이너는 애플리케이션과 그 실행에 필요한 모든 의존성을 하나로 패키징한 경량화된 실행 단위다. 가상머신(VM)과 달리 호스트 OS의 커널을 공유하면서도 프로세스 수준에서 완벽한 격리를 제공한다.
가상머신 vs 컨테이너 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────┐
│ VM vs Container │
├─────────────────────────────────────────────────────────────┤
│ │
│ Virtual Machine Container │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Application │ │ Application │ │
│ ├─────────────────┤ ├─────────────────────────┤ │
│ │ Bins │ │ Bins & Libraries │ │
│ │ Libraries │ │ │ │
│ ├─────────────────┤ ├─────────────────────────┤ │
│ │ Guest OS │ │ Container Runtime │ │
│ ├─────────────────┤ ├─────────────────────────┤ │
│ │ Hypervisor │ │ │ │
│ ├─────────────────┤ │ Host OS Kernel │ │
│ │ Host OS │ │ │ │
│ ├─────────────────┤ ├─────────────────────────┤ │
│ │ Hardware │ │ Hardware │ │
│ └─────────────────┘ └─────────────────────────┘ │
│ │
│ • 무겁다 (GB 단위) • 가볍다 (MB 단위) │
│ • 부팅 시간 길다 • 빠른 시작 (초 단위) │
│ • 완전한 격리 • 커널 공유 │
│ • 리소스 오버헤드 큼 • 효율적 리소스 사용 │
└─────────────────────────────────────────────────────────────┘
가상머신의 특징:
- 각 VM마다 완전한 OS를 실행
- 하드웨어 가상화 기반
- 강력한 격리 제공, 하지만 리소스 오버헤드가 크다
- 부팅에 분 단위 시간이 필요
컨테이너의 특징:
- 호스트 OS 커널을 공유
- OS 수준 가상화 기반
- 효율적인 리소스 사용
- 초 단위로 빠른 시작
- 프로세스 수준 격리 제공
2. Linux 격리 기술의 핵심 요소
컨테이너의 격리는 세 가지 핵심 Linux 기술로 구현된다: Namespaces, Cgroups, 파일시스템 격리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│ Container Isolation │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Namespaces │ │ Cgroups │ │ Filesystem │ │
│ │ │ │ │ │ Isolation │ │
│ │ - PID │ │ - Memory │ │ - chroot │ │
│ │ - Network │ │ - CPU │ │ - pivot_root │ │
│ │ - Mount │ │ - Block I/O │ │ - Union FS │ │
│ │ - UTS │ │ - Devices │ │ │ │
│ │ - IPC │ │ - Network │ │ │ │
│ │ - User │ │ │ │ │ │
│ │ - Cgroup │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ "무엇을" "얼마나" "어디에서" │
│ 볼 수 있나 사용할 수 있나 실행되는가 │
└─────────────────────────────────────────────────────────────┘
2.1 Namespaces: “무엇을 볼 수 있는가”
네임스페이스는 커널 리소스를 추상화해서 각 프로세스가 독립된 시스템 뷰를 가지게 만든다. 같은 하드웨어에서 실행되지만, 각 컨테이너는 마치 독립된 시스템에서 실행되는 것처럼 보인다.
PID Namespace: 프로세스 격리
1
2
3
4
5
6
7
8
9
10
Host System (PID Namespace 0)
├── PID 1: systemd
├── PID 1234: docker daemon
├── PID 2000: Container 1 (새로운 PID Namespace)
│ ├── PID 1: application (실제 Host PID: 2000)
│ ├── PID 2: worker (실제 Host PID: 2001)
│ └── PID 3: helper (실제 Host PID: 2002)
└── PID 3000: Container 2 (또 다른 PID Namespace)
├── PID 1: application (실제 Host PID: 3000)
└── PID 2: monitor (실제 Host PID: 3001)
PID Namespace의 핵심 특징:
- 각 컨테이너는 자체 PID 1을 가진다 (init 프로세스)
- 컨테이너 내부에서는 다른 컨테이너의 프로세스를 볼 수 없다
- 호스트에서는 모든 프로세스를 볼 수 있다
- 계층적 구조로 부모 네임스페이스가 자식을 관리할 수 있다
Network Namespace: 네트워크 격리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────────┐
│ Network Namespace │
├─────────────────────────────────────────────────────────────┤
│ │
│ Host Network Container Network │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ eth0 │ │ eth0 (veth pair) │ │
│ │ 192.168.1.100 │ │ 10.0.1.100 │ │
│ │ │ │ │ │
│ │ Routing Table │ │ Routing Table │ │
│ │ - 0.0.0.0/0 │ │ - 0.0.0.0/0 → Gateway │ │
│ │ via Gateway │ │ │ │
│ │ │ │ iptables Rules │ │
│ │ iptables Rules │ │ - INPUT: ACCEPT │ │
│ │ - Complex rules │ │ - OUTPUT: ACCEPT │ │
│ └─────────────────┘ └─────────────────────────┘ │
│ │ │ │
│ └────────── Bridge ──────────┘ │
│ (docker0) │
└─────────────────────────────────────────────────────────────┘
Network Namespace의 구성 요소:
- 네트워크 인터페이스: 각 컨테이너는 독립된 네트워크 인터페이스 보유
- 라우팅 테이블: 독립된 라우팅 규칙 설정 가능
- iptables 규칙: 컨테이너별 방화벽 정책 적용
- 소켓: TCP/UDP 포트 공간 분리
Mount Namespace: 파일시스템 격리
Mount 네임스페이스는 각 컨테이너가 독립된 파일시스템 뷰를 가지게 한다.
1
2
3
4
5
6
7
8
9
10
11
Host Filesystem Container Filesystem
/ /
├── bin/ ├── bin/ (bind mount)
├── etc/ ├── etc/ (container specific)
├── proc/ ├── proc/ (새로운 procfs)
├── sys/ ├── sys/ (새로운 sysfs)
├── var/ ├── tmp/ (tmpfs)
│ └── lib/ └── app/ (application files)
│ └── docker/
│ └── containers/
└── tmp/
User Namespace: 권한 격리
User 네임스페이스는 가장 중요한 보안 기능 중 하나다. 컨테이너 내부의 root가 호스트의 일반 사용자로 매핑된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────┐
│ User Namespace Mapping │
├─────────────────────────────────────────────────────────────┤
│ │
│ Container View Host View │
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ UID 0 (root) │ → │ UID 1000 (containeruser) │ │
│ │ UID 1 (daemon) │ → │ UID 1001 (containeruser+1) │ │
│ │ UID 2 (bin) │ → │ UID 1002 (containeruser+2) │ │
│ │ ... │ │ ... │ │
│ │ GID 0 (root) │ → │ GID 1000 (containergroup) │ │
│ │ GID 1 (daemon) │ → │ GID 1001 (containergroup+1) │ │
│ └─────────────────┘ └─────────────────────────────┘ │
│ │
│ 장점: │
│ • 컨테이너 root ≠ 호스트 root │
│ • 호스트 시스템 보안 강화 │
│ • 권한 최소화 원칙 적용 │
└─────────────────────────────────────────────────────────────┘
User Namespace의 보안 이점:
- 컨테이너 내부의 root 사용자가 호스트에서는 일반 사용자로 실행
- 컨테이너가 탈출해도 제한된 권한만 보유
- 호스트 시스템 파일에 대한 접근 제한
2.2 Cgroups: “얼마나 사용할 수 있는가”
Control Groups(Cgroups)는 프로세스 그룹의 리소스 사용량을 제한, 격리, 측정하는 Linux 커널 기능이다.
Cgroups v1 vs v2 진화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Cgroups v1 (각 리소스별 독립 계층)
┌─────────────────────────────────────────────────────────────┐
│ /sys/fs/cgroup/ │
│ ├── memory/ ├── cpu/ ├── blkio/ │
│ │ ├── container1/ │ ├── container1/ │ ├── ... │
│ │ └── container2/ │ └── container2/ │ └── ... │
│ │ │ │ │
│ ├── devices/ ├── freezer/ ├── net_cls/ │
│ │ ├── container1/ │ ├── container1/ │ ├── ... │
│ │ └── container2/ │ └── container2/ │ └── ... │
│ │
│ 문제점: 계층 불일치, 복잡한 관리, 성능 이슈 │
└─────────────────────────────────────────────────────────────┘
Cgroups v2 (통합 계층 구조)
┌─────────────────────────────────────────────────────────────┐
│ /sys/fs/cgroup/ │
│ ├── container1/ │
│ │ ├── memory.max │
│ │ ├── cpu.max │
│ │ ├── io.max │
│ │ └── cgroup.controllers │
│ │ │
│ └── container2/ │
│ ├── memory.max │
│ ├── cpu.max │
│ ├── io.max │
│ └── cgroup.controllers │
│ │
│ 장점: 단일 계층, 일관된 인터페이스, 향상된 성능 │
└─────────────────────────────────────────────────────────────┘
Cgroups v1의 문제점:
- 각 리소스 타입마다 별도의 계층 구조
- 관리 복잡성 증가
- 계층 간 불일치 가능성
Cgroups v2의 개선점:
- 단일 통합 계층 구조
- 일관된 API 인터페이스
- 더 나은 성능과 확장성
주요 Cgroup 컨트롤러
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────────┐
│ Cgroup Controllers │
├─────────────────────────────────────────────────────────────┤
│ │
│ Memory Controller │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • memory.limit_in_bytes → 메모리 최대 사용량 │ │
│ │ • memory.usage_in_bytes → 현재 메모리 사용량 │ │
│ │ • memory.oom_control → OOM Killer 제어 │ │
│ │ • memory.stat → 메모리 통계 정보 │ │
│ │ • memory.swappiness → Swap 사용 정책 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ CPU Controller │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • cpu.cfs_quota_us → CPU 시간 할당량 │ │
│ │ • cpu.cfs_period_us → CPU 스케줄링 주기 │ │
│ │ • cpu.shares → CPU 가중치 │ │
│ │ • cpuacct.usage → CPU 사용 시간 통계 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Block I/O Controller │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • blkio.throttle.read_bps_device → 읽기 대역폭 제한 │ │
│ │ • blkio.throttle.write_bps_device → 쓰기 대역폭 제한 │ │
│ │ • blkio.throttle.read_iops_device → 읽기 IOPS 제한 │ │
│ │ • blkio.weight → I/O 우선순위 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
실제 사용 예시:
1
2
3
4
5
6
7
8
9
# 메모리 제한 설정 (512MB)
echo 536870912 > /sys/fs/cgroup/memory/container1/memory.limit_in_bytes
# CPU 사용률 제한 (50%)
echo 50000 > /sys/fs/cgroup/cpu/container1/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/container1/cpu.cfs_period_us
# 블록 I/O 읽기 속도 제한 (10MB/s)
echo "8:0 10485760" > /sys/fs/cgroup/blkio/container1/blkio.throttle.read_bps_device
2.3 파일시스템 격리: “어디에서 실행되는가”
chroot vs pivot_root 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────┐
│ Filesystem Isolation Comparison │
├─────────────────────────────────────────────────────────────┤
│ │
│ chroot (Change Root) - 전통적 방식 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Host FS: │ │
│ │ / │ │
│ │ ├── bin/ │ │
│ │ ├── etc/ │ │
│ │ ├── proc/ ← 여전히 접근 가능 (보안 위험) │ │
│ │ ├── sys/ ← 여전히 접근 가능 (보안 위험) │ │
│ │ └── jail/ │ │
│ │ ├── bin/ ← chroot 후 새로운 / │ │
│ │ ├── lib/ │ │
│ │ └── app/ │ │
│ │ │ │
│ │ 문제점: 불완전한 격리, 보안 취약점 존재 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ pivot_root (Pivot Root) - 현대적 방식 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 새로운 루트 준비: │ │
│ │ /new_root/ │ │
│ │ ├── bin/ │ │
│ │ ├── lib/ │ │
│ │ ├── app/ │ │
│ │ └── .old/ ← 기존 루트가 이동될 위치 │ │
│ │ │ │
│ │ 2. pivot_root 실행: │ │
│ │ pivot_root /new_root /new_root/.old │ │
│ │ │ │
│ │ 3. 기존 루트 제거: │ │
│ │ umount /.old │ │
│ │ rmdir /.old │ │
│ │ │ │
│ │ 장점: 완전한 루트 파일시스템 변경, 보안성 향상 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
chroot의 한계:
- 단순히 루트 디렉토리만 변경
- /proc, /sys 등 특수 파일시스템은 여전히 접근 가능
- 보안 취약점 존재 (chroot 탈출 가능)
pivot_root의 장점:
- 완전한 루트 파일시스템 교체
- 이전 루트를 안전하게 분리
- 더 강력한 보안성 제공
3. 컨테이너 런타임 생태계의 진화
3.1 컨테이너 기술 발전 타임라인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
컨테이너 기술 진화 과정
┌─────────────────────────────────────────────────────────────┐
│ │
│ 2013 2014 2015 2016 2017 2018-2024 │
│ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ Docker K8s OCI CRI dockershim containerd │
│ 1.0 1.0 창설 도입 임시방편 표준채택 │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────────┐ ┌─────────┐ │
│ │단일 │ │오케 │ │표준 │ │인터 │ │호환성 │ │통합 │ │
│ │런타 │ │스트 │ │화 │ │페이 │ │유지 │ │런타임 │ │
│ │임 │ │레이 │ │필요 │ │스 │ │ │ │생태계 │ │
│ │독점 │ │션 │ │성 │ │표준 │ │ │ │ │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────┘
각 단계별 주요 특징:
2013년 - Docker의 등장:
- 컨테이너 기술의 대중화
- 단일 벤더(Docker Inc.) 중심 생태계
- 개발자 친화적 인터페이스 제공
2014년 - Kubernetes 등장:
- 구글의 컨테이너 오케스트레이션 플랫폼
- 클러스터 단위 컨테이너 관리
- Docker와의 긴밀한 통합
2015년 - OCI(Open Container Initiative) 창설:
- 컨테이너 표준화 필요성 대두
- Docker, CoreOS, Google 등 주요 업체 참여
- 벤더 종속성 해결 시작
2016년 - CRI(Container Runtime Interface) 도입:
- Kubernetes의 런타임 추상화
- 다양한 컨테이너 런타임 지원
- 플러거블 아키텍처 구현
2017년 - dockershim의 임시방편:
- Docker와 CRI 사이의 호환성 레이어
- Kubernetes 내장 컴포넌트로 제공
- 기술적 부채로 인식
2018-2024년 - containerd 표준 채택:
- Docker에서 containerd 분리
- 직접적인 CRI 지원
- 성능 및 보안 개선
3.2 OCI(Open Container Initiative) 표준
OCI (Open Container Initiative): 컨테이너 이미지 포맷과 런타임 실행 방식을 표준화한 업계 공통 규약이다. 즉, “컨테이너를 어떻게 만들고 실행할지”의 표준인 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌─────────────────────────────────────────────────────────────┐
│ OCI Standards │
├─────────────────────────────────────────────────────────────┤
│ │
│ Runtime Specification (어떻게 실행할 것인가) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ config.json │ │
│ │ { │ │
│ │ "ociVersion": "1.0.2", │ │
│ │ "process": { │ │
│ │ "args": ["/bin/sh"], │ │
│ │ "env": ["PATH=/usr/bin"], │ │
│ │ "cwd": "/", │ │
│ │ "capabilities": { │ │
│ │ "bounding": ["CAP_NET_BIND_SERVICE"] │ │
│ │ } │ │
│ │ }, │ │
│ │ "root": { "path": "rootfs" }, │ │
│ │ "linux": { │ │
│ │ "namespaces": [ │ │
│ │ {"type": "pid"}, {"type": "network"} │ │
│ │ ], │ │
│ │ "cgroupsPath": "/container" │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Image Specification (어떻게 패키징할 것인가) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Container Image 구조 │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ manifest.json (이미지 메타데이터) │ │ │
│ │ │ ├── config.json (이미지 설정) │ │ │
│ │ │ └── layers/ (파일시스템 레이어) │ │ │
│ │ │ ├── layer1.tar.gz (베이스 OS) │ │ │
│ │ │ ├── layer2.tar.gz (미들웨어) │ │ │
│ │ │ └── layer3.tar.gz (애플리케이션) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Distribution Specification (어떻게 배포할 것인가) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Registry API Protocol │ │
│ │ • GET /v2/{name}/manifests/{reference} │ │
│ │ • PUT /v2/{name}/manifests/{reference} │ │
│ │ • GET /v2/{name}/blobs/{digest} │ │
│ │ • POST /v2/{name}/blobs/uploads/ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
OCI 표준의 중요성:
- 상호 운용성: 다양한 도구들이 같은 이미지를 사용할 수 있다
- 벤더 중립성: 특정 회사에 종속되지 않는 표준
- 혁신 촉진: 표준화된 인터페이스로 새로운 도구 개발 촉진
3.3 CRI(Container Runtime Interface) 아키텍처
CRI (Container Runtime Interface): Kubernetes가 다양한 컨테이너 런타임과 통신하기 위한 표준 API 인터페이스다. 즉, “Kubernetes가 컨테이너 런타임과 어떻게 대화할지”의 표준인 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
┌─────────────────────────────────────────────────────────────┐
│ CRI Interface Architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ kubelet (Kubernetes Node Agent) │
│ │ │
│ │ gRPC calls │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CRI gRPC API │ │
│ │ │ │
│ │ RuntimeService ImageService │ │
│ │ ┌─────────────────────┐ ┌─────────────────┐ │ │
│ │ │ • RunPodSandbox │ │ • PullImage │ │ │
│ │ │ • StopPodSandbox │ │ • RemoveImage │ │ │
│ │ │ • CreateContainer │ │ • ImageStatus │ │ │
│ │ │ • StartContainer │ │ • ListImages │ │ │
│ │ │ • StopContainer │ │ │ │ │
│ │ │ • RemoveContainer │ │ │ │ │
│ │ │ • ListContainers │ │ │ │ │
│ │ │ • ContainerStatus │ │ │ │ │
│ │ │ • ExecSync │ │ │ │ │
│ │ │ • Exec │ │ │ │ │
│ │ └─────────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ containerd │ │ CRI-O │ │
│ │ │ │ │ │
│ │ • gRPC Server │ │ • gRPC Server │ │
│ │ • Container Mgmt │ │ • Container Mgmt │ │
│ │ • Image Mgmt │ │ • Image Mgmt │ │
│ │ • Snapshot Mgmt │ │ • Storage Mgmt │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ OCI Runtime │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ runc │ │ crun │ │ kata │ │ gVisor │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
CRI의 핵심 구성 요소:
RuntimeService (컨테이너 생명주기 관리):
- RunPodSandbox: 파드의 격리 환경 생성
- CreateContainer: 컨테이너 생성
- StartContainer: 컨테이너 시작
- StopContainer: 컨테이너 중지
- ExecSync: 컨테이너 내 명령어 실행
ImageService (이미지 관리):
- PullImage: 이미지 다운로드
- RemoveImage: 이미지 삭제
- ImageStatus: 이미지 상태 확인
- ListImages: 이미지 목록 조회
CRI의 장점:
- 플러거블 아키텍처: 다양한 런타임 지원
- 표준화된 인터페이스: 일관된 API 제공
- 유연성: 용도에 맞는 런타임 선택 가능
3.4 현대 컨테이너 런타임 계층구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
┌─────────────────────────────────────────────────────────────┐
│ Modern Container Runtime Stack │
├─────────────────────────────────────────────────────────────┤
│ │
│ High-level Runtime (사용자 인터페이스 레이어) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Docker │ │ Podman │ │ nerdctl │ │ │
│ │ │ Engine │ │ │ │ │ │ │
│ │ │ │ │ • 데몬리스 │ │ • Docker │ │ │
│ │ │ • 빌드 │ │ • Rootless │ │ 호환성 │ │ │
│ │ │ • 레지스트리│ │ • systemd │ │ • Compose │ │ │
│ │ │ • 네트워킹 │ │ pods │ │ 지원 │ │ │
│ │ │ • 볼륨 │ │ │ │ │ │ │
│ │ │ • Compose │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Container Runtime (CRI 구현 레이어) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ containerd │ │ CRI-O │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Content Store │ │ │ │ Image Store │ │ │ │
│ │ │ │ (이미지/Blob) │ │ │ │ (containers/img)│ │ │ │
│ │ │ └─────────────────┘ │ │ └─────────────────┘ │ │ │
│ │ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Snapshot Store │ │ │ │ Storage Driver │ │ │ │
│ │ │ │ (파일시스템) │ │ │ │ (containers/stg)│ │ │ │
│ │ │ └─────────────────┘ │ │ └─────────────────┘ │ │ │
│ │ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Task Service │ │ │ │ Runtime Service │ │ │ │
│ │ │ │ (생명주기) │ │ │ │ (생명주기) │ │ │ │
│ │ │ └─────────────────┘ │ │ └─────────────────┘ │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ Low-level Runtime (OCI 구현 레이어) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ runc │ │ crun │ │ kata │ │ gVisor │ │ │
│ │ │ │ │ │ │containers│ │ (runsc) │ │ │
│ │ │ • Go │ │ • C │ │ │ │ │ │ │
│ │ │ • OCI │ │ • 빠름 │ │ • VM │ │ • 사용자│ │ │
│ │ │ 표준 │ │ • 경량 │ │ 기반 │ │ 공간 │ │ │
│ │ │ • 기본 │ │ • 메모리│ │ • 강력한│ │ • 커널 │ │ │
│ │ │ 구현 │ │ 효율 │ │ 격리 │ │ 우회 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Operating System (커널 인터페이스 레이어) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Linux Kernel │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Namespaces │ │ Cgroups │ │ Capabilities│ │ │
│ │ │ (격리) │ │ (리소스제한)│ │ (권한제어) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
각 레이어의 역할:
High-level Runtime:
- 사용자 친화적 인터페이스 제공
- 이미지 빌드, 네트워킹, 볼륨 관리
- 개발 및 운영 도구 통합
Container Runtime:
- CRI 표준 구현
- 이미지 관리 및 스토리지
- 컨테이너 생명주기 관리
Low-level Runtime:
- OCI 표준 구현
- 실제 컨테이너 프로세스 생성
- 커널 기능 직접 활용
Operating System:
- 기본 격리 기술 제공
- 리소스 관리 및 보안
3.5 containerd vs CRI-O 상세 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
┌─────────────────────────────────────────────────────────────┐
│ containerd vs CRI-O 비교 │
├─────────────────────────────────────────────────────────────┤
│ │
│ containerd Architecture │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ containerd daemon │ │
│ │ ┌─────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ gRPC │ │ Content Store │ │ │
│ │ │ API │ │ (이미지 & Blob 저장소) │ │ │
│ │ │ Server │ │ │ │ │
│ │ └─────────────┘ └─────────────────────────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ Metadata │ │ Snapshot Store │ │ │
│ │ │ Store │ │ (레이어 관리) │ │ │
│ │ │ (BoltDB) │ │ │ │ │
│ │ └─────────────┘ └─────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Task Service │ │ │
│ │ │ (컨테이너 런타임) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ containerd-shim │ │
│ │ (컨테이너당 하나의 프로세스) │ │
│ │ │ │
│ │ • 컨테이너 프로세스 생명주기 관리 │ │
│ │ • STDIO 포워딩 │ │
│ │ • 종료 상태 보고 │ │
│ │ • 데몬 재시작 시에도 컨테이너 유지 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ CRI-O Architecture │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CRI-O daemon │ │
│ │ ┌─────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ gRPC │ │ containers/image │ │ │
│ │ │ API │ │ (이미지 관리) │ │ │
│ │ │ Server │ │ │ │ │
│ │ └─────────────┘ └─────────────────────────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ Pod Sandbox │ │ containers/storage │ │ │
│ │ │ Management │ │ (스토리지 드라이버) │ │ │
│ │ └─────────────┘ └─────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Runtime Management │ │ │
│ │ │ (컨테이너 생명주기) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ conmon │ │
│ │ (컨테이너 모니터) │ │
│ │ │ │
│ │ • 프로세스 모니터링 │ │
│ │ • 로그 수집 │ │
│ │ • TTY 처리 │ │
│ │ • 종료 코드 보고 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
주요 차이점:
containerd:
- 범용성: Docker와 Kubernetes 모두 지원
- 모듈화: 플러그인 아키텍처로 확장 가능
- 성능: 최적화된 이미지 스토리지
- 안정성: 프로덕션 환경에서 검증됨
CRI-O:
- 특화: Kubernetes 전용으로 설계
- 경량: 불필요한 기능 제거
- 보안: 최소 권한 원칙 적용
- 표준: OCI 표준 엄격 준수
3.6 Docker에서 containerd로의 마이그레이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
┌─────────────────────────────────────────────────────────────┐
│ Docker → containerd 마이그레이션 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Before: Docker + dockershim (비효율적) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ kubelet │ │
│ │ │ CRI 호출 │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ dockershim │ │ │
│ │ │ (CRI → Docker API 변환) │ │ │
│ │ │ │ │ │
│ │ │ • CRI gRPC → Docker REST API 변환 │ │ │
│ │ │ • Kubernetes 내장 컴포넌트 │ │ │
│ │ │ • 임시적 호환성 레이어 │ │ │
│ │ │ • 성능 오버헤드 발생 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ Docker API 호출 │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Docker Engine │ │ │
│ │ │ │ │ │
│ │ │ • 전체 Docker 기능 로드 │ │ │
│ │ │ • 불필요한 빌드, 네트워킹 기능 │ │ │
│ │ │ • 메모리 오버헤드 │ │ │
│ │ │ • 복잡한 아키텍처 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ containerd │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ runc │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ After: 직접 containerd (효율적) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ kubelet │ │
│ │ │ CRI gRPC 직접 호출 │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ containerd │ │ │
│ │ │ (네이티브 CRI) │ │ │
│ │ │ │ │ │
│ │ │ • 직접 CRI gRPC 지원 │ │ │
│ │ │ • 경량화된 컨테이너 런타임 │ │ │
│ │ │ • 불필요한 레이어 제거 │ │ │
│ │ │ • 최적화된 성능 │ │ │
│ │ │ • 더 나은 보안 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ runc │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ 마이그레이션 이점: │
│ ┌─────────────────────────────────────────────┐ │
│ │ • 지연 시간 감소 (30-50% 개선) │ │
│ │ • 메모리 사용량 감소 (20-30% 절약) │ │
│ │ • CPU 오버헤드 감소 │ │
│ │ • 더 나은 보안성 │ │
│ │ • 미래 지향적 아키텍처 │ │
│ │ • 단순화된 아키텍처 │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
마이그레이션 과정:
- 평가 단계: 현재 Docker 의존성 분석
- 테스트 환경: containerd로 파일럿 클러스터 구성
- 점진적 마이그레이션: 노드별 순차적 전환
- 검증: 성능 및 안정성 확인
- 완료: 모든 워커 노드 전환
4. CLI 도구의 진화
4.1 Docker에서 crictl로의 명령어 변화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌─────────────────────────────────────────────────────────────┐
│ CLI Tools 진화 & 비교 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Docker Era → containerd Era 명령어 매핑 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Docker 명령어 → crictl 명령어 │ │
│ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ docker ps │ → │ crictl ps │ │ │
│ │ │ docker images │ → │ crictl images │ │ │
│ │ │ docker exec │ → │ crictl exec │ │ │
│ │ │ docker logs │ → │ crictl logs │ │ │
│ │ │ docker inspect │ → │ crictl inspect │ │ │
│ │ │ docker stats │ → │ crictl stats │ │ │
│ │ │ docker pull │ → │ crictl pull │ │ │
│ │ │ docker version │ → │ crictl version │ │ │
│ │ │ docker info │ → │ crictl info │ │ │
│ │ └─────────────────┘ └─────────────────────────┘ │ │
│ │ │ │
│ │ crictl에서 사용할 수 없는 명령어: │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ • docker run (kubectl run 사용) │ │ │
│ │ │ • docker build (다른 빌드 도구 사용) │ │ │
│ │ │ • docker commit (권장하지 않는 패턴) │ │ │
│ │ │ • docker push (레지스트리 도구 사용) │ │ │
│ │ │ • docker network (CNI 플러그인 사용) │ │ │
│ │ │ • docker volume (쿠버네티스 볼륨 사용) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
중요한 변화:
- crictl은 디버깅 및 검사 도구로 설계됨
- 컨테이너 생성/실행은 kubectl을 통해 수행
- 개발용 기능들은 별도 도구로 분리
4.2 CLI 도구 기능 비교
도구 | 목적 | 주요 기능 | 대상 사용자 |
---|---|---|---|
Docker | 개발 & 운영 | 빌드, 실행, 네트워킹, 볼륨, Compose | 개발자, DevOps |
crictl | 디버깅 & 검사 | CRI 인터페이스, 파드 샌드박스, 컨테이너 관리 | 클러스터 관리자 |
nerdctl | 개발 (containerd) | Docker 호환, Compose, Rootless, K8s 네임스페이스 | 개발자 |
podman | 개발 & 운영 | 데몬리스, Rootless, 파드 지원, systemd | 시스템 관리자 |
결론
컨테이너 기술은 Linux 커널의 기본 격리 기능들을 조합해서 만들어진 기술이다. Namespaces, Cgroups, 파일시스템 격리라는 세 가지 핵심 기술을 통해 가벼우면서도 안전한 애플리케이션 실행 환경을 제공한다.
Docker로 시작된 컨테이너 혁명은 이제 OCI 표준과 CRI 인터페이스를 통해 성숙한 생태계로 발전했다. 특히 containerd와 CRI-O 같은 전문화된 런타임의 등장으로 더욱 효율적이고 안전한 컨테이너 환경을 구축할 수 있게 되었다.
이러한 기반 기술에 대한 깊은 이해는 쿠버네티스를 효과적으로 운영하고 문제를 해결하는 데 필수적이다.