AWS ECR


1. Important Points#

Amazon ECR 是 AWS 的 container image registry。常见用途是给 ECS / EKS / Lambda container image / App Runner 存放私有镜像。

ECR 用来做:
    store private container images
    push images from CI/CD
    pull images from ECS / EKS / EC2 / Lambda
    scan images
    apply lifecycle policy
    replicate images across regions/accounts

ECR 不负责:
    build image
    run container
    deploy application
    replace Dockerfile security practices

核心原则:

repository:
    one app/service usually has one repository
    use lowercase name, optionally with namespace: platform/order-api

tag:
    avoid relying only on latest
    use immutable deploy tags, for example git sha or build timestamp
    keep human-readable tags only as convenience

security:
    push principal needs limited ECR write permissions
    runtime role only needs pull permissions
    do not expose images across accounts without repository policy

2. Core Concepts#

Concept Meaning Example
Registry account + region registry endpoint 111122223333.dkr.ecr.ap-east-1.amazonaws.com
Repository image namespace inside registry base/node
Image tag mutable or immutable label 22-alpine
Image digest immutable content address sha256:...
Authorization token Docker login credential from AWS CLI valid for 12 hours
Lifecycle policy cleanup old images keep last N tags
Repository policy cross-account / principal access allow another account pull

Registry format:

<aws_account_id>.dkr.ecr.<region>.amazonaws.com

Image format:

<aws_account_id>.dkr.ecr.<region>.amazonaws.com/<repository>:<tag>

3. IAM#

Push principal minimum actions:

{
  "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:DescribeRepositories",
        "ecr:DescribeImages"
      ],
      "Resource": "arn:aws:ecr:ap-east-1:111122223333:repository/base/node"
    }
  ]
}

Pull principal minimum actions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchCheckLayerAvailability"
      ],
      "Resource": "arn:aws:ecr:ap-east-1:111122223333:repository/base/node"
    }
  ]
}

Notes:

ecr:GetAuthorizationToken:
    resource must be "*"

push:
    used by CI/CD, Jenkins, GitHub Actions, GitLab CI, developer machine

pull:
    used by ECS task execution role, EKS node/pod role, EC2 instance role

4. Repository Configuration#

Create repository:

aws ecr create-repository --repository-name base/node --image-tag-mutability IMMUTABLE \
  --image-scanning-configuration scanOnPush=true --region ap-east-1

Check:

aws ecr describe-repositories --repository-names base/node --region ap-east-1

Recommended defaults:

image tag immutability:
    enable for deploy repositories
    prevents accidental overwrite of existing tags

scan on push:
    useful baseline
    for stronger security, configure ECR registry scanning / Inspector where available

KMS:
    default encryption is usually enough
    use customer managed KMS key only when compliance requires it

5. Push Docker Hub Image To ECR#

目标:把 Docker Hub 的 node:22-alpine 下载到本地,重新打 ECR tag,登录 ECR,再 push 到 ECR。

Example values:

export AWS_REGION=ap-east-1
export AWS_ACCOUNT_ID=111122223333
export ECR_REPOSITORY=base/node
export SOURCE_IMAGE=node:22-alpine
export ECR_IMAGE="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}:22-alpine"

Create repository if needed:

aws ecr create-repository --repository-name "${ECR_REPOSITORY}" --image-tag-mutability IMMUTABLE \
  --image-scanning-configuration scanOnPush=true --region "${AWS_REGION}"

Pull from Docker Hub:

docker pull "${SOURCE_IMAGE}"
docker images "${SOURCE_IMAGE}"

Tag for ECR:

docker tag "${SOURCE_IMAGE}" "${ECR_IMAGE}"
docker images "${ECR_IMAGE}"

Login to ECR:

aws ecr get-login-password --region "${AWS_REGION}" \
  | docker login --username AWS --password-stdin "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"

Push to ECR:

docker push "${ECR_IMAGE}"

Verify:

