Skip to content
Published on

containerd 이미지 관리: OCI 이미지와 스냅샷

Authors

containerd 이미지 관리: OCI 이미지와 스냅샷

containerd의 이미지 관리 서브시스템은 OCI 이미지 스펙을 기반으로 이미지를 저장, 배포, 언패킹하는 핵심 기능을 담당합니다. 이 글에서는 Content Store, Snapshotter, 이미지 Pull 플로우, 가비지 컬렉션의 내부 동작을 분석합니다.


1. OCI 이미지 스펙

1.1 이미지 구조

OCI 이미지는 세 가지 핵심 구성 요소로 이루어집니다:

OCI 이미지 구조:

1. Image Index (Fat Manifest)
   - 여러 플랫폼(linux/amd64, linux/arm64 등)을 지원
   - 각 플랫폼별 Manifest를 가리킴

2. Image Manifest
   - Config 객체의 다이제스트
   - 레이어 목록 (순서 보장)
   - 미디어 타입 정보

3. Image Config
   - 환경 변수, 엔트리포인트, CMD
   - 레이어 diff ID 목록
   - 생성 히스토리

1.2 콘텐츠 주소 지정

콘텐츠 주소 지정(Content Addressable Storage):

모든 객체는 SHA256 다이제스트로 식별:
  sha256:abc123... -> Image Index JSON
  sha256:def456... -> Image Manifest JSON
  sha256:789ghi... -> Image Config JSON
  sha256:jkl012... -> Layer tar.gz

장점:
  - 중복 제거: 동일 레이어는 한 번만 저장
  - 무결성 검증: 다이제스트로 데이터 검증
  - 캐싱: 다이제스트 기반 캐시 룩업

2. Content Store

2.1 개요

Content Store는 containerd의 콘텐츠 주소 저장소로, 이미지의 모든 바이너리 데이터를 관리합니다.

Content Store 디렉토리 구조:

/var/lib/containerd/io.containerd.content.v1.content/
  blobs/
    sha256/
      abc123...  (Image Index)
      def456...  (Image Manifest)
      789ghi...  (Image Config)
      jkl012...  (Layer 1 tar.gz)
      mno345...  (Layer 2 tar.gz)
  ingest/
    (임시 다운로드 데이터)

2.2 Content Store API

Content Store 주요 연산:

Info(digest)     -> 콘텐츠 메타데이터 조회 (크기, 생성시간)
ReaderAt(digest) -> 콘텐츠 읽기 (io.ReaderAt 인터페이스)
Writer(ref)      -> 콘텐츠 쓰기 (원자적 커밋)
Delete(digest)   -> 콘텐츠 삭제
ListStatuses()   -> 진행 중인 쓰기 작업 조회
Abort(ref)       -> 진행 중인 쓰기 작업 취소

2.3 Ingest 프로세스

콘텐츠 쓰기 (Ingest) 프로세스:

1. Writer 생성 (참조 키 할당)
        |
        v
2. ingest/ 디렉토리에 임시 파일 생성
        |
        v
3. 데이터 스트리밍 쓰기
   (레지스트리에서 레이어 다운로드 등)
        |
        v
4. 다이제스트 검증
   (기대 다이제스트와 실제 데이터 해시 비교)
        |
        v
5. 원자적 커밋
   (ingest/ -> blobs/sha256/ 으로 이동)
        |
        v
6. 실패 시 ingest/ 에서 정리

3. Snapshotter

3.1 Snapshotter 개요

Snapshotter는 이미지 레이어를 파일시스템 스냅샷으로 관리하는 플러그인입니다. 컨테이너가 사용할 루트 파일시스템을 준비합니다.

Snapshotter 역할:

이미지 레이어 (tar.gz)
    |
    v
Snapshotter가 각 레이어를 스냅샷으로 변환
    |
    v
스냅샷을 쌓아서 통합 파일시스템 구성
    |
    v
컨테이너에 마운트 포인트 제공

3.2 스냅샷 타입

스냅샷 종류:

