목차
16 장

ECR — 이미지 레지스트리

ECS와 Lambda가 가져갈 컨테이너 이미지를 어디에 보관하는지 정리합니다. Amazon ECR의 private / public 차이, IAM 인증, docker push / pull, 이미지 스캔, 태그 전략, 라이프사이클 정책, 멀티 아키텍처(linux/amd64 + arm64), VPC Endpoint와 크로스 계정 접근까지 다룹니다.

직전 15장 ECS와 Fargate에서 컨테이너 운영의 그림을 잡았습니다. 한 가지가 비어 있습니다 — 그 ECS / Fargate가 가져가는 이미지가 어디에 있는가입니다. Docker Hub 같은 외부 레지스트리도 가능하지만, AWS 안에서는 표준이 **Amazon ECR (Elastic Container Registry)**입니다.

본 챕터는 ECR의 구조를 한 번에 정리합니다 — private / public 차이, IAM 인증, 이미지 푸시 / 풀, 보안(스캔, 태그 불변성), 운영(라이프사이클 정책, 멀티 아키텍처)입니다. 여기서 잡는 push / pull 흐름과 라이프사이클 정책은 4부 22장 ECS Fargate 배포 골격24장 CI/CD 파이프라인에서 매번 등장합니다.

이미지 레지스트리의 역할 #

도커의 흐름을 떠올려 봅니다.

이미지 라이프사이클
docker build → 로컬 이미지
docker push → 레지스트리 (원격)
docker pull (다른 머신) → 그 이미지를 가져와 docker run

레지스트리는 그 중간 지점입니다. 누가, 어디서, 어떤 버전의 이미지를 받을 수 있는지를 결정합니다.

옵션 비교 #

레지스트리용도
Docker Hub가장 유명. 무료지만 pull 한도, public 기본
GHCR (GitHub Container Registry)GitHub 계정과 연결. private 무료 한도 큼
Amazon ECR PrivateAWS 안에서 IAM으로 인증. ECS / Lambda / EKS와 자연스럽게 연결
Amazon ECR PublicOSS 배포용. 누구나 anonymous pull
GCR / Azure ACR다른 클라우드용

ECS / Lambda / EKS가 AWS 안에 있다면 ECR이 표준입니다.

  • IAM으로 인증합니다 — 별도 비밀번호 관리가 없습니다.
  • VPC Endpoint로 인터넷을 안 거치고 pull 합니다 (NAT 비용 절감).
  • 같은 리전이라 pull이 빠릅니다.
  • 이미지 스캔 (취약점 자동 분석)이 통합됩니다.

Private vs Public #

ECR은 두 종류입니다.

Private (대부분의 경우) #

내 계정의 사용자 / 역할만 접근할 수 있습니다. 회사 / 운영 워크로드는 거의 다 여기입니다.

  • 리전 단위 (이미지마다 리전이 박힙니다)
  • IAM 정책으로 접근 제어
  • 비용: GB 저장 + Data Transfer

Public (OSS 배포 / 학습 자료) #

전 세계 누구나 anonymous pull 가능합니다. AWS가 운영하는 Public Gallery에 노출됩니다.

  • 항상 us-east-1에 호스팅 (글로벌)
  • Push는 IAM 인증, Pull은 무인증
  • 비용: GB 저장 + Data Transfer (push 측)

본 챕터는 Private 기준으로 진행합니다.

Repository 만들기 #

ECR의 단위는 Repository입니다. 하나의 repo 안에 같은 앱의 여러 버전 (태그)을 보관합니다.

repo 만들기
aws ecr create-repository \
  --repository-name myapp \
  --region ap-northeast-2 \
  --image-scanning-configuration scanOnPush=true \
  --encryption-configuration encryptionType=AES256

성공 시 URI입니다.

123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp

이것이 모든 push / pull의 주소입니다. 형태는 다음과 같습니다.

ECR URI의 모양
<계정ID>.dkr.ecr.<리전>.amazonaws.com/<repo>:<태그>

옵션 정리 #

옵션의미
image-scanning-configuration scanOnPush=truepush 시 자동 취약점 스캔
image-tag-mutability IMMUTABLE같은 태그 덮어쓰기 금지 — 운영 권장
encryption-configuration encryptionType=KMS사용자 관리 KMS 키로 암호화

