1. 概览
Artifact Registry 是一项全托管式软件包管理器,可提供统一的工具来管理您的 OCI 容器映像和语言软件包(如 Maven 和 npm)。
Artifact Registry 已与各种其他 Google Cloud 服务完全集成,例如:
- Cloud Build 可以直接将映像制品上传到 Artifact Registry。
- Cloud Deploy 可以将 Artifact Registry 映像直接部署到各种运行时环境。
- 它为 Cloud Run 和 GKE 等容器运行时提供映像,并支持映像流式传输等高级性能优化功能。
- Artifact Registry 可作为 Artifact Analysis 的检测点,用于持续监控已知漏洞。
- Cloud IAM 可对制品访问权限和许可进行精细且一致的控制。
本实验将以实操教程的形式引导您了解其中的许多功能。
学习内容
本实验的学习目标是什么?
- 为容器和语言软件包创建不同的代码库
- 使用 Artifact Registry 创建和使用容器映像
- 使用 Artifact Registry 分析工件的安全状况和内容
- 为 Java Maven 依赖项配置和使用 Artifact Registry
2. 设置和要求
自定进度的环境设置
- 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。



- 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
- 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用
PROJECT_ID标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。 - 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
- 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除项目。Google Cloud 新用户符合参与 300 美元免费试用计划的条件。
设置 gcloud
在 Cloud Shell 中,设置您的项目 ID 和项目编号。将它们保存为 PROJECT_ID 和 PROJECT_NUMBER 变量。
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
启用 Google 服务
gcloud services enable \
cloudresourcemanager.googleapis.com \
run.googleapis.com \
artifactregistry.googleapis.com \
containerregistry.googleapis.com \
containerscanning.googleapis.com \
binaryauthorization.googleapis.com \
cloudbuild.googleapis.com
获取源代码
本实验的源代码位于 GitHub 上的 GoogleCloudPlatform 组织中。使用以下命令克隆该代码库。
git clone https://github.com/GoogleCloudPlatform/java-docs-samples
3. 推送容器映像
在 Artifact Registry 上创建 Docker 仓库
如前所述,Artifact Registry 支持不同的制品库格式,可让您管理容器映像和语言包。与不同类型的制品库的互动在规范中定义,并且是广泛采用的标准。例如,Maven 依赖项的请求与 Node 依赖项的请求不同。
为了支持特定的制品 API 规范,Artifact Registry 需要在相应的代码库类型中管理制品。创建新代码库时,您需要传入 --repository-format 标志来指明代码库类型。
如需为 Docker 映像创建第一个代码库,请在 Cloud Shell 中运行以下命令:
gcloud artifacts repositories create container-example --repository-format=docker \
--location=us-central1 --description="Example Docker repository"
如果出现 Cloud Shell 授权提示,请点击“授权”
前往 Google Cloud 控制台 - Artifact Registry - 代码库,您会看到新创建的 Docker 代码库 container-example。点击该代码库,您会发现目前是空的

配置 Docker 以向 Artifact Registry 进行身份验证
连接到 Artifact Registry 时,需要提供凭据才能授予访问权限。Docker 可以配置为无缝使用您的 gcloud 凭据,而无需设置单独的凭据。
在 Cloud Shell 中运行以下命令,将 Docker 配置为使用 Google Cloud CLI 对向 us-central1 区域中的 Artifact Registry 发出的请求进行身份验证,
gcloud auth configure-docker us-central1-docker.pkg.dev
如果该命令会提示您确认是否更改 Cloud Shell Docker 配置,请按 Enter 键。
探索示例应用
您在之前的步骤中克隆的 Git 代码库中提供了一个示例应用。切换到 java 目录并查看应用代码。
cd java-docs-samples/run/helloworld/
ls
该文件夹包含一个示例 Java 应用,用于渲染一个简单的网页:除了与本实验无关的各种文件外,它还包含 src 文件夹下的源代码,以及我们将用于构建容器映像的 Dockerfile。
构建容器映像
在 Artifact Registry 中存储容器映像之前,您需要先创建一个。
运行以下命令来构建容器映像并使用完整的制品注册表路径对其进行标记:
docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/container-example/java-hello-world:tag1 .
将容器映像推送到 Artifact Registry
运行以下命令,将容器映像推送到之前创建的代码库:
docker push us-central1-docker.pkg.dev/$PROJECT_ID/container-example/java-hello-world:tag1
查看 Artifact Registry 中的映像
前往 Google Cloud 控制台 - Artifact Registry - 代码库. 打开 container-example 代码库,并验证 java-hello-world 映像是否位于其中。

