本教程向将容器部署到 Kubernetes 的开发者和运维人员介绍了如何使用容器映像摘要来识别容器映像。容器映像摘要是唯一且不可改变地标识容器映像。
与使用映像标记相比,使用映像摘要部署容器映像有诸多好处。如需详细了解映像摘要,请参阅使用容器映像摘要的随附文档,然后再继续本教程。
Kubernetes pod 规范中的容器的 image 参数接受包含摘要的映像。此参数适用于所有使用 Pod 规范的位置,例如 Deployment、StatefulSet、DaemonSet、ReplicaSet、CronJob 和 Job 资源的 template 部分。
如需使用摘要部署映像,请使用映像名称,后跟 @sha256: 和摘要值。以下是使用包含摘要的映像的 Deployment 资源示例。Deployment 是一个 Kubernetes API 对象,可让您运行在集群节点中分布的多个 Pod 副本。
apiVersion: apps/v1 kind: Deployment metadata: name: echo-deployment spec: selector: matchLabels: app: echo template: metadata: labels: app: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 ports: - containerPort: 8080
使用映像摘要的一个缺点是,在将映像发布到注册表之前,您不知道摘要值。构建新映像时,摘要值会发生变化,您需要在每次部署时更新 Kubernetes 清单。
本教程介绍了如何使用 Skaffold、kpt、digester、kustomize、gke-deploy 和 ko 等工具在清单中使用映像摘要。
建议
本文档介绍了几种在 Kubernetes 部署中使用映像摘要的方法。本文档中介绍的工具互为补充。例如,您可以结合使用 kpt 函数的输出和 kustomize 来为不同环境创建变体。Skaffold 可以使用 ko 构建映像,并使用 kubectl 或 kpt 将映像部署到 Kubernetes 集群。
这些工具互为补充的原因是它们根据 Kubernetes 资源模型 (KRM) 执行结构化修改。此模型使工具成为可插入工具,您可以改进这些工具的用途,创建可帮助您部署应用和服务的流程和流水线。
首先,我们建议您使用最适合您的现有工具和流程的方法:
- Skaffold 可以向映像引用添加摘要。您只需进行细微的配置更改即可启用此功能。采用 Skaffold 具有额外优势,例如总结不同的工具构建和部署容器映像的方式。 
- 通过使用 digester 工具作为 Kubernetes 集群中的可变准入网络钩子,您可以向所有部署添加摘要,同时将对构建和部署容器映像的当前流程的影响降至最低。Digester 网络钩子也简化了 Binary Authorization 的采用,因为它只需要将标签添加到命名空间。 
- 如果您需要灵活的工具来处理 Kubernetes 清单,kpt 不失为一种很不错的选择。Digester 工具可以在 kpt 流水线中用作客户端 KRM 函数。 
- 如果您已使用 kustomize 跨环境管理 Kubernetes 清单,我们建议您利用其映像转换器以摘要的形式部署映像。 
如果您没有使用本文档中所述的任何工具,我们建议您从 Skaffold 和 digester 网络钩子开始。Skaffold 是开发者和发布团队使用的常用工具,与本教程中所述的其他工具集成。随着需求的变化,您可以利用这些集成选项。Digester Kubernetes webhook 通过为整个集群启用基于摘要的部署来补充 Skaffold。
使用 Skaffold
Skaffold 是一种命令行工具,用于持续开发应用并将应用持续部署到 Kubernetes 集群。
使用 Skaffold 构建映像,将映像推送到 Artifact Registry,然后将 Kubernetes 清单模板中的 image 占位值替换为推送映像的名称、标记和摘要:
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/skaffold cd ~/container-image-digests-tutorial/skaffold
- 克隆 Skaffold Git 代码库: - git clone https://github.com/GoogleContainerTools/skaffold.git
- 转到 - getting-started示例的目录:- cd skaffold/examples/getting-started
- 查看与您的 Skaffold 版本匹配的 Git 标记: - git checkout $(skaffold version)
- 查看 - skaffold.yaml配置文件:- cat skaffold.yaml- 该文件类似于以下内容: - apiVersion: skaffold/v4beta6 kind: Config build: artifacts: - image: skaffold-example manifests: rawYaml: - k8s-pod.yaml - build.artifacts部分包含占位符映像名称。Skaffold 会在输入清单文件中查找此占位符。- manifests部分指示 Skaffold 读取当前目录中名为- k8s-pod.yaml的输入清单。- 如需大致了解所有可用选项,请参阅 - skaffold.yaml参考文档。
- 查看 Kubernetes 清单模板: - cat k8s-pod.yaml- 该文件如下所示: - apiVersion: v1 kind: Pod metadata: name: getting-started spec: containers: - name: getting-started image: skaffold-example - image字段中的- skaffold-example占位值与- skaffold.yaml文件中的- image字段的值匹配。Skaffold 会在渲染的输出中将此占位值替换为完整的映像名称和摘要。
- 构建映像并将其推送到 Artifact Registry: - skaffold build \ --default-repo=LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY \ --file-output=artifacts.json \ --interactive=false \ --push=true \ --update-check=false- 此命令使用以下标志: - --file-output标志指定 Skaffold 用于保存有关构建映像的信息(包括摘要值)的文件。
- --push标志指示 Skaffold 将构建的映像推送到由- --default-repo标志指定的容器映像注册表。
- --interactive和- --update-check标志均设置为- false。在非交互式环境(如构建流水线)中,将这些标志设置为- false;对于本地开发,请将这些标志保留为默认值(两个标志均为- true)。
 - 如果您使用 Cloud Deploy 部署到 GKE,请在创建版本时使用 - --file-output标志中的文件作为- --build-artifacts标志的值。
