使用 Skopeo Copy 提升 Artifact

功能概述

Artifact 提升可将已构建的 artifact 从一个受信任的位置移动到另一个位置,例如从开发镜像仓库移动到暂存或生产仓库。对于容器镜像,请使用 skopeo-copy Task 来复制镜像,而无需重新构建。

当提升需要人工审核时,请在 skopeo-copy 之前添加一个手动批准步骤。批准步骤会暂停 PipelineRun,直到所需批准人完成批准;只有在批准成功后,复制步骤才会开始。

使用场景

  • 将镜像从开发、测试或暂存仓库提升到生产环境。
  • 在一次可审计的提升运行中提升多个相关镜像。
  • 要求发布经理或发布组批准生产提升。
  • 将构建流水线与提升流水线分离,使生产 artifact 复制自不可变的构建输出,而不是重新构建。

前提条件

  • 已安装 Tekton Pipelines
  • skopeo-copy Task 已从已配置的 Hub catalog 中可用。
  • 已部署 Manual Approval Gate,用于本指南中展示的批准工作流。
  • 源和目标 registry 凭证已准备为 Kubernetes Secret 对象,并位于 PipelineRun 运行所在的 namespace 中。
  • 流水线作者可以创建或更新 Pipeline 资源,运行用户可以在目标 namespace 中创建 PipelineRun 资源。
  • 你了解基本的 PipelinePipelineRunTaskWorkspace 概念。

提升流程

典型的镜像提升流水线包含两个阶段:

  1. wait-for-approval:要求指定批准人批准提升。
  2. promote-images:使用 skopeo-copy 复制一个或多个镜像。

将批准策略保留在 Pipeline 定义中。不要将 approversnumberOfApprovalsRequired 或批准规则等批准策略参数作为 PipelineRun 参数暴露出来。如果这些值在运行时可被暴露,流水线执行者就可能覆盖默认批准规则,从而绕过预期的发布控制。

PipelineRun 只应提供每次运行时合法会变化的值,例如源镜像和目标镜像映射。

步骤

1. 准备 registry 凭证

为源 registry 创建一个 secret,为目标 registry 创建另一个 secret。以下示例使用 kubernetes.io/dockerconfigjson secrets:

kubectl create secret docker-registry src-registry-config \
  -n <namespace> \
  --docker-server=dev-registry.example.com \
  --docker-username=<source-username> \
  --docker-password=<source-password>

kubectl create secret docker-registry dst-registry-config \
  -n <namespace> \
  --docker-server=prod-registry.example.com \
  --docker-username=<destination-username> \
  --docker-password=<destination-password>

如果同一组凭证可访问两个 registry,则可以将同一个 secret 绑定到两个 workspace。

2. 创建提升流水线

以下流水线在 Pipeline 中固定批准策略,并且仅将 imageMappingssrcTLSVerifydstTLSVerify 作为运行时输入暴露。请将 release-managergroup:release-approvers 替换为你的 Identity Provider 中的规范用户和组标识符。

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: promote-artifacts-with-approval
spec:
  params:
    - name: imageMappings
      type: array
      description: Source-to-destination image mappings for this promotion run.
    - name: srcTLSVerify
      type: string
      default: "true"
      description: Enable TLS verification for the source registry.
    - name: dstTLSVerify
      type: string
      default: "true"
      description: Enable TLS verification for the destination registry.
  workspaces:
    - name: src-registry-config
    - name: dst-registry-config
  tasks:
    - name: wait-for-approval
      taskRef:
        apiVersion: openshift-pipelines.org/v1alpha1
        kind: ApprovalTask
      timeout: "24h"
      params:
        - name: approvers
          value:
            - release-manager
            - group:release-approvers
        - name: numberOfApprovalsRequired
          value: "1"
        - name: description
          value: "Approve artifact promotion to the production registry."

    - name: promote-images
      runAfter:
        - wait-for-approval
      taskRef:
        resolver: hub
        params:
          - name: type
            value: artifact
          - name: catalog
            value: catalog
          - name: kind
            value: task
          - name: name
            value: skopeo-copy
          - name: version
            value: "0.1"
      params:
        - name: imageMappings
          value:
            - $(params.imageMappings[*])
        - name: srcTLSVerify
          value: $(params.srcTLSVerify)
        - name: dstTLSVerify
          value: $(params.dstTLSVerify)
      workspaces:
        - name: src-registry-config
          workspace: src-registry-config
        - name: dst-registry-config
          workspace: dst-registry-config

imageMappings 参数接受每个数组项一个 SRC DST 映射。每个镜像引用都必须包含 skopeo transport 前缀,例如 docker://。当 imageMappings 不为空时,skopeo-copy 会使用它进行批量提升,并忽略 srcImagedstImages