콘솔에서 GUI로도 동일하게 만들 수 있습니다.

인증 — aws ecr get-login-password #

Docker Hub와 달리 ECR은 AWS IAM으로 인증합니다. 비밀번호가 따로 없습니다. 대신 임시 토큰을 받아 docker login 합니다.

ECR 로그인 (12시간 유효)
aws ecr get-login-password --region ap-northeast-2 \
  | docker login --username AWS --password-stdin \
    123456789012.dkr.ecr.ap-northeast-2.amazonaws.com

토큰은 12시간 유효합니다. CI에서는 매번 새로 받아 쓰는 것이 자연스럽습니다 (CI 잡 시작 시 한 번).

필요한 권한 #

push 하는 사용자 / 역할의 정책
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload",
        "ecr:PutImage",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-2:123456789012:repository/myapp"
    }
  ]
}

GetAuthorizationToken* 자원이고, 나머지는 특정 repo로 제한합니다 (6장 보안 기본의 최소 권한).

Pull만 하는 권한 #

ECS Task의 Execution Role처럼 pull만 하면 됩니다. AWS 매니지드 정책 **AmazonECSTaskExecutionRolePolicy**가 ECR pull 권한을 자동 포함합니다.

Push / Pull #

Push #

첫 push
# 빌드
docker build -t myapp:v1 .

# 태그 (ECR URI로)
docker tag myapp:v1 \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

# 로그인 (위 참고)
aws ecr get-login-password --region ap-northeast-2 | docker login ...

# push
docker push \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

이미지가 100MB 면 첫 push는 100MB 업로드입니다. 다음부터는 레이어 단위 캐싱이라 변경된 부분만 올라가서 보통 수 MB입니다.

Pull #

pull
docker pull 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

ECS / Lambda는 자동으로 pull 합니다. 콘솔에서 직접 할 일은 거의 없지만 디버깅 시 유용합니다.

태그 전략 #

ECR repo 안에 같은 이미지의 여러 버전을 어떻게 부를지에 관한 규칙입니다. 자주 보는 패턴입니다.

1) Semver #

myapp:1.4.2
myapp:1.4
myapp:1
myapp:latest

라이브러리 / 도구처럼 외부에 배포하는 용도에 자연스럽습니다. 운영에서는 latest가 위험합니다 (어느 시점의 latest인지 모호).

2) Git SHA #

myapp:abc1234        ← short sha
myapp:abc1234567...  ← full sha

CI에서 빌드한 commit과 1:1 매칭됩니다. 운영에 가장 추천하는 방식입니다 — 어느 commit이 운영에 있는지 즉시 추적할 수 있습니다.

3) 환경 + 시퀀스 #

myapp:prod-2025-04-01.001
myapp:staging-2025-04-01.005

릴리스를 날짜별로 세는 용도에서 씁니다.

4) 멀티 태그 #

운영 권장 패턴은 immutable + alias입니다.

이미지 한 번 빌드 → 두 태그
docker tag myapp:abc1234 myapp:abc1234           # 불변 (영원히 그대로)
docker tag myapp:abc1234 myapp:prod-current      # 가변 (현재 운영 가리킴)

ECR repo 자체는 IMMUTABLE (한 번 push 한 태그를 못 덮어씀)로 두고, alias가 필요하면 별도 도구 (deployment system)가 관리합니다.

이미지 스캔 #

ECR은 push 한 이미지의 취약점을 자동 스캔해 줍니다. scanOnPush=true 옵션입니다 (위에서 설정).

두 종류 #

종류무엇비용
Basic Scanningopen source CVE DB (CoreOS Clair) 기반 단발 스캔무료
Enhanced ScanningInspector 통합. OS 레이어 + 언어 라이브러리 (npm, pip 등)까지. 지속 모니터링 (이미지 push 후에도 새 CVE 발견 시 알림)repo 당 시간 / 이미지당

운영 워크로드는 Enhanced를 검토합니다. Basic도 첫 출발에는 충분합니다.

결과 보기 #

스캔 결과
aws ecr describe-image-scan-findings \
  --repository-name myapp \
  --image-id imageTag=v1

콘솔에서는 repo → 이미지 → “Vulnerabilities” 탭에서 CRITICAL / HIGH / MEDIUM / LOW 수가 한눈에 보입니다.

