本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
迁移到 Amazon ECR 存储库时自动识别重复的容器映像
Rishabh Yadav 和 Rishi Singla,Amazon Web Services
Summary
此模式提供了一种自动解决方案,可识别存储在不同容器存储库中的映像是否重复。如果您计划将映像从其他容器存储库迁移到 Amazon Elastic Container Registry(Amazon ECR),此检查非常有用。
有关基础信息,此模式还描述了容器映像的组件,例如映像摘要、清单和标签。当您计划迁移到 Amazon ECR 时,您可能会决定通过比较映像摘要来跨容器注册表同步您的容器映像。迁移容器映像之前,您需要检查这些映像是否已存在于 Amazon ECR 存储库中,以防止重复。但是,可能很难通过比较映像摘要来检测重复,并且这可能会导致初始迁移阶段就出现问题。 此模式比较了存储在不同容器注册表中的两个相似映像的摘要,并解释了摘要不同的原因,以帮助您准确比较映像。
先决条件和限制
活跃的 AWS 账户
熟悉以下内容: AWS 服务
已配置的 CodeCommit 凭证(参见说明)
架构
容器映像组件
下图阐明了容器映像的其中一些组件。有关这些组件的描述,可参阅图表后面的内容。

术语和定义
开放容器倡议(OCI)映像规范
注册表:用于映像存储和管理的服务。
客户端:与注册机构通信并处理本地映像的工具。
推送:将映像上传到注册表的过程。
拉取:从注册表下载映像的过程。
Blob:由注册表存储且可通过摘要进行寻址的二进制形式内容。
索引:用于识别不同计算机平台(例如 x86-64 或 ARM 64 位)或媒体类型的多个映像清单的构造。有关更多信息,请参阅 OCI 映像索引规范
。 清单:用于定义通过清单端点上传的映像或构件的 JSON 文档。清单可以通过使用描述符来引用存储库中的其他 Blob。有关更多信息,请参阅 OCI 映像清单规范
。 文件系统层:映像的系统库和其他依赖项。
配置:包含构件元数据且可在清单中引用的 blob。有关更多信息,请参阅 OCI 映像设置规范
。 对象或构件:存储为 blob 并与具有配置的配套清单关联的概念性内容项。
摘要:根据清单内容的加密哈希值创建的唯一标识符。映像摘要有助于唯一标识不可变容器映像。使用其摘要拉取映像时,无论在何种操作系统或架构上,每次都会下载相同的映像。有关更多信息,请参阅 OCI 映像规范
。 标签:人类可读的清单标识符。相较于不可变的映像摘要,标签是动态变化的。指向映像的标签可以更改并从一个映像移动到另一个映像,不过其底层映像摘会保持不变。
目标架构
下图显示了此模式提供的解决方案的高级架构,即通过比较存储在 Amazon ECR 和私有存储库中的映像来识别重复的容器映像。