3. 启动提升运行

创建一个 PipelineRun,并提供需要提升的镜像。对于多个镜像,请将每个源到目标的配对放入 imageMappings 中:

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: promote-artifacts-prod-run
spec:
  pipelineRef:
    name: promote-artifacts-with-approval
  timeouts:
    pipeline: 72h
    tasks: 72h
  params:
    - name: imageMappings
      value:
        - docker://dev-registry.example.com/team/app-api:1.2.3 docker://prod-registry.example.com/team/app-api:1.2.3
        - docker://dev-registry.example.com/team/app-worker:1.2.3 docker://prod-registry.example.com/team/app-worker:1.2.3
    - name: srcTLSVerify
      value: "true"
    - name: dstTLSVerify
      value: "true"
  workspaces:
    - name: src-registry-config
      secret:
        secretName: src-registry-config
    - name: dst-registry-config
      secret:
        secretName: dst-registry-config

timeouts.pipelinetimeouts.tasks 设置得足够长,以覆盖预期的批准窗口。批准任务的超时时间应短于 PipelineRun 的超时时间。在此示例中,批准任务最长可等待 24h,而整个 PipelineRun 最长可运行 72h

另一种方式:将一个镜像提升到多个目标

如果你要将一个源镜像提升到一个或多个目标标签,也可以使用 srcImagedstImages,而不是 imageMappings

tasks:
  - name: promote-image
    taskRef:
      resolver: hub
      params:
        - name: type
          value: artifact
        - name: catalog
          value: catalog
        - name: kind
          value: task
        - name: name
          value: skopeo-copy
        - name: version
          value: "0.1"
    params:
      - name: srcTransport
        value: registry
      - name: srcImage
        value: dev-registry.example.com/team/app-api:1.2.3
      - name: dstTransport
        value: registry
      - name: dstImages
        value:
          - prod-registry.example.com/team/app-api:1.2.3
          - prod-registry.example.com/team/app-api:stable

仅当一个源镜像需要一个或多个目标标签时,才使用这种形式。当一次提升运行包含多个源镜像时,请使用 imageMappings

运行结果

  • PipelineRun 会在 wait-for-approval 处停止,直到所需批准人或组完成批准。
  • 如果批准被拒绝,PipelineRun 会失败,提升复制不会开始。
  • 如果批准成功,skopeo-copy 会将每个映射复制到目标 registry。
  • skopeo-copy 会为成功的 registry 目标输出基于 digest 的结果,包括 IMAGESdst-IMAGE_URLdst-IMAGE_DIGESTdst-image-urlsdst-image-tag

使用以下命令检查运行状态:

kubectl get pipelinerun promote-artifacts-prod-run -n <namespace>

kubectl get approvaltask promote-artifacts-prod-run-wait-for-approval \
  -n <namespace> \
  -o jsonpath='{.status.state}{"\n"}'

PROMOTE_TASKRUN="$(kubectl get taskrun \
  -n <namespace> \
  -l tekton.dev/pipelineRun=promote-artifacts-prod-run,tekton.dev/pipelineTask=promote-images \
  -o jsonpath='{.items[0].metadata.name}')"

kubectl get taskrun "${PROMOTE_TASKRUN}" \
  -n <namespace> \
  -o jsonpath='{range .status.results[*]}{.name}: {.value}{"\n"}{end}'

PROMOTE_POD="$(kubectl get taskrun "${PROMOTE_TASKRUN}" \
  -n <namespace> \
  -o jsonpath='{.status.podName}')"

kubectl logs "${PROMOTE_POD}" -n <namespace> -c step-copy-images

如需查看批准审计详情,请检查生成的 ApprovalTask 上的 status.approversResponse

故障排查

  • 提升在未经过预期 gate 的情况下开始: 确认 skopeo-copy task 设置了 runAfter: [wait-for-approval],或者以其他方式依赖批准任务。
  • 运行用户可以更改批准人:PipelineRun 参数中移除批准策略值,并将其保留在受保护的 Pipeline 定义中。
  • 批准任务始终不出现: 确认已安装并就绪 Manual Approval Gate,然后检查 CustomRunApprovalTask 资源。
  • registry 身份验证失败: 验证源和目标 secret 是否包含 imageMappings 中所用 registry 的凭证。
  • 内部 registry 的 TLS 验证失败: 优先挂载正确的 CA bundle。仅当受信任的内部 registry 有意使用不安全访问或自签名证书时,才将 srcTLSVerifydstTLSVerify 设为 "false"
  • 只有部分映射被复制: 检查 promote-images TaskRun 日志。每个 imageMappings 项都必须恰好包含两个引用:SRC DST

了解更多