1. Committed (커밋됨)
   - 읽기 전용 스냅샷
   - 이미지 레이어에 대응
   - 여러 컨테이너가 공유 가능

2. Active (활성)
   - 읽기/쓰기 스냅샷
   - 컨테이너의 쓰기 가능 레이어
   - 하나의 컨테이너에 할당

3.3 overlayfs Snapshotter

가장 널리 사용되는 Snapshotter입니다:

overlayfs 동작:

레이어 1 (base): /snapshots/1/fs  (lowerdir)
레이어 2 (app):  /snapshots/2/fs  (lowerdir)
쓰기 레이어:     /snapshots/3/fs  (upperdir)
작업 디렉토리:   /snapshots/3/work (workdir)

마운트:
  mount -t overlay overlay \
    -o lowerdir=/snapshots/2/fs:/snapshots/1/fs,\
       upperdir=/snapshots/3/fs,\
       workdir=/snapshots/3/work \
    /container/rootfs

장점:
  - Copy-on-Write: 변경 시에만 복사
  - 빠른 컨테이너 시작
  - 레이어 공유로 디스크 절약

3.4 native Snapshotter

native Snapshotter:

- 각 스냅샷을 독립 디렉토리에 저장
- 부모 스냅샷을 완전히 복사 (hardlink 사용)
- overlayfs를 지원하지 않는 환경에서 사용
- 디스크 사용량이 큼
- 간단하고 이식성이 높음

3.5 devmapper Snapshotter

devmapper Snapshotter:

- Linux device mapper의 thin provisioning 사용
- 블록 레벨 Copy-on-Write
- 고성능 워크로드에 적합
- Firecracker microVM과 함께 사용
- 설정이 복잡함 (thin-pool 사전 구성 필요)

사용 사례:
  - AWS Fargate (Firecracker)
  - 고성능 컨테이너 환경
  - 블록 스토리지 기반 인프라

3.6 Snapshotter API

Snapshotter 주요 연산:

Stat(key)              -> 스냅샷 정보 조회
Prepare(key, parent)   -> Active 스냅샷 생성 (쓰기 가능)
View(key, parent)      -> Committed 스냅샷의 읽기 전용 뷰
Commit(name, key)      -> Active 스냅샷을 Committed로 변환
Mounts(key)            -> 스냅샷의 마운트 정보 반환
Remove(key)            -> 스냅샷 삭제

4. 이미지 Pull 플로우

4.1 전체 플로우

이미지 Pull 전체 과정:

1. 이미지 참조 해석
   docker.io/library/nginx:latest
        |
        v
2. Image Index/Manifest 다운로드
   - 레지스트리에서 매니페스트 가져오기
   - 플랫폼에 맞는 매니페스트 선택
        |
        v
3. Config 다운로드
   - 이미지 설정 JSON 다운로드
   - Content Store에 저장
        |
        v
4. 레이어 다운로드 (병렬)
   - 각 레이어를 Content Store에 저장
   - 이미 존재하는 레이어는 건너뜀
        |
        v
5. 레이어 언패킹
   - Content Store에서 레이어 읽기
   - Snapshotter로 스냅샷 생성
        |
        v
6. 이미지 메타데이터 등록
   - BoltDB에 이미지 레코드 생성
   - 태그와 다이제스트 매핑

4.2 레이어 다운로드 상세

레이어 다운로드:

1. 매니페스트에서 레이어 다이제스트 목록 추출
2. Content Store에 이미 존재하는지 확인
3. 없는 레이어만 레지스트리에서 다운로드
4. Transfer Service가 다운로드 관리:
   - 동시 다운로드 제한 (기본 3개)
   - 진행 상황 추적
   - 재시도 로직
5. 각 레이어는 gzip 압축 상태로 Content Store에 저장

4.3 레이어 언패킹

레이어 언패킹 (Unpack):

1. Content Store에서 레이어 blob 읽기
2. gzip 해제
3. tar 아카이브 추출
4. Snapshotter에 스냅샷 생성:
   a. 첫 번째 레이어: 부모 없이 Prepare
   b. 레이어 내용을 스냅샷에 적용 (Apply)
   c. Commit으로 읽기 전용으로 변환
   d. 다음 레이어: 이전 스냅샷을 부모로 Prepare