aws ecr describe-images --repository-name "${ECR_REPOSITORY}" \
  --image-ids imageTag=22-alpine --region "${AWS_REGION}"

Pull back from ECR:

docker pull "${ECR_IMAGE}"

Expected:

docker push:
    layers are pushed or reused
    output ends with digest: sha256:...

describe-images:
    imageTag contains 22-alpine
    imageDigest is present
    imagePushedAt is recent

Common mistakes:

repository does not exist:
    create repository first, unless using repository creation template

no basic auth credentials:
    run aws ecr get-login-password | docker login again

ImageTagAlreadyExistsException:
    repository is immutable and tag already exists
    use a new tag or delete old image intentionally

wrong region/account:
    registry endpoint must match target AWS account and region

Docker Hub rate limit:
    use ECR pull through cache or mirror base images during CI setup

6. Lifecycle Policy#

Example: keep recent images and avoid unbounded storage growth.

Important limitation:

ECR lifecycle policy:
    tagPrefixList and tagPatternList are not regular expressions
    tagPatternList uses wildcard style matching, for example prod* or *prod*
    native ECR lifecycle policy cannot directly express regex like ^(?!v).*

If your own UI/tool has an "Image tag filters" regex field:
    use ^(?!v).* to match tags that do not start with v

For native ECR lifecycle policy:
    prefer tag naming conventions such as build-*, sha-*, pr-*
    then use tagPatternList / tagPrefixList on those prefixes

Regex meaning for external tools:

^(?!v).*
    ^       start of tag
    (?!v)   next character is not v
    .*      rest of tag

matches:
    22-alpine
    build-20260612
    sha-a1b2c3d

does not match:
    v1.2.3
    v20260612
{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Keep last 30 non-release node base images",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["22-"],
        "countType": "imageCountMoreThan",
        "countNumber": 30
      },
      "action": {
        "type": "expire"
      }
    },
    {
      "rulePriority": 2,
      "description": "Expire untagged images after 7 days",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": {
        "type": "expire"
      }
    }
  ]
}

如果 tag 约定是:

release tags:
    v1.2.3
    v1.2.4

non-release build tags:
    build-20260612-120000
    sha-a1b2c3d

可以用 ECR 原生 lifecycle policy 只评估 build tag:

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Expire old build tags, do not touch v release tags",
      "selection": {
        "tagStatus": "tagged",
        "tagPatternList": ["build-*"],
        "countType": "imageCountMoreThan",
        "countNumber": 30
      },
      "action": {
        "type": "expire"
      }
    }
  ]
}

如果当前已有大量非 v 开头 tag,但没有统一 prefix,ECR 原生 lifecycle policy 不能用一个 negative regex 覆盖它们。更好的做法是先统一新 tag 规则,或用外部清理脚本通过 describe-images 过滤 tag 后再删除。

Apply:

aws ecr put-lifecycle-policy \
  --repository-name base/node \
  --lifecycle-policy-text file://lifecycle-policy.json \
  --region ap-east-1

7. Troubleshooting#

Symptom Check
no basic auth credentials Docker is not logged in to target ECR registry
repository does not exist repository name and region
denied: User is not authorized IAM permissions for push/pull principal
ImageTagAlreadyExistsException immutable tag already exists
push goes to wrong account AWS_ACCOUNT_ID, AWS profile, registry URL
ECS cannot pull image task execution role, private subnet egress/VPC endpoints

Useful commands:

aws sts get-caller-identity
aws ecr describe-repositories --region ap-east-1
aws ecr describe-images --repository-name base/node --region ap-east-1
docker images
docker logout 111122223333.dkr.ecr.ap-east-1.amazonaws.com

8. Checklist#

1. repository exists
2. region and account are correct
3. Docker is logged in to ECR
4. image is tagged with full ECR registry/repository/tag
5. push principal has least-privilege ECR write permissions
6. runtime principal has ECR pull permissions
7. lifecycle policy exists
8. tag immutability decision is explicit
9. ECS/EKS/EC2 pull path has network access to ECR