如何拉取多架构 Docker 镜像并推送到私有注册表

问题描述 投票:0回答:1

在进行了大量测试和移动之后,我仍然遇到与以前相同的问题。我会尽量简短地解释这个问题,但这个问题还有很多东西需要解释:

我有一个 Gitlab CI/CD 管道,它使用 buildx 构建多架构 docker 镜像,将它们推送到我的 Gitlab 容器注册表中的暂存文件夹,扫描它们是否存在漏洞,然后将该镜像推送到我的

dev
production
当注册表符合我的安全合规标准时。目前一切正常,直到我需要将多架构图像推送到我的
dev
production
注册表,当我的 bash 脚本运行
docker push
命令时,我收到此错误:

####################################
Pushing Docker Manifest (alpine_ci:1243060338)...
docker push registry.gitlab.com/warpit/foundation/warp-ci/dev/alpine_ci:1243060338
The push refers to repository [registry.gitlab.com/warpit/foundation/warp-ci/dev/alpine_ci]
missing content: content digest sha256:20ca4eb37ffdd4345c836ba3e5c0c3df8fd8121cc9b6cc73910b8b52eac65e74: not found
Note: You're trying to push a manifest list/index which references multiple platform specific manifests, but not all of them are available locally or available to the remote repository.
Make sure you have all the referenced content and try again.
Cleaning up project directory and file based variables
00:00
ERROR: Job failed: exit code 1

在继续之前,让我准确解释一下这个

push
工作正在做什么:

我的推送 CI 作业执行一个 bash 脚本,该脚本登录到我的容器注册表并拉取前一个

build
作业创建/推送的多架构映像。推送脚本拉取多架构映像后,它会标记该映像,将其指向
dev
production
注册表,然后推送它。

我理解这个错误的意思,但我不明白为什么会出现这个问题,因为我的脚本在拉取图像后运行

docker manifest inspect
命令,以便我可以检查它以进行调试。在下面的输出中,您可以看到存在错误所抱怨的图像摘要(由于过于冗长,不会显示整个输出):

####################################
Inspecting Docker Manifest (alpine_ci:1243060338)...
docker manifest inspect -v registry.gitlab.com/warpit/foundation/warp-ci/staging/alpine_ci:1243060338

