본문으로 건너뛰기
김신건의 로그

[Container] OCI Image: spec, manifest, layer 표준

· 수정 · 📖 약 2분 · 628자/단어 #oci #container #docker #image #cloud
OCI, Open Container Initiative, OCI image spec, image manifest, image index, OCI distribution, containerd, Cosign, SBOM

정의

OCI (Open Container Initiative) = 컨테이너 표준 (Linux Foundation, 2015). image format + runtime + distribution 3가지 spec.

사용 상황

상황핵심 개념
멀티 아키 이미지 빌드 (AMD64 + ARM64)Image Index, BuildKit + QEMU
이미지 공급망 보안Cosign 서명, SBOM
이미지 경량화Dockerfile 레이어 최적화
취약점 스캔 자동화Trivy, Grype
Registry 비용 최적화Lifecycle Policy
Docker 외 런타임 사용 (K8s, K3s)containerd, CRI-O

3개 OCI Spec

Spec의미
Imagetarball + JSON metadata 구조
Runtimerunc 같은 컨테이너 실행 표준
Distributiondocker push/pull 같은 레지스트리 API

Image 구조

manifest.json
  - config.json (image config)
  - layers
    - layer0.tar.gz
    - layer1.tar.gz
    - layer2.tar.gz
flowchart TB
    Idx["Image Index (멀티 아키)"]
    Idx --> Mfst_amd64["Manifest amd64"]
    Idx --> Mfst_arm64["Manifest arm64"]
    Mfst_amd64 --> Cfg_a["Config JSON"]
    Mfst_amd64 --> L1_a["Layer tarball 1"]
    Mfst_amd64 --> L2_a["Layer tarball 2"]

Image Index (멀티 아키)

{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:abc...",
      "platform": { "os": "linux", "architecture": "amd64" }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:def...",
      "platform": { "os": "linux", "architecture": "arm64" }
    }
  ]
}

한 tag (nginx:1.27) 으로 여러 아키 자동 선택. pull 시 클라이언트 아키 매칭.

Manifest

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:abc..."
  },
  "layers": [
    { "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:111..." },
    { "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:222..." }
  ]
}

Image vs OCI Artifact

flowchart LR
    OCI["OCI Distribution"] --> Image["OCI Image (전통 컨테이너)"]
    OCI --> Artifact["OCI Artifact (임의 데이터)"]
    Artifact --> Helm[Helm chart]
    Artifact --> WASM[WebAssembly]
    Artifact --> SBOM[SBOM]
    Artifact --> Sig["Cosign signature"]

OCI registry 가 컨테이너 image 만이 아닌 일반 artifact store 로 진화. Helm chart, WASM, SBOM, signature 모두.

구현체

의미
runcOCI Runtime 의 표준 구현
containerdcontainer 라이프사이클 (Docker / K8s 가 사용)
CRI-OK8s 전용 OCI runtime
podmanDocker 호환, daemonless
buildahimage build 전용
skopeoimage 복사 / 검사

Content-Addressed Storage

image digest = sha256(manifest JSON)
layer digest = sha256(layer tarball)

tag변할 수 있지만 digest불변. 프로덕션 배포 는 항상 digest 로:

nginx:1.27                            # tag (mutable)
nginx@sha256:abc123...                # digest (immutable)

Dockerfile 레이어 최적화

레이어는 캐시됨. 변경 빈도 낮은 것을 위에.

# 잘못된 순서 (매번 deps 재설치)
FROM node:20-alpine
COPY . .
RUN npm install

# 올바른 순서 (package.json 변경 없으면 캐시 재사용)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

# 멀티스테이지: 빌드 산출물만 최종 이미지에 포함
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

레이어 크기 확인:

docker history myapp:latest
dive myapp:latest   # dive 툴로 레이어별 상세 분석

BuildKit + QEMU (멀티 아키 빌드)

# buildx + QEMU 설치
docker buildx create --name multi-arch --driver docker-container --use
docker run --privileged --rm tonistiigi/binfmt --install all

# AMD64 + ARM64 동시 빌드 + push
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myregistry/myapp:1.0.0 \
  --push \
  .

CI/CD (GitHub Actions) 에서:

- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
  with:
    platforms: linux/amd64,linux/arm64
    push: true
    tags: myregistry/myapp:${{ github.sha }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

Image 취약점 스캔 (Trivy)

# 이미지 스캔
trivy image --severity HIGH,CRITICAL nginx:1.27

# CI에서 CRITICAL 발견 시 실패
trivy image \
  --exit-code 1 \
  --severity CRITICAL \
  --ignore-unfixed \
  myapp:latest

# SBOM 출력
trivy image --format cyclonedx --output sbom.json myapp:latest
flowchart LR
    Build["docker build"] --> Scan["trivy scan"]
    Scan --> Pass{결과}
    Pass -->|"CRITICAL 없음"| Push["registry push"]
    Pass -->|"CRITICAL 발견"| Fail["빌드 실패"]

Cosign 이미지 서명

공급망 보안. 이미지 무결성, 출처 검증.

# keyless 서명 (OIDC 기반, Sigstore)
cosign sign \
  --yes \
  myregistry/myapp@sha256:abc123...

# 검증
cosign verify \
  --certificate-identity-regexp "https://github.com/myorg/myrepo/.*" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  myregistry/myapp:latest

GitHub Actions에서 자동 서명:

- uses: sigstore/cosign-installer@v3
- name: Sign image
  env:
    COSIGN_EXPERIMENTAL: 1
  run: |
    cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE }}@${{ steps.build.outputs.digest }}

keyless 서명은 private key 관리 불필요. OIDC token으로 Fulcio CA가 단기 인증서 발급.

SBOM (Software Bill of Materials)

이미지에 포함된 소프트웨어 목록. 취약점 추적, 규정 준수.

# syft로 SBOM 생성
syft myapp:latest -o cyclonedx-json > sbom.json
syft myapp:latest -o spdx-json > sbom.spdx.json

# SBOM을 OCI Artifact로 registry에 첨부
cosign attach sbom \
  --sbom sbom.json \
  myregistry/myapp@sha256:abc123...

# Grype로 SBOM 기반 취약점 스캔
grype sbom:sbom.json

Registry Lifecycle Policy (ECR 예시)

오래된 이미지 자동 삭제로 스토리지 비용 절감.

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "30일 이상 된 untagged 이미지 삭제",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 30
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "최근 10개 tagged 이미지 유지",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["v"],
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": { "type": "expire" }
    }
  ]
}

흔한 함정

WARNING

  1. Tag mutability = 어제 build 한 image 가 오늘 다른 내용. digest pinning.
  2. Image scan 누락 = 취약점 image. Trivy / Grype / Clair.
  3. Cross-arch test 안 함 = AMD64 만 build → ARM 노드 실패.
  4. Layer 너무 많음 = layer 한도 (보통 127). 가능하면 묶기.
  5. root로 실행 = Dockerfile 에 USER nonroot 없으면 컨테이너가 root 권한으로 실행. 보안 위험.
  6. base image 업데이트 안 함 = OS 취약점 방치. 정기 FROM ubuntu:24.04 재빌드.
  7. 빌드 시 secrets 레이어에 남음 = RUN --mount=type=secret 사용. ENV로 비밀 전달 금지.
  8. SBOM/서명 없는 배포 = K8s Policy (Kyverno) 로 서명 없는 이미지 배포 차단 권장.

관련 위키

이 글의 용어 (7개)
[AWS] ECR (Elastic Container Registry)cloud
정의 Amazon Elastic Container Registry (ECR) 는 AWS 가 관리하는 OCI 컨테이너 이미지 및 Helm chart 레지스트리 입니다. Docker…
[Container] cgroups + namespaces: 컨테이너의 두 기둥virtualization
정의 컨테이너 = namespaces + cgroups + 파일시스템 (overlay). VM 이 아니라 host 의 프로세스 + 격리. VM 은 hypervisor + 별도 커…
[Container] Docker: image, layer, registryvirtualization
정의 Docker = 컨테이너 빌드 + 실행 + 배포 의 표준 도구. 2013 출시 → 컨테이너 시대 의 시작. 현재는 OCI 표준 으로 진화 ( , , 등 호환). 컨테이너 런…
[Container] Image Best Practices: 작게, 안전하게virtualization
정의 컨테이너 image best practices = 작고 (small), 안전하고 (secure), 재현 가능하고 (reproducible), 서명된 (signed) imag…
[K8s] Pod: 컨테이너의 최소 단위, sidecar, lifecyclekubernetes
정의 Pod = K8s 의 가장 작은 배포 단위. 1개 이상의 컨테이너 + 공유 네트워크 + 공유 스토리지. [!IMPORTANT] Pod 는 컨테이너의 wrapping 이 아니…
[Kubernetes] Architecture (Control Plane + Node)kubernetes
정의 Kubernetes Architecture 는 Control Plane (제어) 과 Worker Node (실행) 두 계층으로 구성됩니다. Control Plane 은 원하…
VM vs Container: 층 구조, 성능, 격리 완벽 비교virtualization
정의 VM 과 Container 는 같은 문제 (하나의 물리 서버에 여러 워크로드 격리) 를 다른 방식으로 푸는 두 접근 입니다. VM 은 하드웨어를 가상화하고, Containe…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기