- 使用上一步中的容器映像的名称、标记和摘要渲染展开的 Kubernetes 清单: - skaffold render \ --build-artifacts=artifacts.json \ --digest-source=none \ --interactive=false \ --offline=true \ --output=rendered.yaml \ --update-check=false- 此命令使用以下标志: - --build-artifacts标志引用上一步中- skaffold build命令的输出文件。
- --digest-source=none标志表示 Skaffold 使用- --build-artifacts标志提供的文件中的摘要值,而不是解析容器映像注册表中的摘要。
- --offline=true标志表示您可以在无需访问 Kubernetes 集群的情况下运行该命令。
- --output标志指定渲染的清单的输出文件。
 
- 查看呈现的清单: - cat rendered.yaml- 输出类似以下内容: - apiVersion: v1 kind: Pod metadata: name: getting-started spec: containers: - image: LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/skaffold-example:TAG@sha256:DIGEST name: getting-started - 在此输出中,您会看到以下值: - TAG:Skaffold 分配给映像的标记。
- DIGEST:映像摘要值
 
使用 digester
Digester 向 Kubernetes Pod 和 Pod 模板规范中的容器和 init 容器映像添加摘要。Digester 会替换使用标记的容器映像引用:
spec:
  containers:
  - image: gcr.io/google-containers/echoserver:1.10
对于使用映像摘要的引用,请注意以下事项:
spec:
  containers:
  - image: gcr.io/google-containers/echoserver:1.10@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229