点击映像,并注意标记为 tag1 的映像。由于我们已通过 containerscanning.googleapis.com 服务启用映像的自动扫描,因此您可以看到漏洞扫描正在运行或已完成。完成后,您可以看到检测到的此映像修订版本的漏洞数量。我们将在下一部分中探讨漏洞和其他制品洞见。

4. 检查容器映像
现在,您已将第一个映像推送到 container-example 代码库,接下来可以更详细地了解该映像。在“版本”标签页中,点击我们刚刚创建的版本。如需更详细地显示图片,请执行以下操作:

上面的复制按钮特别有用,因为它提供了一种简单的方法来访问相应映像版本的完全限定映像 URI,包括 SHA 哈希。然后,此 URI 可用于拉取特定映像版本,或作为 Kubernetes Deployment 或 Cloud Run Service 中的映像引用。如需测试映像 URI,您可以在 Cloud Shell 中运行以下命令:
docker pull $IMAGE_URI
了解依赖项
接下来,前往顶部的“依赖项”标签页,您会看到映像中检测到的所有依赖项。请注意,它会列出语言依赖项和操作系统级别的依赖项。您还可以查看附加到每个依赖项的软件许可。

仔细查看,您会发现 SBOM 信息尚未填充。如需为制品填充 SOM,您可以在 Cloud Shell 中运行以下命令,并使用可从顶部面包屑栏复制的完全限定映像 URI。
gcloud artifacts sbom export --uri $IMAGE_URI
刷新页面后,您会看到一个链接,指向存储在 Cloud Storage 中的自动生成的 SBOM。如果您依赖于映像的 SBOM,则可能需要自动为制品生成 SBOM,并将生成过程纳入 CI/CD 流水线。
探索映像漏洞
点击视图顶部的“漏洞”标签页,即可查看映像中检测到的所有漏洞。除了顶部的漏洞摘要,您还可以在底部的表格中查看完整的漏洞列表。每一行都链接到 CVE 公告,其中指明了严重程度和来源软件包。对于有修复程序的漏洞,它还会提供有关如何更新依赖项以修复漏洞的说明。

5. 虚拟制品库和远程制品库
在上一部分中,我们使用单个映像代码库来推送和拉取映像。这种方法非常适合小规模使用情形,但对于需要自主管理其代码库的不同团队组成的大型组织来说,会带来挑战。团队或业务部门通常会拥有自己的映像代码库,并具有自己的权限和配置。为了简化这些代码库中映像的使用,并屏蔽消费者对底层组织结构的了解,Artifact Registry 提供了虚拟代码库,可用于汇总多个底层代码库中的资源。潜在的架构可能如下所示:

Docker Hub 的远程代码库
Docker Hub 是一个热门的公共映像注册表,其中托管着许多开源容器映像。虽然直接使用公共代码库很简单,但在生产环境中会遇到许多挑战,而 Artifact Registry 中的远程代码库可以帮助我们克服这些挑战。
借助远程制品库,您可以将请求代理到上游注册表,并在代理过程中缓存映像。这不仅可以缩短图片的下载时间,还可以消除对外部服务正常运行时间的依赖,并让您能够应用与您自己的图片相同的安全和访问政策。
如需为 Docker Hub 创建远程代码库,您可以在 Cloud Shell 中运行以下命令:
gcloud artifacts repositories create dockerhub \
--repository-format=docker \
--mode=remote-repository \
--remote-docker-repo=docker-hub \
--location=us-central1 \
--description="Example Remote Repo for Docker Hub"
现在,您应该会在 Artifact Registry 代码库列表中看到一个额外的代码库:

如需测试您的远程代码库是否能够将请求代理到远程代码库,请在 Cloud Shell 中运行以下命令来拉取 nginx 映像:
docker pull us-central1-docker.pkg.dev/$PROJECT_ID/dockerhub/nginx:stable-alpine
拉取成功后,您还可以在 Cloud 控制台中查看代码库,并确认缓存的 Nginx 映像现在提供的依赖项和漏洞报告与您自行构建的映像相同。
创建虚拟制品库
按照我们目前使用的流程,您可以为每个团队或业务创建多个代码库,并明确定义每个代码库的所有权和 IAM 权限。我们还可以为远程制品库创建代理,从而更轻松、更安全地使用第三方映像。如果您从这些映像的消费者的角度来看,就会发现大量代码库的缺点。开发者如何知道应在部署中使用哪个映像代码库?
为了简化使用并隐藏抽象层后面的底层代码库,您可以在 Cloud Shell 中使用以下命令在 Artifact Registry 中创建虚拟代码库:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com \
--role roles/artifactregistry.reader
cat <<EOF > /tmp/upstream.json
[{
"id" : "hello-repo",
"repository" : "projects/$PROJECT_ID/locations/us-central1/repositories/container-example",
"priority" : 100
},{
"id" : "dockerhub",
"repository" : "projects/$PROJECT_ID/locations/us-central1/repositories/dockerhub",
"priority" : 101
}]
EOF
gcloud artifacts repositories create all-images \
--repository-format=docker \
--mode=virtual-repository \
--location=us-central1 \
--upstream-policy-file=/tmp/upstream.json \
--description="Example Virtual Repo"
现在,我们可以从虚拟代码库中拉取映像,而无需公开底层结构。如需验证一切是否正常运行,您可以在 Cloud Shell 中运行以下命令:
docker pull us-central1-docker.pkg.dev/$PROJECT_ID/all-images/java-hello-world:tag1
docker pull us-central1-docker.pkg.dev/$PROJECT_ID/all-images/nginx:stable-alpine
6. 部署到 Cloud Run
有了相应的代码库和映像后,我们现在就可以在部署中使用它们了。为了说明一个使用情形示例并避免部署额外的基础设施,我们将容器部署到 Cloud Run。最简单的部署方法是在 Cloud Shell 中运行以下命令:
gcloud run deploy hello-world \
--image us-central1-docker.pkg.dev/$PROJECT_ID/all-images/java-hello-world:tag1 \
--region us-central1 \
--allow-unauthenticated
部署完成后,系统会自动生成一个网址,您可以通过该网址访问您的服务。
Deploying container to Cloud Run service [hello-world] in project [my-project] region [us-central1] OK Deploying... Done. OK Creating Revision... OK Routing traffic... OK Setting IAM Policy... Done. Service [hello-world] revision [hello-world-00001-wtc] has been deployed and is serving 100 percent of traffic. Service URL: https://hello-world-13746229022.us-central1.run.app
如果您在新浏览器标签页中打开该网址,应该会看到“Hello World”成功消息。

