AWS ECR - 컨테이너 이미지 레지스트리 완벽 가이드
TL;DR
- ECR은 레지스트리·리포지토리·이미지 3단 구조로 관리된다.
- IAM, 라이프사이클 정책, 이미지 스캔, 복제 기능을 제공한다.
- Docker Hub 대비 보안·비용·지연 문제를 개선한다.
1. 개념
ECR은 AWS가 제공하는 관리형 컨테이너 이미지 레지스트리 서비스다.
2. 배경
Docker Hub의 제한과 기업 보안 요구로 전용 레지스트리가 필요해졌다.
3. 이유
권한 통제, 감사, 비용 효율을 확보하기 위해 ECR을 사용한다.
4. 특징
IAM 통합, lifecycle 정책, 스캔, pull-through cache, 리전 복제가 핵심이다.
5. 상세 내용
AWS ECR - 컨테이너 이미지 레지스트리 완벽 가이드
작성일: 2026-02-27 카테고리: Cloud / AWS / Container 포함 내용: ECR, Elastic Container Registry, Repository, Registry, Docker, OCI, Image, Tag, Digest, Manifest, Layer, Lifecycle Policy, Image Scanning, Pull Through Cache, Replication
1. 컨테이너 이미지 레지스트리란?
1.1 레지스트리 = “컨테이너 세계의 앱 스토어”
┌─────────────────────────────────────────────────────────────────┐
│ 컨테이너 이미지 레지스트리 개념 (비유) │
│ │
│ 비유: 앱 스토어와 컨테이너 레지스트리 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 앱 스토어 (App Store / Google Play) │ │
│ │ ├── 앱 개발자가 앱을 "업로드" (push) │ │
│ │ ├── 사용자가 앱을 "다운로드" (pull) │ │
│ │ ├── 버전별로 관리 (v1.0, v2.0, v2.1) │ │
│ │ └── 카테고리별 정리 (게임, 유틸리티, SNS) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↕ 똑같은 구조 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 컨테이너 레지스트리 (ECR / Docker Hub) │ │
│ │ ├── 개발자가 이미지를 "push" │ │
│ │ ├── 서버가 이미지를 "pull" │ │
│ │ ├── 태그로 버전 관리 (latest, v1.0, v2.0) │ │
│ │ └── 레포지토리별 정리 (frontend, backend, worker) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 핵심: 레지스트리 = 컨테이너 이미지의 중앙 저장소 + 배포 허브 │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 Docker Hub의 역할과 한계
┌─────────────────────────────────────────────────────────────────┐
│ Docker Hub - 최초의 퍼블릭 레지스트리 │
│ │
│ Docker Hub는 컨테이너 이미지의 "GitHub" 같은 존재 │
│ │
│ 장점: │
│ ├── 무료로 퍼블릭 이미지 제공 (nginx, ubuntu, node 등) │
│ ├── 커뮤니티 이미지 풍부 │
│ └── docker pull nginx 한 줄이면 바로 사용 │
│ │
│ 기업 환경에서의 한계: │
│ ├── 🔒 IAM 통합 불가: AWS 권한 체계와 분리 │
│ ├── 🚫 Rate Limiting: 익명 100pulls/6h, 인증 200pulls/6h │
│ ├── 🐌 지연 시간: AWS 리전에서 외부로 나갔다 돌아옴 │
│ ├── 💰 전송 비용: AWS → 인터넷 → Docker Hub → 인터넷 → AWS │
│ └── 📋 감사 로그 없음: 누가 언제 어떤 이미지를 pull 했는지 │
│ │
│ → 이 문제들을 해결하기 위해 AWS ECR이 탄생! │
│ │
└─────────────────────────────────────────────────────────────────┘
2. ECR의 핵심 개념 - Registry, Repository, Image
2.1 3단 계층 구조: Registry > Repository > Image
┌─────────────────────────────────────────────────────────────────┐
│ ECR 3단 계층 구조 │
│ │
│ Registry (레지스트리) │
│ ├── AWS 계정당 리전별 1개 자동 생성 │
│ ├── URI: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com │
│ │ ───────────── ────────────── │
│ │ AWS 계정 ID 리전 이름 │
│ │ │
│ ├── Repository: my-backend-api │
│ │ ├── Image: v1.0.0 (tag) │
│ │ ├── Image: v1.1.0 (tag) │
│ │ ├── Image: v2.0.0 (tag) │
│ │ └── Image: latest (tag) │
│ │ │
│ ├── Repository: my-frontend-app │
│ │ ├── Image: v1.0.0 │
│ │ └── Image: latest │
│ │ │
│ └── Repository: worker-service │
│ ├── Image: v3.2.1 │
│ └── Image: latest │
│ │
│ 비유: │
│ ├── Registry = 도서관 건물 (계정+리전 단위) │
│ ├── Repository = 도서관의 책꽂이 (앱 하나 단위) │
│ └── Image = 책꽂이의 책 (앱의 특정 버전) │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 Registry - AWS 계정의 이미지 금고
┌─────────────────────────────────────────────────────────────────┐
│ Registry 상세 │
│ │
│ 하나의 Registry URI 구조: │
│ │
│ 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com │
│ ──────┬───── ─┬─ ─┬─ ───────┬────── ─────┬───── │
│ AWS계정ID ECR 리전 리전이름 도메인 │
│ │
│ 특징: │
│ ├── 계정+리전 조합마다 자동으로 1개 존재 │
│ ├── 별도 생성/삭제 불가 (계정에 종속) │
│ ├── Private Registry: 기본, IAM으로 접근 제어 │
│ └── Public Registry: public.ecr.aws (ECR Public Gallery) │
│ │
│ ECR Public vs Private: │
│ ┌────────────────┬──────────────────┬──────────────────┐ │
│ │ 구분 │ Private ECR │ Public ECR │ │
│ ├────────────────┼──────────────────┼──────────────────┤ │
│ │ URI │ ACCT.dkr.ecr... │ public.ecr.aws/ │ │
│ │ 인증 │ IAM 필수 │ 인증 없이 pull │ │
│ │ 리전 │ 모든 리전 │ us-east-1 only │ │
│ │ 용도 │ 사내 앱 이미지 │ OSS/공개 이미지 │ │
│ │ 비용 │ 저장+전송 │ 50GB 무료 │ │
│ └────────────────┴──────────────────┴──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2.3 Repository - 하나의 앱 이미지 저장소
┌─────────────────────────────────────────────────────────────────┐
│ Repository 상세 │
│ │
│ Repository = 하나의 애플리케이션의 모든 이미지 버전 저장 단위 │
│ │
│ 생성 예시: │
│ aws ecr create-repository --repository-name my-backend-api │
│ │
│ Namespace로 계층적 이름 지정 (폴더처럼 /로 구분): │
│ ├── frontend/react-app │
│ ├── frontend/admin-dashboard │
│ ├── backend/user-service │
│ ├── backend/order-service │
│ └── infra/nginx-proxy │
│ │
│ Repository 설정 옵션: │
│ ├── Image Tag Mutability: MUTABLE / IMMUTABLE │
│ ├── Image Scanning: on push / continuous │
│ ├── Encryption: AES-256 (기본) / KMS (커스텀) │
│ └── Lifecycle Policy: 오래된/불필요한 이미지 자동 삭제 │
│ │
│ 주의: Repository는 미리 생성해야 push 가능! │
│ (Docker Hub와 다름 - Docker Hub는 push하면 자동 생성) │
│ │
└─────────────────────────────────────────────────────────────────┘
3. 이미지 구조 - Layer, Manifest, Digest, Tag
3.1 Layer - Dockerfile 한 줄 = 하나의 레이어
┌─────────────────────────────────────────────────────────────────┐
│ 컨테이너 이미지 = 레이어의 스택 │
│ │
│ Dockerfile: 결과 이미지: │
│ │
│ FROM node:18-alpine ──→ ┌──────────────────────────┐ │
│ │ Layer 1: node:18-alpine │ │
│ │ (약 170MB, 공유 가능) │ │
│ WORKDIR /app ──→ ├──────────────────────────┤ │
│ │ Layer 2: WORKDIR 설정 │ │
│ │ (거의 0 bytes) │ │
│ COPY package*.json . ──→ ├──────────────────────────┤ │
│ │ Layer 3: package.json │ │
│ │ (수 KB) │ │
│ RUN npm ci ──→ ├──────────────────────────┤ │
│ │ Layer 4: node_modules │ │
│ │ (수십~수백 MB) │ │
│ COPY . . ──→ ├──────────────────────────┤ │
│ │ Layer 5: 소스 코드 │ │
│ │ (수 MB) │ │
│ CMD ["node", "app.js"] ──→ ├──────────────────────────┤ │
│ │ Layer 6: CMD 설정 │ │
│ │ (메타데이터만) │ │
│ └──────────────────────────┘ │
│ │
│ 핵심 원리: SHA256 해시로 중복 제거 (Content Addressable) │
│ ├── 같은 FROM node:18-alpine → 동일 레이어 재사용 │
│ ├── package.json 안 바뀌면 → Layer 3, 4 캐시 사용 │
│ └── 소스 코드만 변경 시 → Layer 5만 새로 push │
│ │
│ ECR에서의 레이어 공유: │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Repository A (v1.0) Repository A (v1.1) │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Layer 1 │ ←───→ │ Layer 1 │ (공유!) │ │
│ │ │ Layer 2 │ ←───→ │ Layer 2 │ (공유!) │ │
│ │ │ Layer 3 │ ←───→ │ Layer 3 │ (공유!) │ │
│ │ │ Layer 4 (A) │ │ Layer 4 (B) │ (다름) │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ → 저장 비용: Layer 1~3은 한 번만 저장/과금! │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 Manifest - 이미지의 설명서 (JSON)
┌─────────────────────────────────────────────────────────────────┐
│ Manifest = 이미지 설명서 │
│ │
│ Manifest는 "이 이미지가 어떤 레이어로 구성되어 있는지" 설명 │
│ │
│ Image Manifest (단일 아키텍처): │
│ { │
│ "schemaVersion": 2, │
│ "mediaType": "application/vnd.oci.image.manifest.v1+json", │
│ "config": { │
│ "digest": "sha256:abc123...", ← 이미지 설정 │
│ "size": 7023 │
│ }, │
│ "layers": [ │
│ { "digest": "sha256:def456...", "size": 32654481 }, │
│ { "digest": "sha256:ghi789...", "size": 16724970 }, │
│ { "digest": "sha256:jkl012...", "size": 73109 } │
│ ] ↑ 각 레이어의 SHA256 해시와 크기 │
│ } │
│ │
│ Manifest List (멀티 아키텍처 = Fat Manifest): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Manifest List (sha256:aaa...) │ │
│ │ ├── linux/amd64 → Manifest (sha256:bbb...) │ │
│ │ │ └── Layers: [sha256:111, sha256:222, ...] │ │
│ │ ├── linux/arm64 → Manifest (sha256:ccc...) │ │
│ │ │ └── Layers: [sha256:333, sha256:444, ...] │ │
│ │ └── linux/arm/v7 → Manifest (sha256:ddd...) │ │
│ │ └── Layers: [sha256:555, sha256:666, ...] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ → docker pull my-image:v1.0 하면 │
│ 현재 머신의 아키텍처에 맞는 Manifest를 자동 선택! │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 Digest vs Tag - 불변 vs 가변 식별자
┌─────────────────────────────────────────────────────────────────┐
│ Digest vs Tag 비교 │
│ │
│ Digest (다이제스트): │
│ ├── 형식: sha256:64자리 16진수 │
│ ├── 예: sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d0│
│ ├── 이미지 내용의 해시값 → 내용이 바뀌면 Digest도 바뀜 │
│ ├── 불변(Immutable): 한번 정해지면 절대 변하지 않음 │
│ └── 용도: 프로덕션에서 정확한 이미지 고정 │
│ │
│ Tag (태그): │
│ ├── 형식: 사람이 읽을 수 있는 문자열 │
│ ├── 예: latest, v1.0.0, 2026-02-27, stable │
│ ├── 가변(Mutable): 같은 태그가 다른 이미지를 가리킬 수 있음 │
│ ├── 위험: latest를 오늘 pull → 내일 pull → 다른 이미지! │
│ └── 해결: Immutable Tags 설정 → 태그 덮어쓰기 방지 │
│ │
│ 시각적 비교: │
│ │
│ 시점 1 (월요일): │
│ ┌────────────┐ │
│ │ Image A │ ← tag: "latest" │
│ │ sha256:aaa │ ← digest (절대 안 변함) │
│ └────────────┘ │
│ │
│ 시점 2 (화요일, 새 이미지 push with latest tag): │
│ ┌────────────┐ │
│ │ Image A │ (tag 없음 = "untagged" 됨) │
│ │ sha256:aaa │ ← digest는 그대로 │
│ └────────────┘ │
│ ┌────────────┐ │
│ │ Image B │ ← tag: "latest" (여기로 이동!) │
│ │ sha256:bbb │ ← 새로운 digest │
│ └────────────┘ │
│ │
│ → Tag는 "포스트잇", Digest는 "주민등록번호" │
│ → 프로덕션에서는 반드시 Digest 또는 Immutable Tag 사용! │
│ │
└─────────────────────────────────────────────────────────────────┘
4. Image URI 형식 - 이미지를 정확히 지정하는 주소
┌─────────────────────────────────────────────────────────────────┐
│ ECR Image URI 형식 │
│ │
│ 1) Tag로 참조: │
│ 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1.0 │
│ ─────────────────────────┬─────────────────────── ──┬── ─┬─ │
│ Registry URI Repo명 Tag │
│ │
│ 2) Digest로 참조: │
│ 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app@sha256│
│ :a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c8... │
│ ─────────────────────────┬─────────────────────── ──┬── │
│ Registry URI Repo명 │
│ @sha256:64자리해시 │
│ │
│ 3) ECR Public 형식: │
│ public.ecr.aws/amazonlinux/amazonlinux:2023 │
│ ─────┬────── ─────┬────── ─────┬──── ─┬─ │
│ Public도메인 Alias/계정 이미지이름 Tag │
│ │
│ URI 구성요소 정리: │
│ ┌──────────────────┬────────────────────────────────────┐ │
│ │ 구성요소 │ 설명 │ │
│ ├──────────────────┼────────────────────────────────────┤ │
│ │ Registry URI │ 계정ID.dkr.ecr.리전.amazonaws.com │ │
│ │ Repository Name │ namespace/이름 (예: backend/api) │ │
│ │ Tag │ :tag (예: :v1.0.0, :latest) │ │
│ │ Digest │ @sha256:64자리해시 │ │
│ └──────────────────┴────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
5. ECR 탄생 배경과 역사
┌─────────────────────────────────────────────────────────────────┐
│ ECR 탄생 배경과 역사 │
│ │
│ 2013 ── Docker 오픈소스 공개 │
│ 컨테이너 기술 폭발적 성장 시작 │
│ │ │
│ 2014 ── Docker Hub 퍼블릭 레지스트리 성장 │
│ AWS에서 ECS (Elastic Container Service) 발표 │
│ │ │
│ 2015.12.21 ── AWS ECR (Elastic Container Registry) GA 출시 │
│ ECS와 함께 사용할 프라이빗 레지스트리 필요성 │
│ │ │
│ 2017 ── EKS (Elastic Kubernetes Service) 발표 │
│ ECR이 EKS의 기본 이미지 레지스트리로 활용 │
│ │ │
│ 2020.12 ── ECR Public 출시 │
│ public.ecr.aws 통해 퍼블릭 이미지 호스팅 가능 │
│ │ │
│ 2021 ── Pull Through Cache 기능 추가 │
│ Docker Hub Rate Limiting 문제 해결 │
│ │ │
│ 2022 ── Enhanced Scanning (Amazon Inspector 통합) │
│ SOCI (Seekable OCI) Index 지원 → 빠른 컨테이너 시작 │
│ │ │
│ 2023 ── Lifecycle Policy 고도화 │
│ Cross-Region / Cross-Account Replication 개선 │
│ │ │
│ 2024 ── OCI Artifact 지원 확대 (Helm Chart, WASM 등) │
│ Repository Creation Template 추가 │
│ │
│ 탄생 핵심 이유 (Docker Hub의 한계): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 문제 │ ECR의 해결 │ │
│ ├─────────────────────────┼────────────────────────────┤ │
│ │ IAM 통합 불가 │ AWS IAM 네이티브 통합 │ │
│ │ Rate Limiting │ 무제한 pull (같은 리전) │ │
│ │ 외부 네트워크 지연 │ 같은 리전 = 최소 지연 │ │
│ │ 데이터 전송 비용 │ 같은 리전 전송 무료 │ │
│ │ 감사 로그 없음 │ CloudTrail 완전 통합 │ │
│ │ 취약점 스캔 유료 │ Basic Scanning 무료 │ │
│ └─────────────────────────┴────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
6. 인증과 보안
6.1 Authorization Token - ECR 로그인 방식
┌─────────────────────────────────────────────────────────────────┐
│ ECR 인증 흐름 │
│ │
│ ECR은 Docker CLI의 표준 인증을 사용하되, │
│ AWS IAM 자격 증명을 Docker 토큰으로 변환하는 방식 │
│ │
│ ┌──────────┐ ①IAM 자격증명 ┌─────────┐ │
│ │ 개발자 │ ──────────────────→ │ AWS │ │
│ │ (CLI) │ │ STS │ │
│ │ │ ←────────────────── │ │ │
│ └──────────┘ ②Authorization └─────────┘ │
│ │ Token (12시간) │
│ │ │
│ │ ③ docker login │
│ │ Username: "AWS" │
│ │ Password: <Authorization Token> │
│ ▼ │
│ ┌──────────┐ │
│ │ ECR │ ← 토큰 검증 후 push/pull 허용 │
│ │ Registry │ │
│ └──────────┘ │
│ │
│ 로그인 명령어: │
│ aws ecr get-login-password --region ap-northeast-2 \ │
│ | docker login --username AWS --password-stdin \ │
│ 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com │
│ │
│ 핵심 포인트: │
│ ├── Username은 항상 "AWS" (고정값) │
│ ├── Password는 get-login-password로 받은 토큰 │
│ ├── 토큰 유효기간: 12시간 (만료되면 재발급 필요) │
│ └── CI/CD에서는 매 파이프라인마다 재로그인 권장 │
│ │
└─────────────────────────────────────────────────────────────────┘
6.2 Repository Policy - 리포지토리 수준 접근 제어
┌─────────────────────────────────────────────────────────────────┐
│ Repository Policy │
│ │
│ IAM Policy (주체 기반) vs Repository Policy (리소스 기반) │
│ ├── "이 사용자가 무엇을 │ "이 레포지토리에 누가 │
│ │ 할 수 있는가?" │ 접근할 수 있는가?" │
│ └── IAM 사용자/역할에 부착 │ ECR Repository에 부착 │
│ │
│ 대표적 사용 사례: │
│ ├── Cross-Account Pull: 다른 계정에서 이미지 pull 허용 │
│ ├── 특정 IAM Role만 push 가능하도록 제한 │
│ └── Lambda Execution Role에 pull 권한 부여 │
│ │
│ Cross-Account 예시 (계정 B에서 pull 허용): │
│ { │
│ "Version": "2012-10-17", │
│ "Statement": [{ │
│ "Sid": "AllowCrossAccountPull", │
│ "Effect": "Allow", │
│ "Principal": { │
│ "AWS": "arn:aws:iam::ACCOUNT_B_ID:root" │
│ }, │
│ "Action": [ │
│ "ecr:GetDownloadUrlForLayer", │
│ "ecr:BatchGetImage", │
│ "ecr:BatchCheckLayerAvailability" │
│ ] │
│ }] │
│ } │
│ │
└─────────────────────────────────────────────────────────────────┘
6.3 Immutable Tags와 암호화
┌─────────────────────────────────────────────────────────────────┐
│ Immutable Tags + 암호화 │
│ │
│ Immutable Tags (불변 태그): │
│ ├── 설정하면 한번 push된 태그는 덮어쓰기 불가 │
│ ├── v1.0.0으로 push → 다시 v1.0.0으로 push 시 ERROR │
│ ├── "이 태그 = 항상 이 이미지" 보장 │
│ └── 프로덕션 환경에서 강력 권장 │
│ │
│ 설정: │
│ aws ecr put-image-tag-mutability \ │
│ --repository-name my-app \ │
│ --image-tag-mutability IMMUTABLE │
│ │
│ 암호화: │
│ ┌────────────────────┬──────────────────────────────────┐ │
│ │ 방식 │ 설명 │ │
│ ├────────────────────┼──────────────────────────────────┤ │
│ │ AES-256 (기본) │ AWS 관리형 키, 추가 비용 없음 │ │
│ │ AWS KMS (선택) │ 고객 관리형 키(CMK), 세밀한 제어 │ │
│ │ │ KMS 비용 별도 발생 │ │
│ └────────────────────┴──────────────────────────────────┘ │
│ │
│ 모든 이미지는 저장 시 자동 암호화 (at-rest encryption) │
│ 전송 중 암호화: HTTPS 강제 (in-transit encryption) │
│ │
└─────────────────────────────────────────────────────────────────┘
7. Lifecycle Policy - 수명 주기 정책
7.1 왜 필요한가? - 이미지가 계속 쌓인다
┌─────────────────────────────────────────────────────────────────┐
│ Lifecycle Policy가 필요한 이유 │
│ │
│ CI/CD 파이프라인이 매일 이미지를 push하면: │
│ │
│ 1월: 30개 이미지 (약 3GB) │
│ 2월: 60개 이미지 (약 6GB) │
│ 3월: 90개 이미지 (약 9GB) │
│ ... │
│ 12월: 365개 이미지 (약 36GB) → 매달 $3.6 저장 비용 │
│ │
│ 실제로 사용하는 이미지: 최근 5개 정도 │
│ 나머지 360개: 비용만 발생하는 디지털 쓰레기! │
│ │
│ Lifecycle Policy = "규칙에 따라 오래된 이미지 자동 삭제" │
│ │
│ 비유: 냉장고 정리 │
│ ├── "유통기한 지난 것은 버린다" = 나이 기반 규칙 │
│ ├── "같은 종류는 3개까지만" = 개수 기반 규칙 │
│ └── "태그 없는 것은 바로 버린다" = untagged 정리 규칙 │
│ │
└─────────────────────────────────────────────────────────────────┘
7.2 규칙 유형과 우선순위
┌─────────────────────────────────────────────────────────────────┐
│ Lifecycle Policy 규칙 유형 │
│ │
│ 1) 이미지 개수 기반 (countNumber): │
│ "최근 N개만 유지, 나머지 삭제" │
│ 예: 태그된 이미지 최근 10개만 유지 │
│ │
│ 2) 이미지 나이 기반 (sinceImagePushed): │
│ "N일 이상 지난 이미지 삭제" │
│ 예: 30일 이상 된 이미지 삭제 │
│ │
│ 3) Untagged 이미지 정리: │
│ "태그 없는 이미지 삭제" │
│ 예: untagged 이미지 1일 후 삭제 │
│ │
│ 우선순위 (rulePriority): │
│ ├── 숫자가 낮을수록 먼저 평가 │
│ ├── 한 이미지에 여러 규칙 해당 시 → 낮은 우선순위 규칙 적용 │
│ └── 같은 이미지가 "유지"와 "삭제" 동시 해당 시 → 유지 우선 │
│ │
└─────────────────────────────────────────────────────────────────┘
7.3 Lifecycle Policy JSON 예시
┌─────────────────────────────────────────────────────────────────┐
│ 실전 Lifecycle Policy 예시 │
│ │
│ { │
│ "rules": [ │
│ { │
│ "rulePriority": 1, │
│ "description": "Untagged 이미지 1일 후 삭제", │
│ "selection": { │
│ "tagStatus": "untagged", │
│ "countType": "sinceImagePushed", │
│ "countUnit": "days", │
│ "countNumber": 1 │
│ }, │
│ "action": { "type": "expire" } │
│ }, │
│ { │
│ "rulePriority": 2, │
│ "description": "dev- 태그 이미지 최근 5개만 유지", │
│ "selection": { │
│ "tagStatus": "tagged", │
│ "tagPatternList": ["dev-*"], │
│ "countType": "imageCountMoreThan", │
│ "countNumber": 5 │
│ }, │
│ "action": { "type": "expire" } │
│ }, │
│ { │
│ "rulePriority": 3, │
│ "description": "모든 태그 이미지 90일 후 삭제", │
│ "selection": { │
│ "tagStatus": "any", │
│ "countType": "sinceImagePushed", │
│ "countUnit": "days", │
│ "countNumber": 90 │
│ }, │
│ "action": { "type": "expire" } │
│ } │
│ ] │
│ } │
│ │
│ 적용 방법: │
│ aws ecr put-lifecycle-policy \ │
│ --repository-name my-app \ │
│ --lifecycle-policy-text file://lifecycle-policy.json │
│ │
│ 테스트 (삭제 시뮬레이션): │
│ aws ecr get-lifecycle-policy-preview \ │
│ --repository-name my-app │
│ → 실제 삭제 전에 어떤 이미지가 삭제될지 미리 확인! │
│ │
└─────────────────────────────────────────────────────────────────┘
8. Image Scanning - 취약점 검사
8.1 Basic Scanning vs Enhanced Scanning
┌─────────────────────────────────────────────────────────────────┐
│ Image Scanning 비교 │
│ │
│ ┌───────────────────┬──────────────────┬──────────────────┐ │
│ │ 항목 │ Basic Scanning │ Enhanced Scanning │ │
│ ├───────────────────┼──────────────────┼──────────────────┤ │
│ │ 엔진 │ Clair (오픈소스) │ Amazon Inspector │ │
│ │ 스캔 대상 │ OS 패키지만 │ OS + 언어 패키지 │ │
│ │ │ │ (npm, pip, maven) │ │
│ │ 스캔 시점 │ Push 시 1회 │ Push 시 + 지속적 │ │
│ │ 새 CVE 발견 시 │ 수동 재스캔 필요 │ 자동 재스캔 │ │
│ │ 비용 │ 무료 │ Inspector 요금 │ │
│ │ EventBridge 연동 │ Yes │ Yes │ │
│ │ 결과 형식 │ CVE ID + 심각도 │ CVE + CVSS 점수 │ │
│ │ │ │ + 영향 패키지 │ │
│ │ │ │ + 수정 버전 │ │
│ └───────────────────┴──────────────────┴──────────────────┘ │
│ │
│ 심각도 레벨: │
│ ┌─────────┬────────────────────────────────────────┐ │
│ │ CRITICAL │ 즉시 조치! 원격 코드 실행 등 │ │
│ │ HIGH │ 가능한 빨리 패치 적용 │ │
│ │ MEDIUM │ 정기 패치 사이클에 포함 │ │
│ │ LOW │ 다음 메이저 업데이트 시 처리 │ │
│ │ INFO │ 참고 사항 │ │
│ └─────────┴────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
8.2 스캐닝 설정과 활용
┌─────────────────────────────────────────────────────────────────┐
│ Image Scanning 실전 활용 │
│ │
│ Basic Scanning 활성화 (Repository 단위): │
│ aws ecr put-image-scanning-configuration \ │
│ --repository-name my-app \ │
│ --image-scanning-configuration scanOnPush=true │
│ │
│ Enhanced Scanning 활성화 (Registry 단위): │
│ aws ecr put-registry-scanning-configuration \ │
│ --scan-type ENHANCED \ │
│ --rules '[{ │
│ "repositoryFilters": [{"filter":"*","filterType":"WILDCARD"}│
│ ], │
│ "scanFrequency": "CONTINUOUS_SCAN" │
│ }]' │
│ │
│ CI/CD에서 스캔 결과 확인 (게이트 역할): │
│ ┌──────┐ ┌──────┐ ┌──────────┐ ┌──────────┐ │
│ │ Build │ → │ Push │ → │ Scan │ → │ 결과확인 │ │
│ │ │ │ ECR │ │ (자동) │ │ CRITICAL? │ │
│ └──────┘ └──────┘ └──────────┘ └─────┬────┘ │
│ Yes │ No │ │
│ ▼ ▼ │
│ ┌────────┐ ┌────────┐ │
│ │ 배포 │ │ 배포 │ │
│ │ 차단! │ │ 진행 │ │
│ └────────┘ └────────┘ │
│ │
│ EventBridge 규칙으로 CRITICAL 발견 시 Slack 알림 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
9. Pull Through Cache - 외부 레지스트리 캐싱
9.1 Pull Through Cache란?
┌─────────────────────────────────────────────────────────────────┐
│ Pull Through Cache 개념 │
│ │
│ 문제: Docker Hub Rate Limiting으로 이미지 pull 실패! │
│ ├── 익명: 100 pulls / 6시간 │
│ ├── 인증: 200 pulls / 6시간 │
│ └── EKS 클러스터 노드가 많으면 순식간에 한도 초과 │
│ │
│ 해결: Pull Through Cache │
│ ├── ECR이 외부 레지스트리의 "프록시/캐시" 역할 │
│ ├── 첫 pull: ECR → 외부 레지스트리에서 가져옴 → 캐시 저장 │
│ ├── 이후 pull: ECR 캐시에서 직접 제공 (외부 요청 없음!) │
│ └── 24시간마다 자동 동기화 (최신 상태 유지) │
│ │
│ 지원 외부 레지스트리: │
│ ┌─────────────────────────┬──────────────────────────┐ │
│ │ 레지스트리 │ ECR 캐시 prefix │ │
│ ├─────────────────────────┼──────────────────────────┤ │
│ │ Docker Hub │ docker-hub/ │ │
│ │ ECR Public Gallery │ ecr-public/ │ │
│ │ Quay.io │ quay/ │ │
│ │ GitHub Container (GHCR) │ ghcr/ │ │
│ │ Kubernetes Registry │ k8s/ │ │
│ └─────────────────────────┴──────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
9.2 Pull Through Cache 동작 흐름
┌─────────────────────────────────────────────────────────────────┐
│ Pull Through Cache 동작 흐름 │
│ │
│ 설정 예시 (Docker Hub 캐시): │
│ aws ecr create-pull-through-cache-rule \ │
│ --ecr-repository-prefix docker-hub \ │
│ --upstream-registry-url registry-1.docker.io \ │
│ --credential-arn arn:aws:secretsmanager:...:dockerhub-creds │
│ │
│ 사용 방법 (URI만 변경!): │
│ 기존: docker pull nginx:latest │
│ 변경: docker pull 123456789012.dkr.ecr.ap-northeast-2 │
│ .amazonaws.com/docker-hub/library/nginx:latest │
│ │
│ 동작 흐름: │
│ │
│ ┌──────────┐ ① pull 요청 ┌──────────┐ │
│ │ EKS │ ──────────────→ │ ECR │ │
│ │ Node │ │ (캐시) │ │
│ └──────────┘ └─────┬────┘ │
│ ▲ │ │
│ │ 캐시에 있나? │
│ │ ┌───┴───┐ │
│ │ YES NO │
│ │ │ │ │
│ │ │ ②외부 레지스트리에서 │
│ │ │ 이미지 가져옴 │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ ┌──────────────┐ │
│ │ │ │ Docker Hub │ │
│ │ │ │ (원본) │ │
│ │ │ └──────┬───────┘ │
│ │ │ │ │
│ │ │ ③ECR에 캐시 저장 │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ④ 이미지 반환 │
│ └────────────────────────────────── │
│ │
│ 장점: │
│ ├── Docker Hub Rate Limit 회피 │
│ ├── 같은 리전 = 빠른 전송 + 전송비 무료 │
│ ├── Lifecycle Policy 적용 가능 (캐시 정리) │
│ └── Image Scanning 적용 가능 (캐시된 이미지도 스캔) │
│ │
└─────────────────────────────────────────────────────────────────┘
10. AWS 서비스 통합
┌─────────────────────────────────────────────────────────────────┐
│ ECR과 AWS 서비스 통합 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ECR (이미지 저장소) │ │
│ │ │ │
│ │ my-app:v1.0 my-api:v2.0 my-worker:v3.0 │ │
│ └──────────┬──────────┬──────────────┬────────────────┘ │
│ │ │ │ │
│ ┌────▼───┐ ┌───▼────┐ ┌────▼─────┐ │
│ │ ECS │ │ EKS │ │ Lambda │ │
│ │Fargate │ │ Pods │ │Container │ │
│ └────────┘ └────────┘ └──────────┘ │
│ │
│ 1) ECS (Elastic Container Service): │
│ ├── Task Definition에서 ECR 이미지 URI 지정 │
│ ├── Task Execution Role에 ecr:GetAuthorizationToken 필요 │
│ ├── ecr:BatchGetImage, ecr:GetDownloadUrlForLayer 필요 │
│ └── 같은 리전 ECR → 전송비 무료, 최소 지연 │
│ │
│ 2) EKS (Elastic Kubernetes Service): │
│ ├── Pod spec의 image 필드에 ECR URI 지정 │
│ ├── Node의 Instance Role에 ECR 읽기 권한 필요 │
│ ├── IRSA (IAM Roles for Service Accounts)로 세밀한 제어 │
│ └── imagePullSecrets 없이 ECR 사용 가능 (IAM 기반) │
│ │
│ 3) Lambda (Container Image): │
│ ├── ⚠️ ECR에 저장된 이미지만 지원! (Docker Hub 직접 불가) │
│ ├── 최대 이미지 크기: 10GB │
│ ├── Lambda Runtime Interface Client 필요 │
│ └── ECR 이미지 URI로 Lambda Function 생성 │
│ │
│ 4) AWS Fargate: │
│ ├── ECS Fargate + EKS Fargate 모두 ECR 네이티브 지원 │
│ ├── SOCI Index로 Lazy Loading 가능 (빠른 시작) │
│ └── VPC Endpoint 사용 시 프라이빗 네트워크로 pull │
│ │
│ 5) 기타 서비스: │
│ ├── CodeBuild: 빌드 환경 이미지로 ECR 사용 │
│ ├── App Runner: ECR 이미지로 웹 서비스 자동 배포 │
│ ├── Batch: 배치 작업 컨테이너 이미지 소스 │
│ └── SageMaker: ML 학습/추론 컨테이너 이미지 │
│ │
│ VPC Endpoint (프라이빗 접근): │
│ ├── com.amazonaws.REGION.ecr.api (ECR API) │
│ ├── com.amazonaws.REGION.ecr.dkr (Docker Registry API) │
│ └── com.amazonaws.REGION.s3 (이미지 레이어 저장소) │
│ → 3개 모두 설정해야 프라이빗 서브넷에서 ECR 사용 가능! │
│ │
└─────────────────────────────────────────────────────────────────┘
11. 태그 전략과 CI/CD
11.1 이미지 태그 전략
┌─────────────────────────────────────────────────────────────────┐
│ 이미지 태그 전략 │
│ │
│ 1) Semantic Versioning (권장): │
│ ├── v1.0.0, v1.1.0, v2.0.0 │
│ ├── 의미: MAJOR.MINOR.PATCH │
│ └── Immutable Tags와 함께 사용 → 버전 추적 완벽 │
│ │
│ 2) Git SHA: │
│ ├── abc1234, def5678 │
│ ├── 커밋과 1:1 매핑 → "어떤 코드로 빌드했는지" 추적 │
│ └── 사람이 읽기 어려움 → 단독 사용보다 보조용 │
│ │
│ 3) 이중 태그 (Best Practice): │
│ docker tag my-app:latest my-app:v1.2.3 │
│ docker tag my-app:latest my-app:abc1234 │
│ → 하나의 이미지에 버전 태그 + Git SHA 태그 동시 부여 │
│ │
│ 4) 환경별 태그: │
│ ├── dev-abc1234 → 개발 환경 │
│ ├── staging-v1.2.3 → 스테이징 환경 │
│ └── v1.2.3 → 프로덕션 환경 │
│ │
│ ⚠️ latest 태그 주의: │
│ ├── 가변이므로 프로덕션에서 사용 금지! │
│ ├── "latest = 최신" 보장 안 됨 (마지막 push된 것일 뿐) │
│ └── Kubernetes에서 imagePullPolicy: Always 강제됨 │
│ │
└─────────────────────────────────────────────────────────────────┘
11.2 GitHub Actions CI/CD 예시
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Actions + ECR CI/CD 파이프라인 │
│ │
│ # .github/workflows/deploy.yml │
│ name: Build and Push to ECR │
│ on: │
│ push: │
│ branches: [main] │
│ │
│ permissions: │
│ id-token: write # OIDC 토큰 발급용 │
│ contents: read │
│ │
│ jobs: │
│ build: │
│ runs-on: ubuntu-latest │
│ steps: │
│ - uses: actions/checkout@v4 │
│ │
│ # OIDC로 AWS 인증 (Access Key 불필요!) │
│ - uses: aws-actions/configure-aws-credentials@v4 │
│ with: │
│ role-to-assume: arn:aws:iam::123456789012:role/github │
│ aws-region: ap-northeast-2 │
│ │
│ # ECR 로그인 │
│ - uses: aws-actions/amazon-ecr-login@v2 │
│ id: ecr-login │
│ │
│ # 이미지 빌드 + Push │
│ - name: Build and Push │
│ env: │
│ REGISTRY: $ │
│ REPO: my-app │
│ TAG: $ │
│ run: | │
│ docker build -t $REGISTRY/$REPO:$TAG . │
│ docker tag $REGISTRY/$REPO:$TAG $REGISTRY/$REPO:latest│
│ docker push $REGISTRY/$REPO:$TAG │
│ docker push $REGISTRY/$REPO:latest │
│ │
│ OIDC 인증의 장점: │
│ ├── Access Key / Secret Key 불필요 (유출 위험 제거) │
│ ├── GitHub → AWS STS → 임시 자격 증명 발급 │
│ ├── 리포지토리별 세밀한 권한 제어 가능 │
│ └── 자격 증명 로테이션 자동 (임시 토큰이므로) │
│ │
└─────────────────────────────────────────────────────────────────┘
12. 비용, 레지스트리 비교, 베스트 프랙티스
12.1 ECR 비용 구조
┌─────────────────────────────────────────────────────────────────┐
│ ECR 비용 구조 │
│ │
│ 1) 저장 비용: │
│ ┌──────────────────────┬───────────────────────────────┐ │
│ │ 항목 │ 비용 │ │
│ ├──────────────────────┼───────────────────────────────┤ │
│ │ Private ECR │ $0.10 / GB / 월 │ │
│ │ Public ECR │ 50GB 무료, 초과 시 $0.10/GB │ │
│ └──────────────────────┴───────────────────────────────┘ │
│ │
│ 2) 데이터 전송 비용: │
│ ┌──────────────────────────────┬────────────────────┐ │
│ │ 전송 경로 │ 비용 │ │
│ ├──────────────────────────────┼────────────────────┤ │
│ │ 같은 리전 AWS 서비스 → ECR │ 무료! │ │
│ │ 다른 리전 → ECR │ 리전간 전송 요금 │ │
│ │ 인터넷 → ECR (inbound) │ 무료 │ │
│ │ ECR → 인터넷 (outbound) │ $0.09/GB~ │ │
│ │ ECR Public → 인터넷 │ 5TB/월 무료 │ │
│ └──────────────────────────────┴────────────────────┘ │
│ │
│ 비용 절약 팁: │
│ ├── Lifecycle Policy로 불필요 이미지 자동 삭제 │
│ ├── 멀티 스테이지 빌드로 이미지 크기 최소화 │
│ ├── Alpine/Distroless 베이스 이미지 사용 │
│ ├── 같은 리전 배치 (전송비 무료 활용) │
│ └── 레이어 캐싱 최적화 (변경 적은 레이어를 위에 배치) │
│ │
│ 비용 계산 예시: │
│ ├── 이미지 크기: 500MB × 20개 = 10GB │
│ ├── 저장 비용: 10GB × $0.10 = $1.00/월 │
│ ├── 같은 리전 pull: $0.00 │
│ └── 총 비용: 약 $1.00/월 (매우 저렴!) │
│ │
└─────────────────────────────────────────────────────────────────┘
12.2 컨테이너 레지스트리 비교
┌─────────────────────────────────────────────────────────────────┐
│ 주요 컨테이너 레지스트리 비교 │
│ │
│ ┌──────────┬───────────┬───────────┬──────────┬──────────┐ │
│ │ 항목 │ ECR │ Docker Hub│ GCR/GAR │ GHCR │ │
│ ├──────────┼───────────┼───────────┼──────────┼──────────┤ │
│ │ 제공자 │ AWS │ Docker │ Google │ GitHub │ │
│ │ 무료 티어 │ - │ 1 private │ 0.5GB │ 500MB │ │
│ │ │ │ repo │ 무료 │ 무료 │ │
│ │ 저장비용 │ $0.10/GB │ $5~$9/월 │ $0.026/ │ $0.008/ │ │
│ │ │ /월 │ (플랜) │ GB/월 │ GB/일 │ │
│ │ IAM통합 │ AWS IAM │ 자체인증 │ GCP IAM │ GitHub │ │
│ │ 스캐닝 │ 기본+고급 │ 유료플랜 │ Artifact │ 없음 │ │
│ │ │ │ │ Analysis │ │ │
│ │ Rate Limit│ 무제한 │ 100~200/ │ 무제한 │ 무제한 │ │
│ │ (같은CSP) │ (같은리전)│ 6h │ (같은GCP)│ │ │
│ │ Lifecycle │ Yes │ Pro만 │ Yes │ 없음 │ │
│ │ 멀티리전 │ Repli- │ 유료 │ Multi- │ 없음 │ │
│ │ 복제 │ cation │ │ region │ │ │
│ │ OCI표준 │ Yes │ Yes │ Yes │ Yes │ │
│ └──────────┴───────────┴───────────┴──────────┴──────────┘ │
│ │
│ 선택 가이드: │
│ ├── AWS 중심 인프라 → ECR (네이티브 통합, 전송비 절약) │
│ ├── GCP 중심 인프라 → Artifact Registry (구 GCR) │
│ ├── 오픈소스/퍼블릭 이미지 → Docker Hub 또는 GHCR │
│ └── 멀티 클라우드 → Harbor (자체 호스팅 오픈소스) │
│ │
└─────────────────────────────────────────────────────────────────┘
12.3 ECR 베스트 프랙티스
┌─────────────────────────────────────────────────────────────────┐
│ ECR 베스트 프랙티스 체크리스트 │
│ │
│ 🔒 보안: │
│ ├── [✓] Immutable Tags 활성화 → 태그 덮어쓰기 방지 │
│ ├── [✓] Enhanced Scanning 활성화 → 취약점 지속 모니터링 │
│ ├── [✓] Repository Policy로 최소 권한 원칙 적용 │
│ ├── [✓] VPC Endpoint 사용 → 프라이빗 네트워크로 접근 │
│ └── [✓] KMS 암호화 → 규정 준수 필요 시 │
│ │
│ 💰 비용: │
│ ├── [✓] Lifecycle Policy 설정 → 불필요 이미지 자동 삭제 │
│ ├── [✓] 멀티 스테이지 빌드 → 이미지 크기 최소화 │
│ ├── [✓] Alpine/Distroless 베이스 → 작은 이미지 │
│ └── [✓] 같은 리전 배치 → 전송비 무료 │
│ │
│ 🏷️ 태그: │
│ ├── [✓] Semantic Versioning (v1.2.3) 사용 │
│ ├── [✓] Git SHA 태그 병행 → 코드 추적 │
│ ├── [✓] latest 태그 프로덕션 사용 금지 │
│ └── [✓] 환경별 태그 prefix (dev-, staging-) │
│ │
│ 🔄 CI/CD: │
│ ├── [✓] OIDC 인증 사용 (Access Key 대신) │
│ ├── [✓] 매 빌드마다 ECR 재로그인 │
│ ├── [✓] 스캔 결과 게이트로 CRITICAL 차단 │
│ └── [✓] Pull Through Cache로 외부 이미지 캐싱 │
│ │
│ 📦 운영: │
│ ├── [✓] Namespace로 레포지토리 계층 정리 │
│ ├── [✓] Cross-Region Replication → DR 대비 │
│ ├── [✓] CloudTrail로 API 호출 감사 │
│ └── [✓] EventBridge로 스캔 결과 알림 설정 │
│ │
│ 이미지 크기 최적화 예시: │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Before: FROM node:18 → 약 1.1 GB │ │
│ │ After: FROM node:18-alpine → 약 170 MB │ │
│ │ Best: Multi-stage + alpine → 약 50~80 MB │ │
│ │ Ultra: Distroless → 약 20~30 MB │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 멀티 스테이지 빌드 예시: │
│ # Stage 1: Build │
│ FROM node:18-alpine AS builder │
│ WORKDIR /app │
│ COPY package*.json . │
│ RUN npm ci --only=production │
│ COPY . . │
│ RUN npm run build │
│ │
│ # Stage 2: Production (빌드 도구 없이 실행 파일만!) │
│ FROM node:18-alpine │
│ WORKDIR /app │
│ COPY --from=builder /app/dist ./dist │
│ COPY --from=builder /app/node_modules ./node_modules │
│ CMD ["node", "dist/main.js"] │
│ │
└─────────────────────────────────────────────────────────────────┘
관련 키워드
ECR, Elastic Container Registry, Repository, Registry, Docker, OCI, Container Image, Tag, Digest, Manifest, Layer, Lifecycle Policy, Image Scanning, Pull Through Cache, Replication, Docker Hub, IAM, Authorization Token, Immutable Tags, ECS, EKS, Lambda, Fargate, CI/CD, GitHub Actions, Semantic Versioning, SOCI, Enhanced Scanning, Amazon Inspector