LabHub

블로그

containerd 아키텍처 내부 분석

한국어English日本語

containerd 아키텍처 내부 분석

containerd는 업계 표준 컨테이너 런타임으로, Docker에서 분리되어 독립 프로젝트로 발전했습니다. Kubernetes CRI(Container Runtime Interface)를 구현하며, 컨테이너의 전체 생명주기를 관리합니다.


1. containerd 개요

1.1 위치와 역할

containerd는 컨테이너 스택에서 중간 계층에 위치합니다:

Kubernetes (kubelet)
    |
    v (CRI gRPC)
containerd
    |
    v (OCI runtime)
runc / kata / gVisor
    |
    v
Linux kernel (namespaces, cgroups)

주요 역할:

1.2 핵심 설계 원칙


2. gRPC API 구조

2.1 주요 서비스

containerd는 다음 gRPC 서비스를 제공합니다:

2.2 네임스페이스

containerd의 네임스페이스는 Kubernetes 네임스페이스와 다릅니다. containerd 내부에서 리소스를 격리하는 메커니즘입니다:

# 네임스페이스별 컨테이너 확인
ctr -n k8s.io containers list
ctr -n moby containers list

3. 플러그인 시스템

3.1 플러그인 타입

containerd의 모든 기능은 플러그인으로 구현됩니다:

타입설명예시
ServicegRPC 서비스 구현containers, content, images
Snapshotter파일시스템 스냅샷 관리overlayfs, native, devmapper
Runtime컨테이너 런타임io.containerd.runc.v2
GC가비지 컬렉션metadata GC
Content Store콘텐츠 저장소local content store
Metadata메타데이터 저장소bolt metadata store
Differ레이어 diff 계산walking differ

3.2 플러그인 등록

// 플러그인 등록 예시
func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.ServicePlugin,
        ID:   "containers-service",
        Requires: []plugin.Type{
            plugin.MetadataPlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            m, err := ic.Get(plugin.MetadataPlugin)
            // 서비스 초기화 로직
            return newContainerService(m), nil
        },
    })
}

3.3 의존성 해결

containerd는 시작 시 모든 플러그인의 의존성을 분석하고 올바른 순서로 초기화합니다:

  1. 모든 등록된 플러그인 수집
  2. 의존성 그래프 구성
  3. 위상 정렬(topological sort)로 초기화 순서 결정
  4. 순서대로 플러그인 초기화

3.4 플러그인 설정

containerd의 설정 파일(config.toml)에서 플러그인별 설정:

version = 2

[plugins."io.containerd.grpc.v1.cri"]
  sandbox_image = "registry.k8s.io/pause:3.9"

[plugins."io.containerd.grpc.v1.cri".containerd]
  default_runtime_name = "runc"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  runtime_type = "io.containerd.runc.v2"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true

4. CRI 구현

4.1 CRI 서비스

containerd는 CRI gRPC 서비스를 내장 플러그인으로 구현합니다:

4.2 CRI 요청 흐름

kubelet의 Pod 생성 요청이 처리되는 과정:

  1. kubelet이 CRI의 RunPodSandbox 호출
  2. containerd가 pause 컨테이너 생성 (네트워크 네임스페이스 설정)
  3. CNI 플러그인 호출하여 네트워크 구성
  4. kubelet이 CRI의 CreateContainer + StartContainer 호출
  5. containerd가 shim 프로세스를 통해 컨테이너 실행

5. Shim v2 아키텍처

5.1 Shim의 역할

Shim은 containerd와 실제 컨테이너 런타임(runc) 사이의 중간 프로세스입니다:

containerd --> shim (containerd-shim-runc-v2) --> runc --> container process

Shim의 책임:

5.2 Shim v2 프로토콜

Shim v2는 ttrpc(tiny-ttrpc) 프로토콜을 사용합니다. ttrpc는 gRPC의 경량 버전으로:

5.3 Shim 생명주기

  1. containerd가 shim 바이너리를 실행 (fork/exec)
  2. shim이 ttrpc 소켓을 생성하고 주소를 반환
  3. containerd가 ttrpc를 통해 shim과 통신
  4. shim이 runc를 호출하여 컨테이너 생성/실행
  5. containerd 재시작 시 기존 shim에 재연결

5.4 다양한 Shim 구현

Shim런타임설명
containerd-shim-runc-v2runc기본 OCI 런타임
containerd-shim-kata-v2Kata경량 VM 기반
containerd-shim-runsc-v1gVisor사용자 공간 커널
containerd-shim-wasmWasmWebAssembly 런타임

6. 메타데이터 저장소

6.1 BoltDB 메타데이터

containerd는 BoltDB를 메타데이터 저장소로 사용합니다:

/var/lib/containerd/
  io.containerd.metadata.v1.bolt/
    meta.db          # BoltDB 메타데이터
  io.containerd.content.v1.content/
    blobs/sha256/    # 콘텐츠 저장소
  io.containerd.snapshotter.v1.overlayfs/
    snapshots/       # overlayfs 스냅샷

6.2 가비지 컬렉션

containerd의 GC는 참조되지 않는 리소스를 정리합니다:


7. 정리

containerd의 플러그인 기반 아키텍처는 높은 확장성과 유연성을 제공합니다. Shim v2의 프로세스 격리 모델은 containerd 재시작에도 컨테이너를 안정적으로 유지하며, ttrpc를 통한 효율적인 통신으로 오버헤드를 최소화합니다. 다음 글에서는 containerd의 이미지 관리와 스냅샷 시스템을 상세히 분석합니다.

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다