7. 加强供应链安全性
现在,您的容器映像已成功部署到正式版,接下来,我们不妨了解一下如何加强端到端供应链。在上一部分中,我们了解了 Artifact Registry 的容器分析功能如何提供有关映像中所用库和许可的深入分析。不过,恶意攻击者仍有可能在供应链中将有害内容引入您的映像。在本部分中,我们将探讨如何使用 SLSA 框架为 build 制品引入证明,甚至利用制品本身的加密签名来确保只有可信的制品才能部署到 Cloud Run 运行时。
使用 Cloud Build 进行 SLSA 证明
SLSA 框架为供应链制品提供不同级别的证据。规范和实现乍一看可能令人望而却步,但使用 Cloud Build 创建 SLSA 证明非常简单,只需添加一个将 requestedVerifyOption 设置为 VERIFIED 的 cloudbuild.yaml 规范即可。
对于我们的应用,我们可以在 Cloud Shell 中运行以下命令,在 helloworld 文件夹中创建 cloudbuild.yaml 文件。
cat <<EOF > cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '\$_IMAGE_URI', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '\$_IMAGE_URI']
images:
- '\$_IMAGE_URI'
options:
requestedVerifyOption: VERIFIED
substitutions:
_IMAGE_URI: us-central1-docker.pkg.dev/$PROJECT_ID/container-example/java-hello-world:latest
EOF
现在,我们在 Cloud Shell 中运行以下命令,以创建一个新的 Cloud Build 作业,该作业会构建新版本的 Java Hello World 映像。
gcloud builds submit --substitutions=_IMAGE_URI=us-central1-docker.pkg.dev/$PROJECT_ID/container-example/java-hello-world:cloud-build
构建完成后,我们可以前往 Google Cloud 控制台中的 Cloud Build 界面,打开我们的 build,然后在“Build 摘要”下查看“Build 制品”,从而查看我们达到的 SLSA 级别。您可以在该图片中看到一个用于查看“安全洞见”的按钮。点击该工件后,您会看到 SLSA 证明,以及之前在 Artifact Registry 界面中推送本地 build 时看到的熟悉漏洞报告。