빌드 단에서 차단하기 #

CRITICAL이 있으면 배포를 막습니다 — CI 잡에서 다음을 둡니다.

CI 게이트
CRITICAL=$(aws ecr describe-image-scan-findings \
  --repository-name myapp --image-id imageTag=$SHA \
  --query 'imageScanFindings.findingSeverityCounts.CRITICAL' \
  --output text)

if [ "$CRITICAL" != "None" ] && [ "$CRITICAL" -gt 0 ]; then
  echo "🚨 CRITICAL CVE 발견. 배포 중단."
  exit 1
fi

이 게이트는 24장 CI/CD 파이프라인에서 빌드 단계에 그대로 끼워 넣습니다.

라이프사이클 정책 — 자동 정리 #

이미지가 쌓이면 ECR 비용이 옵니다. 라이프사이클 정책으로 자동 정리합니다.

lifecycle.json
{
  "rules": [
    {
      "rulePriority": 1,
      "description": "untagged 이미지 7일 후 삭제",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "최신 30개만 유지 (그 외 삭제)",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 30
      },
      "action": { "type": "expire" }
    }
  ]
}
적용
aws ecr put-lifecycle-policy \
  --repository-name myapp \
  --lifecycle-policy-text file://lifecycle.json

흔한 패턴은 다음과 같습니다.

  • untagged 7~14일 후 삭제 (실패한 빌드의 잔재)
  • 태그 prefix pr- 인 것 30일 후 삭제 (PR 미리보기 이미지)
  • 태그 prefix release-는 영구 보존

운영 워크로드는 1 년치 이미지가 누적되면 GB 단위 비용이 듭니다. 라이프사이클을 처음에 잡아 두는 것이 운영 위생의 핵심입니다.

멀티 아키텍처 이미지 #

Apple Silicon Mac (arm64)에서 빌드한 이미지를 그대로 운영 (보통 amd64)에 push 하면 부팅이 안 됩니다. 두 길이 있습니다.

1) buildx로 다중 플랫폼 push #

buildx multi-arch
docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1 \
  --push .

ECR 한 태그 (v1) 안에 두 아키텍처가 함께 들어가는 manifest list입니다. pull 측이 자기 아키텍처에 맞는 것을 자동 선택합니다.

2) Fargate ARM으로 통일 #

15장 Fargate에서 task definition의 runtimePlatform.cpuArchitecture: ARM64로 두면 ARM 전용 이미지만 push 해도 됩니다. 단가가 약 20% 저렴한 보너스가 있습니다.

Task Definition (ARM)
{
  "runtimePlatform": {
    "cpuArchitecture": "ARM64",
    "operatingSystemFamily": "LINUX"
  }
}

작은 / 중간 트래픽의 새 프로젝트는 처음부터 ARM을 권장합니다.

VPC Endpoint — NAT 없이 pull #

private subnet의 ECS Task가 ECR에서 pull 하면 기본은 NAT Gateway 경유라 GB 당 과금됩니다.

ECR은 VPC Endpoint를 지원해 NAT를 우회합니다.

ECR VPC Endpoint 두 개
# api 호출용
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.ecr.api \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-aaa subnet-bbb

# 이미지 layer 다운로드용
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.ecr.dkr \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-aaa subnet-bbb

# 이미지 layer가 S3에 살아 있어 S3 endpoint도 같이
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.s3 \
  --vpc-endpoint-type Gateway \
  --route-table-ids rtb-xxx

세 개 (api, dkr, s3)가 한 세트입니다. NAT Gateway 비용을 크게 줄여 줍니다 — 운영 트래픽이 큰 환경에서는 거의 필수입니다. VPC와 endpoint의 깊이는 28장 VPC 깊이에서 더 다룹니다.

크로스 계정 접근 #

prod 계정의 ECR repo를 dev 계정에서 pull 하고 싶다면 Repository Policy로 허용합니다.

repo-policy.json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "AllowDevAccountPull",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::222222222222:root"
      },
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    }
  ]
}
aws ecr set-repository-policy \
  --repository-name myapp \
  --policy-text file://repo-policy.json

원칙은 다음과 같습니다. 운영 이미지는 운영 계정의 ECR에 한 번만 두고 다른 계정은 pull 합니다. 같은 이미지를 환경별 계정에 중복 빌드하지 않습니다. 멀티 어카운트 거버넌스는 29장 보안 거버넌스에서 다룹니다.

