本页面介绍映像摘要,包括什么是映像摘要、如何查找映像摘要以及如何在 Kubernetes 集群中强制使用它们。本页面适用于构建和部署容器映像的开发者和运营商。
容器映像摘要是唯一且不可改变地标识容器映像。通过摘要部署映像时,您可以避免通过映像标记进行部署的缺点。
本页面中的命令假定您可以使用已安装的工具(例如 Google Cloud CLI、Docker、cURL、jq
和 pack
)访问 Linux 或 macOS shell 环境。或者,您可以使用预先安装了这些工具的 Cloud Shell。
容器映像和映像标记
使用容器映像时,需要一种方法来引用所使用的映像。映像标签是引用映像不同版本的常用方法。一种常见的方法是在构建时用版本标识符标记映像。例如,v1.0.1
可以引用您称为 1.0.1
的版本。
标记使映像修订易于通过人类可读的字符串查找。但是,标记是可变引用,这意味着标签所引用的映像可以更改,如下图所示:
如上图所示,如果您发布的映像所使用的标记与现有映像相同,则该标记会停止指向现有映像,并开始指向新映像。
映像标记的缺点
标记是可变的,因此当您使用它们部署映像时,它们具有以下缺点:
在 Kubernetes 中,按标签部署可能会导致意外结果。例如,假设您有一个现有的 Deployment 资源,该资源通过标记
v1.0.1
引用了一个容器映像。要修复错误或进行一些小的更改,您的构建过程将创建一个具有相同标记v1.0.1
的新映像。即使您不更改 Deployment 资源规范,通过 Deployment 资源创建的新 Pod 仍可以使用旧映像或新映像。此问题也适用于其他 Kubernetes 资源,例如StatefulSets、DaemonSets、ReplicaSets 和 Jobs。如果您使用工具扫描或分析映像,由这些工具生成的结果将仅对扫描的映像有效。为了确保您部署已经过扫描的映像,您无法依赖标记,因为该标记引用的映像可能已经更改。
如果您将 Binary Authorization 与 Google Kubernetes Engine (GKE) 搭配使用,则基于标记的部署会被禁止使用,因为无法确定创建 Pod 时使用的确切映像。
部署映像时,您可以使用映像摘要来避免使用标记的劣势。您仍然可以根据需要为映像添加标记,但并非必须这样做。
映像的结构
映像由以下组件组成:
这些组件如下图所示:
上图显示了与映像组件有关的其他详细信息:
- 映像清单是一个 JSON 文档,其中包含对配置对象、文件系统层和可选元数据的引用。
- 映像清单使用
digest
特性引用配置对象和每个文件系统层。digest
特性的值是摘要所引用的内容的加密哈希值,通常使用 SHA-256 算法进行计算。 - 摘要值用于构建对象的不可变地址。此过程称为内容可寻址的存储,这意味着您可以根据其摘要检索映像清单、映像索引、配置对象和层。
- 映像摘要是映像索引或映像清单 JSON 文档的哈希值。
- 配置对象是一个 JSON 文档,用于定义映像的特性,例如 CPU 架构、入口点、公开端口和环境变量。
- 文件系统层数组定义了容器运行时堆叠层的顺序。这些层作为 tar 文件分发,通常使用
gzip
实用程序进行压缩。 - 可选映像索引(有时称为清单列表)引用一个或多个映像清单。该参考是映像清单的摘要。在为不同的平台(例如
amd64
和arm64
架构)生成多个相关映像时,映像索引会非常有用。
如需了解详情,请参阅浏览映像清单、摘要和标记部分。
查找映像摘要
如需使用映像摘要进行部署,您必须先找到摘要。然后,您可以将摘要与部署命令一起使用,或将其包含在 Kubernetes 清单中。
您可以通过多种方式获取映像的摘要,具体取决于您当前的情况。以下部分包含不同产品和工具的示例。
在以下部分中,在 Cloud Shell 或已安装 gcloud CLI、Docker、cURL 和 jq
等工具的 shell 环境中运行命令。
Artifact Registry
对于存储在 Artifact Registry 中的映像,您可以使用
gcloud artifacts docker images describe
命令。gcloud artifacts docker images describe \ LOCATION-docker.pkg.dev/PROJECT/REPOSITORY/IMAGE:TAG \ --format 'value(image_summary.digest)'
替换以下内容:
LOCATION
:代码库的区域或多区域位置PROJECT
:您的 Google Cloud 项目 IDREPOSITORY
:您的代码库名称IMAGE
:您的映像名称TAG
:您的映像标记
Container Registry
对于存储在 Container Registry 中的映像,您可以使用
gcloud container images describe
命令获取映像的摘要,方法是提供名称和标记。使用--format
标志只显示摘要:gcloud container images describe \ gcr.io/google-containers/pause-amd64:3.2 \ --format 'value(image_summary.digest)'
尽管您的摘要值可能有所不同,但输出类似于以下内容:
sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
Cloud Build
对于使用 Cloud Build 构建的映像,您可以结合使用 gcloud builds describe
命令和 --format
标志来获取映像摘要。无论您使用哪个注册表来发布映像,此方法都适用。
对于已经完成的构建,请执行以下操作:
获取项目的构建列表:
gcloud builds list
记下
BUILD_ID
。获取映像摘要:
gcloud builds describe BUILD_ID \ --format 'value(results.images[0].digest)'
将
BUILD_ID
替换为 Cloud Build 分配给构建的唯一 ID。
针对当前项目从 Cloud Build 获取最新构建的映像名称和摘要:
gcloud builds describe \ $(gcloud builds list --limit 1 --format 'value(id)') \ --format 'value[separator="@"](results.images[0].name,results.images[0].digest)'
如果您的构建生成了多张映像,请过滤输出并获取其中一个映像的摘要:
gcloud builds describe BUILD_ID --format json \ | jq -r '.results.images[] | select(.name=="YOUR_IMAGE_NAME") | .digest'
将
YOUR_IMAGE_NAME
替换为cloudbuild.yaml
文件中其中一个映像的名称。如果您使用
gcloud builds submit
命令向 Cloud Build 提交构建,则可以从环境变量的输出中捕获映像摘要:IMAGE_DIGEST=$(gcloud builds submit \ --format 'value(results.images[0].digest)' | tail -n1)
Cloud Native Buildpacks
如果您使用 Cloud Native Buildpack 和 Google Cloud 构建器来构建和发布映像,则可以将
--quiet
标志与pack
命令结合使用来捕获映像名称和摘要。pack build --builder gcr.io/buildpacks/builder:v1 --publish --quiet \ LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE \ > image-with-digest.txt
替换以下内容:
LOCATION
:代码库的区域或多区域位置PROJECT_ID
:您的 Google Cloud 项目 IDREPOSITORY
:您的代码库名称IMAGE
:您的映像名称
文件
image-with-digest.txt
包含映像名称和摘要。如果要为映像添加标记,请使用
--tag
标志。
Docker 客户端
docker
命令行客户端的manifest
子命令可以从容器映像注册表中提取映像清单和清单列表。从映像
registry.k8s.io/pause:3.9
的清单列表获取摘要(针对amd64
CPU 架构和linux
操作系统):docker manifest inspect --verbose registry.k8s.io/pause:3.9 | \ jq -r 'if type=="object" then .Descriptor.digest else .[] | select(.Descriptor.platform.architecture=="amd64" and .Descriptor.platform.os=="linux") | .Descriptor.digest end'
输出类似于以下内容:
sha256:8d4106c88ec0bd28001e34c975d65175d994072d65341f62a8ab0754b0fafe10
对于存储在本地 Docker 守护程序中的映像,以及从映像注册表拉取或推送到映像注册表的映像,您可以使用 Docker 命令行工具来获取映像摘要:
将映像拉取到本地 Docker 守护程序:
docker pull docker.io/library/debian:bookworm
获取映像摘要:
docker inspect docker.io/library/debian:bookworm \ | jq -r '.[0].RepoDigests[0]' \ | cut -d'@' -f2
尽管您的摘要值可能有所不同,但输出类似于以下内容:
sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b
列出本地 Docker 守护程序中的所有映像和摘要:
docker images --digests
输出会显示具有摘要值的映像的摘要。从映像注册表拉取映像或将映像推送到映像注册表时,映像只有摘要值。
crane
和 gcrane
您可以使用开源 crane
和 gcrane
命令行工具来获取映像摘要,而无需将映像拉取到本地 Docker 守护程序。
将
crane
和gcrane
下载到当前目录:VERSION=$(curl -sL https://api.github.com/repos/google/go-containerregistry/releases/latest | jq -r .tag_name) curl -L "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_$(uname -s)_$(uname -m).tar.gz" | tar -zxf - crane gcrane
获取映像摘要:
./gcrane digest gcr.io/distroless/static-debian11:nonroot
强制在 Kubernetes 部署中使用映像摘要
如果要对部署到 Kubernetes 集群的映像强制执行摘要,可以使用 Policy Controller 或 Open Policy Agent (OPA) Gatekeeper。Policy Controller 是基于 OPA Gatekeeper 开源项目构建的。
Policy Controller 和 OPA Gatekeeper 都是在 OPA 政策引擎的基础上构建的。Policy Controller 和 OPA Gatekeeper 提供 Kubernetes 验证准许网络钩子强制执行政策,并且自定义资源定义 (CRD) 适用于限制条件模板和限制条件。
限制条件模板包含一种使用名为 Rego 的高级声明语言来表示的政策逻辑。以下限制条件模板可验证 Kubernetes 资源规范中的容器、init 容器和临时容器是否使用含摘要的映像:
上述政策包含一个正则表达式作为 re_match
函数的输入。此正则表达式与容器映像摘要一致,并且基于 Open Container Initiative 映像规范中的摘要格式。
限制条件会通过与 kind
和 namespace
等特性进行匹配来将政策应用于 Kubernetes 资源。以下示例限制条件将限制条件模板中的政策应用于 default
命名空间中的所有 Pod
资源。
创建限制条件模板和限制条件后,default
命名空间中的任何新 pod 都必须使用映像摘要来引用容器映像。
如需查看完整示例,请参阅 Gatekeeper 政策库中的 imagedigests
政策。
关于映像清单、摘要和标记
在本部分中,您将了解如何使用 curl
和 docker
等命令行工具探索注册表中的现有映像。在 Cloud Shell 或已安装 gcloud CLI、Docker、cURL 和 jq
等工具的 shell 环境中运行命令。以下命令使用 Artifact Registry 中的公共映像。
使用 cURL 和清单网址获取映像
gcr.io/google-containers/pause-amd64:3.2
的清单:curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2
输出内容类似如下:
{ "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { "mediaType": "application/vnd.docker.container.image.v1+json", "size": 759, "digest": "sha256:80d28bedfe5dec59da9ebf8e6260224ac9008ab5c11dbbe16ee3ba3e4439ac2c" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 296534, "digest": "sha256:c74f8866df097496217c9f15efe8f8d3db05d19d678a02d01cc7eaed520bb136" } ] }
config
部分有一个摘要特性,您可以使用此值来检索配置对象。同样,每个层都有一个digest
特性,您可以使用该特性检索该层的 tar 文件。如果映像包含可选的映像索引,则使用标记发送到清单网址的 HTTP
GET
请求会返回映像索引,而不是映像清单。获取映像
gcr.io/google-containers/pause:3.2
的映像索引:curl -s https://gcr.io/v2/google-containers/pause/manifests/3.2
输出类似于以下内容:
{ "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "manifests": [ { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 526, "digest": "sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108", "platform": { "architecture": "amd64", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 526, "digest": "sha256:bbb7780ca6592cfc98e601f2a5e94bbf748a232f9116518643905aa30fc01642", "platform": { "architecture": "arm", "os": "linux", "variant": "v7" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 526, "digest": "sha256:31d3efd12022ffeffb3146bc10ae8beb890c80ed2f07363515580add7ed47636", "platform": { "architecture": "arm64", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 526, "digest": "sha256:7f82fecd72730a6aeb70713476fb6f7545ed1bbf32cadd7414a77d25e235aaca", "platform": { "architecture": "ppc64le", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 526, "digest": "sha256:1175fd4d728641115e2802be80abab108b8d9306442ce35425a4e8707ca60521", "platform": { "architecture": "s390x", "os": "linux" } } ] }
对结果进行过滤,以提取所需平台的映像摘要。获取
amd64
CPU 架构和linux
操作系统的映像清单的摘要:curl -s https://gcr.io/v2/google-containers/pause/manifests/3.2 | \ jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest'
此命令中的过滤操作会模拟容器运行时(例如 containerd)如何从映像索引中选择与目标平台匹配的映像。
输出类似于以下内容:
sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
获取映像
gcr.io/google-containers/pause-amd64:3.2
的摘要:curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \ | shasum -a 256 \ | cut -d' ' -f1
输出内容类似如下:
4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
您可以使用映像摘要值来引用此映像,如下所示:
gcr.io/google-containers/pause-amd64@sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
使用内容可寻址的存储概念,将摘要用作参考,以获取映像清单:
curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
许多容器映像注册表会在
Docker-Content-Digest
标头中返回清单摘要、映像索引、配置对象和文件系统层,以响应 HTTPHEAD
请求。获取映像gcr.io/google-containers/pause-amd64:3.2
的映像索引的摘要:curl -s --head https://gcr.io/v2/google-containers/pause/manifests/3.2 \ | grep -i Docker-Content-Digest \ | cut -d' ' -f2
输出类似于以下内容:
sha256:927d98197ec1141a368550822d18fa1c60bdae27b78b0c004f705f548c07814f
Open Container Initiative Distribution 规范并未规定
Docker-Content-Digest
标头,因此此方法可能并不适用于所有容器映像注册表。您可以将其与 Artifact Registry 和 Container Registry 搭配使用。如需使用映像清单中的摘要值检索映像配置对象,请执行以下操作:
获取配置摘要:
CONFIG_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \ | jq -r '.config.digest')
使用配置摘要来检索配置对象,并使用
jq
设置输出格式,使其更易于阅读:curl -sL https://gcr.io/v2/google-containers/pause-amd64/blobs/$CONFIG_DIGEST \ | jq
输出内容类似如下:
{ "architecture": "amd64", "config": { "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Entrypoint": [ "/pause" ], "WorkingDir": "/", "OnBuild": null }, "created": "2020-02-14T10:51:50.60182885-08:00", "history": [ { "created": "2020-02-14T10:51:50.60182885-08:00", "created_by": "ARG ARCH", "comment": "buildkit.dockerfile.v0", "empty_layer": true }, { "created": "2020-02-14T10:51:50.60182885-08:00", "created_by": "ADD bin/pause-amd64 /pause # buildkit", "comment": "buildkit.dockerfile.v0" }, { "created": "2020-02-14T10:51:50.60182885-08:00", "created_by": "ENTRYPOINT [\"/pause\"]", "comment": "buildkit.dockerfile.v0", "empty_layer": true } ], "os": "linux", "rootfs": { "type": "layers", "diff_ids": [ "sha256:ba0dae6243cc9fa2890df40a625721fdbea5c94ca6da897acdd814d710149770" ] } }
如需使用映像清单中的摘要值检索文件系统层,请执行以下操作:
获取要检索的层的摘要:
LAYER_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \ | jq -r '.layers[0].digest')
使用层摘要来检索层 tar 文件,并列出内容:
curl -sL https://gcr.io/v2/google-containers/pause-amd64/blobs/$LAYER_DIGEST \ | tar --list
此层只有一个文件,名为
pause
。
如需查找与映像摘要关联的标记,请执行以下操作:
定义您想查找的摘要:
IMAGE_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \ | shasum -a 256 \ | cut -d' ' -f1)
IMAGE_DIGEST
环境变量包含标记3.2
所引用映像的摘要。使用映像标记列表端点
/tags/list
列出标记信息,并提取摘要值对应的标记:curl -s "https://gcr.io/v2/google-containers/pause-amd64/tags/list?n=1" \ | jq ".manifest.\"sha256:$IMAGE_DIGEST\".tag"
输出类似于以下内容:
[ "3.2" ]
如需使用 cURL 从 Artifact Registry 容器映像代码库获取映像的清单,请在
Authorization
请求标头中添加访问令牌:curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \ https://LOCATION-docker.pkg.dev/v2/PROJECT_ID/REPOSITORY/IMAGE/manifests/DIGEST
替换以下内容:
LOCATION
:代码库的区域或多区域位置PROJECT_ID
:您的 Google Cloud 项目 IDREPOSITORY
:您的代码库名称IMAGE
:您的映像名称DIGEST
:映像摘要,格式为sha256:DIGEST_VALUE