Post

컨테이너 기술의 진화와 Linux 격리 기술

컨테이너 기술의 진화와 Linux 격리 기술

0. 참고 자료


쿠버네티스를 제대로 이해하기 위해서는 먼저 기반 기술들에 대한 이해가 필요하다. 컨테이너는 애플리케이션과 그 실행에 필요한 모든 의존성을 패키징한 경량화된 실행 단위다. 가상머신과 달리 호스트 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 오버헤드 감소                        │   │
│  │ • 더 나은 보안성                           │   │
│  │ • 미래 지향적 아키텍처                     │   │
│  │ • 단순화된 아키텍처                        │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

마이그레이션 과정:

  1. 평가 단계: 현재 Docker 의존성 분석
  2. 테스트 환경: containerd로 파일럿 클러스터 구성
  3. 점진적 마이그레이션: 노드별 순차적 전환
  4. 검증: 성능 및 안정성 확인
  5. 완료: 모든 워커 노드 전환

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 인터페이스를 통해 성숙한 생태계로 발전했다. 특히 containerdCRI-O 같은 전문화된 런타임의 등장으로 더욱 효율적이고 안전한 컨테이너 환경을 구축할 수 있게 되었다.

이러한 기반 기술에 대한 깊은 이해는 쿠버네티스를 효과적으로 운영하고 문제를 해결하는 데 필수적이다.

This post is licensed under CC BY 4.0 by the author.