비용 #

항목가격 (서울 리전)
StorageGB / 월 $0.10
Data Transfer Out (인터넷)GB $0.126 (1GB 무료)
Data Transfer Out (같은 리전)무료
Enhanced Scanning이미지당 + repo 당 시간

같은 리전 안의 ECS가 pull 하면 무료입니다. 인터넷으로 나가는 (CI / 외부 도구) pull만 비용입니다. 라이프사이클 정책으로 storage만 잡으면 비용은 거의 0에 가깝습니다.

자주 만나는 함정 #

1) denied: ... is not authorized to perform: ecr:... #

권한 누락입니다. 둘 다 확인합니다.

  • 사용자 / 역할 정책에 ECR action이 들어 있는지
  • Repository Policy가 차단하지 않는지 (보통 비어 있음 — 비어 있으면 IAM 정책만으로 충분)

2) manifest unknown 또는 repository ... not found #

99%는 리전 / 계정 ID 오타입니다. URI의 ap-northeast-2 위치, 123456789012 위치를 다시 확인합니다.

3) IMMUTABLE repo에 같은 태그 push #

같은 태그를 두 번 push 하려고 하면 거부됩니다. 의도된 동작이며 운영 권장입니다. CI 잡에서 commit SHA로 태그 짓는 식으로 우회합니다.

4) 멀티 아키텍처 빠뜨림 #

Mac (ARM)에서 빌드 → push → x86_64 Fargate에서 exec format error가 납니다. buildx로 multi-arch 하거나 task definition을 ARM으로 통일합니다.

5) NAT Gateway 비용 폭발 #

ECR pull이 NAT를 거치면 GB 당 과금됩니다. VPC Endpoint 셋 (api / dkr / s3)을 추가합니다.

6) 이미지 누적 #

라이프사이클 정책 없이 1년 운영하면 수천 개의 이미지가 GB 단위 비용을 만듭니다. 첫 repo 만들 때 라이프사이클도 같이 둡니다.

연습문제 #

  1. 본인의 앱 이미지에 어떤 태그 전략을 쓸지 §“태그 전략"의 네 패턴 중 하나를 고르고, 운영에서 latest를 쓰지 않는 이유를 한 줄로 적어 두세요. 24장 CI/CD 파이프라인에서 CI가 commit SHA로 태그를 붙일 때 이 결정이 기준이 됩니다.
  2. private subnet의 ECS Task가 ECR에서 pull 할 때 NAT Gateway를 거치는 경로와 VPC Endpoint(api / dkr / s3)를 거치는 경로의 비용 차이를 §“VPC Endpoint"와 §“비용"을 근거로 한 단락으로 설명해 보세요(15장 ECS와 Fargate의 NAT 비용 함정과 묶어 봅니다).
  3. 라이프사이클 정책에서 untagged 이미지 7일 삭제 + 최신 30개 유지 룰을 적용하면, 1년 동안 매일 한 번씩 배포할 때 repo에 남는 이미지 수가 대략 얼마가 되는지 §“라이프사이클 정책"을 근거로 추정해 보세요.

한 줄 요약: ECR은 AWS 안의 컨테이너 이미지 레지스트리로, ECS / Lambda / EKS와 IAM으로 연결되며 운영은 Private repo를 쓴다. 인증은 12시간짜리 토큰을 받는 aws ecr get-login-password이고, 운영 권장 태그는 Git SHA + IMMUTABLE이다. push 시 이미지 스캔으로 CRITICAL을 CI에서 차단하고, 라이프사이클 정책으로 이미지를 자동 정리한다. 멀티 아키텍처는 buildx 또는 Fargate ARM으로 해결하고, VPC Endpoint(api·dkr·s3)로 NAT 비용을 회피한다.

다음 챕터 #

ECS와 ECR은 컨테이너가 항상 떠 있는 모델입니다. 다음 17장 Lambda 기초에서는 반대편 — 요청이 올 때만 함수가 깨어나는 서버리스 — 를 다룹니다. Lambda의 동작 방식, runtime / handler / 이벤트 모델, 콜드 스타트, 동시성, 로깅까지 AWS 서버리스의 첫 단추를 정리합니다.

X