5. 최종 스냅샷 체인 완성

스냅샷 체인:
  Layer 1 (committed) <- Layer 2 (committed) <- Layer 3 (committed)

5. 이미지 메타데이터

5.1 이미지 레코드

이미지 메타데이터 (BoltDB):

이미지 레코드:
  - Name: "docker.io/library/nginx:latest"
  - Target:
      MediaType: "application/vnd.oci.image.index.v1+json"
      Digest: "sha256:abc123..."
      Size: 1234
  - Labels:
      "containerd.io/gc.ref.content.0": "sha256:def456..."
      "containerd.io/gc.ref.content.1": "sha256:789ghi..."
  - CreatedAt: 2026-03-20T00:00:00Z
  - UpdatedAt: 2026-03-20T00:00:00Z

5.2 이미지 조회

# ctr로 이미지 목록 조회
ctr -n k8s.io images list

# 이미지 상세 정보
ctr -n k8s.io images check

# 이미지 콘텐츠 확인
ctr -n k8s.io content get sha256:abc123... | jq .

6. 가비지 컬렉션

6.1 GC 메커니즘

가비지 컬렉션 동작:

1. 루트 객체 식별:
   - 이미지 레코드
   - 컨테이너 레코드
   - Lease 레코드

2. 참조 추적 (Mark):
   - 이미지 -> Manifest -> Config + Layers
   - 컨테이너 -> 스냅샷 체인
   - Lease -> 보호 중인 리소스

3. 미참조 객체 삭제 (Sweep):
   - Content Store의 미참조 blob 삭제
   - Snapshotter의 미참조 스냅샷 삭제
   - 메타데이터의 고아 레코드 정리

6.2 GC 레이블

GC 참조 레이블:

containerd는 GC 참조를 레이블로 관리합니다:

이미지 레이블:
  "containerd.io/gc.ref.content.0": "sha256:..."  (매니페스트 참조)
  "containerd.io/gc.ref.content.1": "sha256:..."  (레이어 참조)

콘텐츠 레이블:
  "containerd.io/gc.ref.content.config": "sha256:..." (config 참조)
  "containerd.io/gc.ref.content.l.0": "sha256:..."    (레이어 참조)

스냅샷 레이블:
  "containerd.io/gc.ref.snapshot.overlayfs": "sha256:..." (스냅샷 참조)

6.3 Lease

Lease (임대):

- 진행 중인 작업의 리소스를 GC로부터 보호
- 이미지 Pull 중 다운로드된 레이어 보호
- 컨테이너 생성 중 스냅샷 보호
- TTL 기반 자동 만료
- 작업 완료 후 명시적 삭제 가능

예시:
  이미지 Pull 시작 -> Lease 생성
  레이어 다운로드 -> Lease가 콘텐츠 보호
  이미지 등록 완료 -> Lease 삭제 (이미지 레코드가 참조)

6.4 GC 스케줄링

GC 트리거:

1. 주기적 실행:
   - containerd 설정의 gc_schedule (기본값 없음)
   - 설정 시 cron 표현식으로 주기 지정

2. 이벤트 기반:
   - 이미지 삭제 시
   - 컨테이너 삭제 시
   - API를 통한 명시적 호출

3. ctr을 통한 수동 실행:
   ctr -n k8s.io content prune

7. 정리

containerd의 이미지 관리는 Content Store의 콘텐츠 주소 저장, Snapshotter의 레이어 관리, 그리고 GC의 리소스 정리라는 세 축으로 구성됩니다. overlayfs Snapshotter의 Copy-on-Write 메커니즘은 빠른 컨테이너 시작과 효율적인 디스크 사용을 가능하게 하며, Lease 기반 GC 보호는 이미지 작업의 안전성을 보장합니다. 다음 글에서는 containerd의 컨테이너 생명주기 관리를 분석합니다.