{
        "Ref": "registry.gitlab.com/warpit/foundation/warp-ci/staging/alpine_ci:1243060338@sha256:20ca4eb37ffdd4345c836ba3e5c0c3df8fd8121cc9b6cc73910b8b52eac65e74",
        "Descriptor": {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "digest": "sha256:20ca4eb37ffdd4345c836ba3e5c0c3df8fd8121cc9b6cc73910b8b52eac65e74",
            "size": 840,
            "platform": {
                "architecture": "unknown",
                "os": "unknown"
            }
        },
        "Raw": "ewogICJzY2hlbWFWZXJzaW9uIjogMiwKICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsCiAgImNvbmZpZyI6IHsKICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5jb25maWcudjEranNvbiIsCiAgICAiZGlnZXN0IjogInNoYTI1Njo1OWEyNzEyOGJmMGYxYjIxNmFkZmFjMmFkYjY3YzBhNjgzNzFiODdlZDcwODMyNTlhNDAzOWY2NjE5YmQyNDRiIiwKICAgICJzaXplIjogMjQxCiAgfSwKICAibGF5ZXJzIjogWwogICAgewogICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5pbi10b3RvK2pzb24iLAogICAgICAiZGlnZXN0IjogInNoYTI1NjoxMzc2Yzg1NjdkNDI2NWIwY2EzY2NkNDEwMmJiODA4NDQxZDdjYmM5ZWM2MGE4OWY0OTZlNmZiMjM1NzRjMzE3IiwKICAgICAgInNpemUiOiA1NzQ2ODc3LAogICAgICAiYW5ub3RhdGlvbnMiOiB7CiAgICAgICAgImluLXRvdG8uaW8vcHJlZGljYXRlLXR5cGUiOiAiaHR0cHM6Ly9zcGR4LmRldi9Eb2N1bWVudCIKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm1lZGlhVHlwZSI6ICJhcHBsaWNhdGlvbi92bmQuaW4tdG90bytqc29uIiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NjVmMDUzYmI2ZmQ3NmU3NjA0YjRiMGJhM2E3Mjc0N2UzNWY5N2UyNGFmYjAzMjkwNmNkNmFlZjgxODUxOGFkMyIsCiAgICAgICJzaXplIjogMTM1MywKICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICJpbi10b3RvLmlvL3ByZWRpY2F0ZS10eXBlIjogImh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4yIgogICAgICB9CiAgICB9CiAgXQp9",
        "OCIManifest": {
            "schemaVersion": 2,
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "config": {
                "mediaType": "application/vnd.oci.image.config.v1+json",
                "digest": "sha256:59a27128bf0f1b216adfac2adb67c0a68371b87ed7083259a4039f6619bd244b",
                "size": 241
            },
            "layers": [
                {
                    "mediaType": "application/vnd.in-toto+json",
                    "digest": "sha256:1376c8567d4265b0ca3ccd4102bb808441d7cbc9ec60a89f496e6fb23574c317",
                    "size": 5746877,
                    "annotations": {
                        "in-toto.io/predicate-type": "https://spdx.dev/Document"
                    }
                },
                {
                    "mediaType": "application/vnd.in-toto+json",
                    "digest": "sha256:65f053bb6fd76e7604b4b0ba3a72747e35f97e24afb032906cd6aef818518ad3",
                    "size": 1353,
                    "annotations": {
                        "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
                    }
                }
            ]
        }
    }
]

我知道有两种解决方案:

  1. push
    脚本中从头开始构建图像并推送这些图像。
  2. 让我的
    build
    作业为所有架构构建特定于架构的映像,以便我的推送作业可以拉取它们并生成要推送的清单。

尽管我知道这些都能解决我的问题,但这不是我想要做的。话虽如此,这是我的问题:

  1. 任何人都可以告诉我更多有关我遇到的错误以及如何修复它的信息吗?
  2. 是否有可能简单地提取多架构映像,对其进行标记,然后推送到注册表?如果是这样,我做错了什么,因为我的脚本就是这么做的?

我尚未测试的一件事是在 for 循环中使用

docker pull --platform
来提取特定架构的图像,提取清单本身,然后尝试从那里推送。一旦我有时间这样做,我将用我的发现更新这张票。

docker manifest buildx
1个回答
0
投票

对于相关人士来说, 我仍然对为什么这个问题存在以及为什么我的 docker 清单似乎包含多个带有

unknown
的条目(因为它的架构和操作系统)感到困惑,但我找到了一个合理的解决方案,不需要我重新构建从头开始的图像。

既然我提供了一个其他人可能会觉得有用的解决方案,我将详细介绍一切的设置和功能。

最近我发现,为了让您能够在本地构建多架构镜像,并利用 docker 内置的

sbom
provenance
证明,您需要更新 docker 引擎以使用 containerd 镜像存储 。启用此功能并在 Gitlab 运行器实例上重新启动 docker 服务后,我决定更新我的管道、脚本等以支持具有
sbom
证明的多架构图像。

考虑到这一点,我现在可以让我的 Gitlab 运行程序同时构建任何架构的 docker 映像,将它们及其证明打包到单个清单中,并将其推送到我的注册表以供稍后扫描,并上传到我的开发和生产环境。我认为这很棒并且工作得非常好,直到我不得不拉出多架构图像并将其推送到我的注册表。

经过多次尝试后,我意识到我在构建时创建的

sbom
provenance
证明被附加到多架构图像作为具有
unknown
架构和操作系统的对象。这是导致我的问题的原因之一。我不完全理解为什么 docker 会这样工作,但事实就是这样。之后,我将构建命令更改为:

docker buildx build --platform "linux/amd64,linux/arm64" --provenance false -t <image-tag> -f <path/to/dokcerfile> . --push

一旦我这样做了,清单中的

unknown
对象就消失了,但我们还没有摆脱困境,因为我仍然收到错误消息,指出找不到
arm64
哈希值。为了解决这个问题,我将 pull 命令更新为 for 循环,该循环将迭代 CI 变量中定义的所有架构
$TARGET_PLATFORMS
:

#!/bin/bash

set -e

green='\033[0;32m'
blue='\033[0;34m'
clear='\033[0m'

###########################
# Script Requirnments
###########################
# This bash script requires the follwing environment variables to be set:
#  - IMAGE_NAME
#  - IMAGE_TAG
#  - ENV
#  - TARGET_PLATFORMS

###########################
# Login to Gitlab Registry
###########################
echo -e "${green}####################################${clear}"
echo -e "${blue}Logging into Gitlab Container Registry...${clear}"
docker login ${CI_REGISTRY} -u ${CI_REGISTRY_USER} -p ${CI_JOB_TOKEN}
echo -e "${green}####################################${clear}"

######################################
# Convert ${TARGET_PLATFORMS} to array
######################################
readarray -td ',' PLATFORMS < <(awk '{ gsub(/,[ ]+/,"\0"); print; }' <<<"$TARGET_PLATFORMS")

###########################
# Pull docker images
###########################
for platform in ${PLATFORMS[@]}
do
  echo -e "${green}####################################${clear}"
  echo -e "${blue}Pulling Docker Image (${IMAGE_NAME} | ${platform})...${clear}"
  echo -e "${green}docker pull --platform ${platform} ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG}${clear}"
  docker pull --platform ${platform} ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG}
  echo -e "${green}####################################${clear}"
done

#################################
# Inspect Docker Manifest
#################################
echo -e "${green}####################################${clear}"
echo -e "${blue}Inspecting Docker Manifest (${IMAGE_NAME}:${IMAGE_TAG})...${clear}"
echo -e "${green}docker manifest inspect -v ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG}${clear}"
docker manifest inspect -v ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG}
echo -e "${green}####################################${clear}"

##############################
# Tag & Push Docker Manifest
##############################
echo -e "${green}####################################${clear}"
echo -e "${blue}Tagging Docker Manifest (${IMAGE_NAME}:${IMAGE_TAG})...${clear}"
echo -e "${green}docker tag ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG} ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG}${clear}"
docker tag ${CI_REGISTRY_IMAGE}/staging/${IMAGE_NAME}:${IMAGE_TAG} ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG}
echo -e "${green}####################################${clear}"
echo -e "${blue}Pushing Docker Manifest (${IMAGE_NAME}:${IMAGE_TAG})...${clear}"
echo -e "${green}docker manifest push ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG}${clear}"
docker push ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG}
echo -e "${green}####################################${clear}"

##############################
# Tag & Push as latest
##############################
echo -e "${green}####################################${clear}"
echo -e "${blue}Tagging as latest (${IMAGE_NAME}:${IMAGE_TAG})...${clear}"
echo -e "${green}docker tag ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG} ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}${clear}"
docker tag ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}:${IMAGE_TAG} ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}
echo -e "${green}####################################${clear}"
echo -e "${blue}Pushing as latest (${IMAGE_NAME}:${IMAGE_TAG})...${clear}"
echo -e "${green}docker manifest push ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}${clear}"
docker push ${CI_REGISTRY_IMAGE}/${ENV}/${IMAGE_NAME}
echo -e "${green}####################################${clear}"

更新此内容后,我的推送作业终于成功,没有任何问题,因为当 docker 需要将清单推送到新注册表时,所有图像及其哈希值都存在。

© www.soinside.com 2019 - 2024. All rights reserved.