Digester 可在 Kubernetes 集群中作为变更准入 webhook 运行,也可使用 kpt 或 kustomize 命令行工具作为客户端 KRM 函数运行。
使用 digester KRM 函数
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/digester-fn cd ~/container-image-digests-tutorial/digester-fn
- 下载 digester 二进制文件: - mkdir -p ${HOME}/bin export PATH=${HOME}/bin:${PATH} DIGESTER_VERSION=$(curl -sL https://api.github.com/repos/google/k8s-digester/releases/latest | jq -r .tag_name) curl -L "https://github.com/google/k8s-digester/releases/download/${DIGESTER_VERSION}/digester_$(uname -s)_$(uname -m)" --output ${HOME}/bin/digester chmod +x ${HOME}/bin/digester
- 使用标记 - 1.10创建引用映像- gcr.io/google-containers/echoserver的 Kubernetes Pod 清单:- cat << EOF > pod.yaml apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 ports: - containerPort: 8080 EOF
- 使用 kpt 和当前目录 ( - .) 中的清单运行摘要 KRM 函数:- kpt fn eval . --exec digester- 运行此命令时,kpt 会对当前目录中的清单执行就地更新。如果您希望 kpt 在控制台中显示更新后的清单,并保持清单文件不变,请添加 - --output unwrap标志。
- 查看更新后的清单: - cat pod.yaml- 该文件如下所示: - apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 ports: - containerPort: 8080 
使用 digester 准入网络钩子
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/digester-webhook cd ~/container-image-digests-tutorial/digester-webhook
- 使用 kind 创建本地 Kubernetes 集群: - kind create cluster- kind 是一种命令行工具,用于使用 Docker 运行本地 Kubernetes 集群。 
- 部署 digester 网络钩子: - DIGESTER_VERSION=$(curl -sL https://api.github.com/repos/google/k8s-digester/releases/latest | jq -r .tag_name) kustomize build "https://github.com/google/k8s-digester.git/manifests?ref=${DIGESTER_VERSION}" | kubectl apply -f -
- 在种类集群中创建一个名为 - digester-demo的 Kubernetes 命名空间:- kubectl create namespace digester-demo
- 将 - digest-resolution: enabled标签添加到- digester-demo命名空间:- kubectl label namespace digester-demo digest-resolution=enabled- Digester 网络钩子会将摘要添加到带有此标签的命名空间中的 pod。 
- 使用标记 - 1.10创建引用映像- gcr.io/google-containers/echoserver的 Kubernetes Deployment 清单:- cat << EOF > deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: echo-deployment spec: selector: matchLabels: app: echo template: metadata: labels: app: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 ports: - containerPort: 8080 EOF
- 在 - digester-demo命名空间中应用清单:- kubectl apply --filename deployment.yaml --namespace digester-demo \ --output jsonpath='{.spec.template.spec.containers[].image}{"\n"}'- --output标志指示- kubectl将映像名称输出到控制台,后跟换行符。输出如下所示:- gcr.io/google-containers/echoserver:1.10@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229- 此输出表明 digester 网络钩子将映像摘要添加到了 Deployment 资源中的 Pod 模板规范。 
- 删除种类集群以释放 Cloud Shell 会话中的资源: - kind delete cluster
使用 kpt setter
kpt 是一个命令行工具,用于管理、操作、自定义和应用 Kubernetes 资源清单。
您可以在构建新映像时使用 kpt Functions 目录中的 create-setters 和 apply-setters KRM 函数更新 Kubernetes 清单中的映像摘要。
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/kpt cd ~/container-image-digests-tutorial/kpt
- 在当前目录中创建 kpt 软件包: - kpt pkg init --description "Container image digest tutorial"
- 使用标记 - 1.10创建引用映像- gcr.io/google-containers/echoserver的 Kubernetes Pod 清单:- cat << EOF > pod.yaml apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 ports: - containerPort: 8080 EOF
- 使用 kpt 为清单字段创建一个名为 - echoimage的 setter,现有值为- gcr.io/google-containers/echoserver:1.10:- kpt fn eval . \ --image gcr.io/kpt-fn/create-setters@sha256:0220cc87f29ff9abfa3a3b5643aa50f18d355d5e9dc9e1f518119633ddc4895c \ -- "echoimage=gcr.io/google-containers/echoserver:1.10"
- 查看清单: - cat pod.yaml- 该文件如下所示: - apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 # kpt-set: ${echoimage} ports: - containerPort: 8080 
- 获取容器映像的摘要值: - DIGEST=$(gcloud container images describe \ gcr.io/google-containers/echoserver:1.10 \ --format='value(image_summary.digest)')
- 设置新字段值: - kpt fn eval . \ --image gcr.io/kpt-fn/apply-setters@sha256:4d4295727183396f0c3c6a75d2560254c2f9041a39e95dc1e5beffeb49cc1a12 \ -- "echoimage=gcr.io/google-containers/echoserver:1.10@$DIGEST"- 运行此命令时,kpt 将就地替换清单中的 - image字段值。
- 查看更新后的清单: - cat pod.yaml- 该文件如下所示: - apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 # kpt-set: ${echoimage} ports: - containerPort: 8080 
使用 kustomize 映像转换器
kustomize 是一个命令行工具,可让您使用叠加层、补丁程序和转换器自定义 Kubernetes 清单。
您可以使用 kustomize 映像转换器来更新现有清单中的映像名称、标记和摘要。
以下 kustomization.yaml 代码段介绍如何将映像转换器配置为将转换器 digest 值用于映像,这些映像中的 Pod 规范 image 值与转换器 name 值匹配:
images: - name: gcr.io/google-containers/echoserver digest: sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229
如需使用含有映像摘要的映像转换器,请执行以下操作:
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/kustomize cd ~/container-image-digests-tutorial/kustomize
- 创建 - kustomization.yaml文件:- kustomize init
- 通过使用标记 - 1.10引用映像- gcr.io/google-containers/echoserver的 Pod 规范创建一个 Kubernetes 清单:- cat << EOF > pod.yaml apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 ports: - containerPort: 8080 EOF
- 将清单作为资源添加到 - kustomization.yaml文件中:- kustomize edit add resource pod.yaml
- 使用映像转换器更新映像的摘要: - kustomize edit set image \ gcr.io/google-containers/echoserver@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229
- 在 - kustomization.yaml文件中查看映像转换器:- cat kustomization.yaml- 该文件如下所示: - apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - pod.yaml images: - digest: sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 name: gcr.io/google-containers/echoserver 
- 查看生成的清单: - kustomize build .- 输出如下所示: - apiVersion: v1 kind: Pod metadata: name: echo spec: containers: - image: gcr.io/google-containers/echoserver@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 name: echoserver ports: - containerPort: 8080 
- 如需在一个步骤中运行 kustomize 转换器并将生成的清单应用于 Kubernetes 集群,您可以使用带有 - --kustomize标志的- kubectl apply命令:- kubectl apply --kustomize .- 如果您之后要应用输出,可以将 - kustomize build命令的输出重定向到一个文件。
使用 gke-deploy
gke-deploy 是一个与 Google Kubernetes Engine (GKE) 搭配使用的命令行工具。gke-deploy 封装了 kubectl 命令行工具,并且可以修改您按照 Google 推荐的做法创建的资源。
如果您使用 gke-deploy 子命令 prepare 或 run,gke-deploy 会默认将您的映像标记解析为摘要并使用映像摘要将展开后的清单保存到文件 output/expanded/aggregated-resources.yaml。
您可以使用 gke-deploy run 将映像标记替换为摘要,并将展开的清单应用于 GKE 集群。虽然此命令使用方便,但存在一个缺点:映像标记会在部署时被替换。与标记关联的映像可能会在您决定部署与部署之间发生更改,导致部署异常映像。对于生产部署,我们建议将生成和应用清单步骤分开执行。
如需使用映像摘要替换 Kubernetes 部署清单中的映像标记,请执行以下操作:
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/gke-deploy cd ~/container-image-digests-tutorial/gke-deploy
- 安装 - gke-deploy:- go install github.com/GoogleCloudPlatform/cloud-builders/gke-deploy@latest
- 使用标记 - 1.10创建引用映像- gcr.io/google-containers/echoserver的 Kubernetes Deployment 清单:- cat << EOF > deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: echo-deployment spec: selector: matchLabels: app: echo template: metadata: labels: app: echo spec: containers: - name: echoserver image: gcr.io/google-containers/echoserver:1.10 ports: - containerPort: 8080 EOF
- 根据 - deployment.yaml清单生成展开的清单:- gke-deploy prepare \ --filename deployment.yaml \ --image gcr.io/google-containers/echoserver:1.10 \ --version 1.10
- 查看展开的清单: - cat output/expanded/aggregated-resources.yaml- 输出如下所示: - apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/managed-by: gcp-cloud-build-deploy app.kubernetes.io/version: "1.10" name: echo-deployment namespace: default spec: selector: matchLabels: app: echo template: metadata: labels: app: echo app.kubernetes.io/managed-by: gcp-cloud-build-deploy app.kubernetes.io/version: "1.10" spec: containers: - image: gcr.io/google-containers/echoserver@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 name: echoserver ports: - containerPort: 8080 - 在展开的清单中,映像标记会被替换为摘要。 - 与 - gke-deploy命令结合使用的- --version参数会设置部署中- app.kubernetes.io/version标签的值以及扩展清单的 Pod 模板元数据。- 如需了解如何将 - gke-deploy与 Cloud Build 结合使用,请参阅- gke-deploy的 Cloud Build 文档。
使用 ko
ko 是一种命令行工具和库,用于构建 Go 容器映像并将其部署到 Kubernetes 集群。ko 可以在不使用 Docker 守护程序的情况下构建映像,以便您可以在无法安装 Docker 的环境中使用映像。
ko 子命令 build 会构建映像并将其发布到容器映像注册表或将其加载到本地 Docker 守护程序。
ko 子命令 resolve 会执行以下操作:
- 通过在 Kubernetes 清单的 image字段中查找您使用--filename参数提供的占位符来找到要构建的映像。
- 构建和发布映像。
- 将 image值占位符替换为所构建映像的名称和摘要。
- 输出展开的清单。
ko 子命令 apply、create 和 run 会执行与 resolve 相同的步骤,然后使用展开的清单执行 kubectl apply、create 或 run。
如需通过 Go 源代码构建映像,并将映像摘要添加到 Kubernetes 部署清单,请执行以下操作:
- 在 Cloud Shell 中,创建并转到用于存储您在本部分创建的文件的目录: - mkdir -p ~/container-image-digests-tutorial/ko cd ~/container-image-digests-tutorial/ko
- 下载 - ko并将其添加到您的- PATH:- mkdir -p ${HOME}/bin export PATH=${HOME}/bin:${PATH} KO_VERSION=$(curl -sL https://api.github.com/repos/ko-build/ko/releases/latest | jq -r .tag_name | cut -c2-) curl -L "https://github.com/ko-build/ko/releases/download/v${KO_VERSION}/ko_${KO_VERSION}_$(uname -s)_$(uname -m).tar.gz" | tar -zxC ${HOME}/bin ko
- 在名为 - app的新目录中创建模块名称为- example.com/hello-world的 Go 应用:- mkdir -p app/cmd/ko-example cd app go mod init example.com/hello-world cat << EOF > cmd/ko-example/main.go package main import "fmt" func main() { fmt.Println("hello world") } EOF
- 定义 - ko用于发布映像的映像代码库:- export KO_DOCKER_REPO=LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY- 此示例使用 Artifact Registry,但您可以将 - ko与其他容器映像注册表结合使用。
- 如需为您的应用构建和发布映像,请执行以下步骤之一: - 通过提供 Go 主软件包的路径,为应用构建并发布映像: - ko build --base-import-paths ./cmd/ko-example- 可选参数 - --base-import-paths表示- ko使用主软件包目录的短名称作为映像名称。- ko采用以下格式将映像名称和摘要输出到- stdout:- LOCATION-docker.pkg.dev/PROJECT_ID/ko-example@sha256:DIGEST- 在此输出中, - DIGEST是映像摘要值。
- 使用 - ko将清单占位符替换为其构建并发布的映像的名称和摘要:- 创建 Kubernetes pod 清单。清单使用占位符 - ko://IMPORT_PATH_OF_YOUR_MAIN_PACKAGE作为- image字段的值:- cat << EOF > ko-pod.yaml apiVersion: v1 kind: Pod metadata: name: ko-example spec: containers: - name: hello-world image: ko://example.com/hello-world/cmd/ko-example EOF
- 为应用构建和发布映像,并将清单占位符替换为映像名称和摘要: - ko resolve --base-import-paths --filename ko-pod.yaml- ko会将包含映像名称和清单的摘要输出到- stdout:- apiVersion: v1 kind: Pod metadata: name: ko-example spec: containers: - name: hello-world image: LOCATION-docker.pkg.dev/PROJECT_ID/ko-example@sha256:DIGEST - 在此输出中, - DIGEST是映像摘要值。