工具
AWS 服务
CloudFormation帮助您设置 AWS 资源,快速一致地配置资源,并在资源的整个生命周期中跨地区对其 AWS 账户 进行管理。
AWS CodeBuild 是一项完全托管式构建服务,可编译源代码、运行单元测试和生成部署就绪的构件。
AWS CodeCommit 是一项版本控制服务,可帮助您私下存储和管理 Git 存储库,而无需管理自己的源代码控制系统。
AWS CodePipeline 可帮助您快速对软件发布过程的不同阶段进行建模和配置,并自动执行持续发布软件变更所需步骤。
Amazon Elastic Container Registry(Amazon ECR)是一项安全、可扩展且可靠的托管容器映像注册表服务。
代码
此模式的代码可在存储库中找到,用于识别 GitHub 存储库之间重复的容器镜像的自动解决方案
最佳实践
CloudFormation 最佳实践
AWS CodePipeline 最佳实践
操作说明
| Task | 说明 | 所需技能 |
|---|---|---|
从 Amazon ECR 公有存储库中拉取映像。 | 从终端运行以下命令,以从 Amazon ECR 公有存储库中拉取映像
将映像拉取到本地计算机后,您将看到以下拉取摘要,而该摘要可表示映像索引。
| 应用程序开发人员、AWS DevOps、AWS 管理员 |
将映像推送到 Amazon ECR 私有存储库。 |
| AWS 管理员、AWS DevOps、应用程序开发者 |
从 Amazon ECR 私有存储库中拉取相同的映像。 |
| 应用程序开发人员、AWS DevOps、AWS 管理员 |
| Task | 说明 | 所需技能 |
|---|---|---|
查找存储在 Amazon ECR 公有存储库中的映像的清单。 | 从终端运行以下命令,以从 Amazon ECR 公有存储库中拉取映像
| AWS 管理员、AWS DevOps、应用程序开发者 |
查找存储在 Amazon ECR 私有存储库中的映像的清单。 | 从终端运行以下命令,以从 Amazon ECR 私有存储库中拉取映像
| AWS DevOps、AWS 系统管理员、应用程序开发者 |
将 Docker 拉取的摘要与 Amazon ECR 私有存储库中映像的清单摘要进行比较。 | 另一个问题是,为什么 docker pull 命令提供的摘要与映像 用于 docker pull 的摘要表示存储在注册表中的映像清单的摘要。此摘要可视为哈希链的根,因为清单包含将下载并导入到 Docker 中的内容的哈希值。 Docker 中使用的映像 ID 可以在此清单 要确认此信息,您可以比较 Amazon ECR 公有和私有存储库上的 docker inspect 命令的输出: 结果验证了两个映像具有相同的映像 ID 摘要和层摘要。 身份证: 层: 此外,摘要的基础是本地管理的对象的字节(本地文件是容器映像层的 tar)或推送到注册表服务器的 blob。但是,当您将 blob 推送到注册表时,tar 会被压缩,并在压缩的 tar 文件中计算摘要。因此,docker pull 摘要值的差异源于在注册表(Amazon ECR 私有或公有)级别应用的压缩。 注意此解释专门针对使用 Docker 客户端的情况。您不会在其他客户端(例如 nerdctl 或 Finch)上看到这种行为,因为在推拉操作期间,其不会自动压缩映像。 | AWS DevOps、AWS 系统管理员、应用程序开发者 |
| Task | 说明 | 所需技能 |
|---|---|---|
克隆存储库。 | 将此模式的 GitHub 存储库克隆到本地文件夹:
| AWS 管理员,AWS DevOps |
设置 CI/CD 管道。 | GitHub 存储库中包含一个用于创建 CloudFormation 堆栈的
该管道将设置为两个阶段(CodeCommit 以及 CodeBuild,如架构图所示),以识别私有存储库中也存在于公共存储库中的图像。管道配置有以下资源:
| AWS 管理员,AWS DevOps |
填充 CodeCommit 存储库。 | 要填充 CodeCommit 存储库,请执行以下步骤:
| AWS 管理员,AWS DevOps |
清理。 | 为避免将来产生费用,请按照以下步骤删除资源:
| AWS 管理员 |
问题排查
| 问题 | 解决方案 |
|---|---|
当您尝试从终端或命令行推送、拉取 CodeCommit 存储库或以其他方式与仓库交互时,系统会提示您提供用户名和密码,并且必须为您的 IAM 用户提供 Git 证书。 | 出现此错误的最常见原因如下:
根据您的操作系统和本地环境,您可能需要安装凭证管理器、配置操作系统随附的凭证管理器或自定义您的本地环境以使用凭证存储。例如,如果您的计算机运行的是 macOS,您可以使用 Keychain Access 实用程序存储您的凭证。如果您的计算机运行的是 Windows,您可以使用随 Windows 版 Git 安装的 Git Credential Manager。有关更多信息,请参阅文档中的使用 Git 凭据为 HTTPS 用户设置和 Git CodeCommit 文档中的凭据存储 |
将映像推送到 Amazon ECR 存储库时,您会遇到 HTTP 403 或“没有基本的身份验证凭证”错误。 | 即使你已使用 aws ecr 命令成功向 Docker 进行了身份验证,你也可能会在 docker push 或 docker pull 命令中遇到这些错误消息。 get-login-password已知原因包括: |
相关资源
Amazon ECR 中的私有映像(Amazon ECR 文档)
AWS::CodePipeline::Pipeline 资源(CloudFormation 文档)
附加信息
Amazon ECR 公有存储库中映像的 Docker 检查输出
[ { "Id": "sha256:f7cee5e1af28ad4e147589c474d399b12d9b551ef4c3e11e02d982fce5eebc68", "RepoTags": [ "<account-id>.dkr.ecr.us-east-1.amazonaws.com/test_ecr_repository:latest", "public.ecr.aws/amazonlinux/amazonlinux:2018.03" ], "RepoDigests": [ "<account-id>.dkr.ecr.us-east-1.amazonaws.com/test_ecr_repository@sha256:52db9000073d93b9bdee6a7246a68c35a741aaade05a8f4febba0bf795cdac02", "public.ecr.aws/amazonlinux/amazonlinux@sha256:f972d24199508c52de7ad37a298bda35d8a1bd7df158149b381c03f6c6e363b5" ], "Parent": "", "Comment": "", "Created": "2023-02-23T06:20:11.575053226Z", "Container": "ec7f2fc7d2b6a382384061247ef603e7d647d65f5cd4fa397a3ccbba9278367c", "ContainerConfig": { "Hostname": "ec7f2fc7d2b6", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"/bin/bash\"]" ], "Image": "sha256:c1bced1b5a65681e1e0e52d0a6ad17aaf76606149492ca0bf519a466ecb21e51", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "20.10.17", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "sha256:c1bced1b5a65681e1e0e52d0a6ad17aaf76606149492ca0bf519a466ecb21e51", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 167436755, "VirtualSize": 167436755, "GraphDriver": { "Data": { "MergedDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/merged", "UpperDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/diff", "WorkDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:d5655967c2c4e8d68f8ec7cf753218938669e6c16ac1324303c073c736a2e2a2" ] }, "Metadata": { "LastTagTime": "2023-03-02T10:28:47.142155987Z" } } ]
Amazon ECR 私有存储库中映像的 Docker 检查输出
[ { "Id": "sha256:f7cee5e1af28ad4e147589c474d399b12d9b551ef4c3e11e02d982fce5eebc68", "RepoTags": [ "<account-id>.dkr.ecr.us-east-1.amazonaws.com/test_ecr_repository:latest", "public.ecr.aws/amazonlinux/amazonlinux:2018.03" ], "RepoDigests": [ "<account-id>.dkr.ecr.us-east-1.amazonaws.com/test_ecr_repository@sha256:52db9000073d93b9bdee6a7246a68c35a741aaade05a8f4febba0bf795cdac02", "public.ecr.aws/amazonlinux/amazonlinux@sha256:f972d24199508c52de7ad37a298bda35d8a1bd7df158149b381c03f6c6e363b5" ], "Parent": "", "Comment": "", "Created": "2023-02-23T06:20:11.575053226Z", "Container": "ec7f2fc7d2b6a382384061247ef603e7d647d65f5cd4fa397a3ccbba9278367c", "ContainerConfig": { "Hostname": "ec7f2fc7d2b6", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"/bin/bash\"]" ], "Image": "sha256:c1bced1b5a65681e1e0e52d0a6ad17aaf76606149492ca0bf519a466ecb21e51", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "20.10.17", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "sha256:c1bced1b5a65681e1e0e52d0a6ad17aaf76606149492ca0bf519a466ecb21e51", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 167436755, "VirtualSize": 167436755, "GraphDriver": { "Data": { "MergedDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/merged", "UpperDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/diff", "WorkDir": "/var/lib/docker/overlay2/c2c2351a82b26cbdf7782507500e5adb5c2b3a2875bdbba79788a4b27cd6a913/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:d5655967c2c4e8d68f8ec7cf753218938669e6c16ac1324303c073c736a2e2a2" ] }, "Metadata": { "LastTagTime": "2023-03-02T10:28:47.142155987Z" } } ]