您还可以在 Cloud Shell 中运行以下命令,检索映像的 SLSA 出处:
gcloud artifacts docker images describe \
"us-central1-docker.pkg.dev/$PROJECT_ID/container-example/java-hello-world:cloud-build" \
--show-provenance
要求使用 Binary Authorization 时提供 Cloud Build 出处信息
有了 Cloud Build 流水线,如果能确保我们部署到生产环境的所有映像都是使用该可编程且可重现的构建环境构建的,那岂不是很好?
这时,Binary Authorization 就能派上用场了。它允许您在容器运行时前面放置一个守门人,用于检查容器映像并验证是否存在可信的证明。如果未找到任何证明,系统会根据配置创建审核日志条目或完全阻止部署。
如需将项目的默认 Binary Authorization 配置更改为要求使用 Cloud Run 签发的内置证明,请在 Cloud Shell 中运行以下命令:
cat << EOF > /tmp/policy.yaml
defaultAdmissionRule:
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
evaluationMode: REQUIRE_ATTESTATION
requireAttestationsBy:
- projects/$PROJECT_ID/attestors/built-by-cloud-build
name: projects/$PROJECT_ID/policy
EOF
gcloud container binauthz policy import /tmp/policy.yaml
当然,您也可以在此处添加自己的自定义证明者,但这超出了本 Codelab 的范围,您可以将其作为可选的课外作业练习。
如需在 Cloud Run 服务上强制执行 Binary Authorization,请在 Cloud Shell 中运行以下命令:
gcloud run services update hello-world \
--region us-central1 \
--binary-authorization=default
我们先重新部署本地构建的映像,测试 Binary Authorization 是否已正确应用
gcloud run deploy hello-world \
--image us-central1-docker.pkg.dev/$PROJECT_ID/all-images/java-hello-world:tag1 \
--region us-central1
您应该会收到一条错误消息,说明映像无法部署的原因,该消息如下所示:
Image us-central1-docker.pkg.dev/my-project/all-images/java-hello-world@sha256:71eebbf04bf7d1d023e5de5e18f786ea3b8b6411bf64c8def3301c71baca0518 denied by attestor projects/my-project/attestors/built-by-cloud-build: No attestations found that were valid and signed by a key trusted by the attestor
因此,若要将新版本部署到 Cloud Run 服务,我们需要提供使用 Cloud Build 构建的映像。
gcloud run deploy hello-world \
--image us-central1-docker.pkg.dev/$PROJECT_ID/all-images/java-hello-world:cloud-build \
--region us-central1
这次部署应该会成功,并显示类似如下内容的成功部署消息:
Deploying container to Cloud Run service [hello-world] in project [my-project] region [us-central1] OK Deploying... Done. OK Creating Revision... OK Routing traffic... Done. Service [hello-world] revision [hello-world-00005-mq4] has been deployed and is serving 100 percent of traffic. Service URL: https://hello-world-13746229022.us-central1.run.app
8. 管理 Java Maven 语言软件包
在本部分中,您将了解如何设置 Artifact Registry Java 代码库并向其中上传软件包,然后在不同的应用中利用这些软件包。
创建 Maven 软件包代码库
在 Cloud Shell 中运行以下命令,为 Java 工件创建一个仓库:
gcloud artifacts repositories create java-repo \
--repository-format=maven \
--location=us-central1 \
--description="Example Java Maven Repo"
如果出现 Cloud Shell 授权提示,请点击“授权”
前往 Google Cloud 控制台 - Artifact Registry - 代码库,您会看到新创建的 Maven 制品库 java-repo。点击该制品库,您会发现目前是空的。
设置对 Artifact Repository 的身份验证
使用以下命令,将应用默认凭据 (ADC) 的已知位置更新为您的用户账号凭据,以便 Artifact Registry 凭据帮助程序在连接到代码库时可以使用这些凭据进行身份验证:
gcloud auth login --update-adc
为 Artifact Registry 配置 Maven
运行以下命令,输出要添加到 Java 项目的制品库配置。
gcloud artifacts print-settings mvn \
--repository=java-repo \
--location=us-central1 | tee config.xml
在 Cloud Shell 中,从 helloworld 目录内运行以下命令,即可在 Cloud Shell Editor 中打开 pom.xml:
cloudshell edit pom.xml
并将返回的设置添加到文件中的相应部分,
更新 distributionManagement 部分
<distributionManagement>
<snapshotRepository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
</snapshotRepository>
<repository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
</repository>
</distributionManagement>
更新代码库部分
<repositories>
<repository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
更新构建下的扩展程序部分
<extensions>
<extension>
<groupId>com.google.cloud.artifactregistry</groupId>
<artifactId>artifactregistry-maven-wagon</artifactId>
<version>2.1.0</version>
</extension>
</extensions>
以下是完整文件的示例,供您参考。请务必将 <PROJECT> 替换为您的项目 ID。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.run</groupId>
<artifactId>helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.google.cloud.samples</groupId>
<artifactId>shared-configuration</artifactId>
<version>1.2.0</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<spring-boot.version>3.2.2</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- [START Artifact Registry Config] -->
<distributionManagement>
<snapshotRepository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
</snapshotRepository>
<repository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
</repository>
</distributionManagement>
<repositories>
<repository>
<id>artifact-registry</id>
<url>artifactregistry://us-central1-maven.pkg.dev/<PROJECT>/java-repo</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<extensions>
<extension>
<groupId>com.google.cloud.artifactregistry</groupId>
<artifactId>artifactregistry-maven-wagon</artifactId>
<version>2.2.0</version>
</extension>
</extensions>
<!-- [END Artifact Registry Config] -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<to>
<image>gcr.io/PROJECT_ID/helloworld</image>
</to>
</configuration>
</plugin>
</plugins>
</build>
</project>
将 Java 软件包上传到 Artifact Registry
在 Maven 中配置 Artifact Registry 后,您现在可以使用 Artifact Registry 存储 Java Jar,供组织中的其他项目使用。
运行以下命令,将 Java 软件包上传到 Artifact Registry:
mvn deploy
检查 Artifact Registry 中的 Java 软件包
前往 Cloud 控制台 - Artifact Registry - 制品库,点击进入 java-repo,检查 helloworld 二进制制品是否已存在:

9. 恭喜!
恭喜,您已完成此 Codelab!
所学内容
- 为容器和语言软件包创建了制品库
- 使用 Artifact Registry 管理容器映像
- 将 Artifact Registry 与 Cloud Code 集成
- 配置了 Maven 以使用 Artifact Registry 获取 Java 依赖项
清理
在 Cloud Shell 中运行以下命令以删除整个项目
gcloud projects delete $PROJECT_ID