构建多架构镜像

构建多架构镜像

harbor目前不支持,harbor说2.0支持此功能

测试环境:

本机:

Linux HT061 4.13.0-36-generic #40~16.04.1-Ubuntu SMP Fri Feb 16 23:25:58 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

docker :

docker version
Client: Docker Engine - Community
Version:           19.03.8
API version:       1.40
Go version:        go1.12.17
Git commit:        afacb8b7f0
Built:             Wed Mar 11 01:25:46 2020
OS/Arch:           linux/amd64
Experimental:      true

Server: Docker Engine - Community
Engine:
Version:          19.03.8
API version:      1.40 (minimum version 1.12)
Go version:       go1.12.17
Git commit:       afacb8b7f0
Built:            Wed Mar 11 01:24:19 2020
OS/Arch:          linux/amd64
Experimental:     true
containerd:
Version:          1.2.6
GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version:          1.0.0-rc8
GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version:          0.18.0
GitCommit:        fec3683

服务器:

Linux storage 4.4.13-20170224.kylin.5.YUN+ #51 SMP Mon Apr 10 14:11:02 CST 2017 aarch64 aarch64 aarch64 GNU/Linux

docker:

 docker version
Client:
 Version:      17.03.2-ce-rc1
 API version:  1.27
 Go version:   go1.9.2
 Git commit:   eef6495
 Built:        Wed Nov 15 13:33:44 2017
 OS/Arch:      linux/arm64

Server:
 Version:      17.03.2-ce-rc1
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.9.2
 Git commit:   eef6495
 Built:        Wed Nov 15 13:33:44 2017
 OS/Arch:      linux/arm64
 Experimental: false

开始

1. docker version要求

Docker 版本不低于 19.03 且开启buildx export DOCKER_CLI_EXPERIMENTAL=enabled或者

cat /etc/docker/daemon.json 
{
   "experimental": true
}
docker buildx --help 

Usage:	docker buildx COMMAND

Build with BuildKit

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  inspect     Inspect current builder instance
  ls          List builder instances
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance
  version     Show buildx version information 

Run 'docker buildx COMMAND --help' for more information on a command.

2. 启用 binfmt_misc

docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64

验证是 binfmt_misc 否开启:

root@HT061:/  ls -al /proc/sys/fs/binfmt_misc/
总用量 0
drwxr-xr-x 2 root root 0 4月   7 09:06 .
dr-xr-xr-x 1 root root 0 4月   7 09:05 ..
-rw-r--r-- 1 root root 0 4月   7 09:06 ksh
-rw-r--r-- 1 root root 0 4月   7 09:06 llvm-6.0-runtime.binfmt
-rw-r--r-- 1 root root 0 4月   7 09:06 python2.7
-rw-r--r-- 1 root root 0 4月   7 09:06 python3.5
-rw-r--r-- 1 root root 0 4月   7 09:06 python3.6
-rw-r--r-- 1 root root 0 4月   9 08:49 qemu-aarch64
-rw-r--r-- 1 root root 0 4月   9 08:49 qemu-arm
-rw-r--r-- 1 root root 0 4月   9 08:49 qemu-ppc64le
-rw-r--r-- 1 root root 0 4月   9 08:49 qemu-riscv64
-rw-r--r-- 1 root root 0 4月   9 08:49 qemu-s390x
--w------- 1 root root 0 4月   7 09:06 register
-rw-r--r-- 1 root root 0 4月   7 09:06 status

验证是否启用了相应的处理器:

root@HT061:/  cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7
mask ffffffffffffff00fffffffffffffffffeffff

3. 从默认的构建器切换到多平台构建器

root@HT061:~#  docker buildx create --use --name mybuilder
mybuilder

启动构建器:

root@HT061:~#  docker buildx inspect mybuilder --bootstrap

查看当前使用的构建器及构建器支持的 CPU 架构,可以看到支持很多 CPU 架构:

root@HT061:~#  docker buildx ls

4. 准备代码和Dockerfile

root@HT061:/home/ht061/桌面/mutiarch# cat hello.go 
package main

import (
        "fmt"
        "runtime"
)

func main() {
        fmt.Printf("Hello, %s!\n", runtime.GOARCH)
}
root@HT061:/home/ht061/桌面/mutiarch# cat Dockerfile 
FROM golang:alpine AS builder
RUN mkdir /app
ADD . /app/
WORKDIR /app
RUN go build -o hello .

FROM alpine
RUN mkdir /app
WORKDIR /app
COPY --from=builder /app/hello .
CMD ["./hello"]

golang和alpine都是支持多架构的 golang:alpine镜像: alpine镜像:

5. build/push

build镜像并push到dockerhub

root@HT061:/home/ht061/桌面/mutiarch#  docker buildx build -t lurenjia/hello-arch --platform=linux/arm64,linux/amd64    . --push 

查看不同架构信息:

root@HT061:/home/ht061/桌面/mutiarch# docker manifest inspect lurenjia/hello-arch

6. pull / 测试

由于之前已经启用了 binfmt_misc,现在我们就可以运行任何 CPU 架构的 Docker 镜像了,因此可以在本地系统上测试之前生成的 2个镜像是否有问题。 现在就可以通过 docker pull lurenjia/hello-arch 拉取刚刚创建的镜像了,Docker 将会根据你的 CPU 架构拉取匹配的镜像

root@HT061:/home/ht061/桌面/mutiarch# docker buildx imagetools inspect lurenjia/hello-arch

root@HT061:/home/ht061/桌面/mutiarch# docker run --rm docker.io/lurenjia/hello-arch:latest@sha256:cccc0329458f6a4a6a689080a8631b3634feeda5792ed0f6d00bc3fc86c02961

root@HT061:/home/ht061/桌面/mutiarch#  docker run --rm  docker.io/lurenjia/hello-arch:latest@sha256:5a99f4cd5f90b03e63a77e6646e6eda8daa297f492d22b5a3e061181da966bfd

docker pull / docker run 测试

在arm64上:

原生仓库支持此功能

root@HT061:/home/ht061/桌面/registry# ./test-registry.sh 192.168.17.187:5000/arch
Warning: some commands will fail if you are not authenticated to 192.168.17.187:5000/arch
>> 1: Pulling required images from DockerHub
>> 2: Tagging and pushing images to registry 192.168.17.187:5000/arch
docker tag s390x/alpine:latest 192.168.17.187:5000/arch/s390x_alpine:latest
The push refers to repository [192.168.17.187:5000/arch/s390x_alpine]
b0f4d46f0129: Mounted from s390x_alpine 
latest: digest: sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de size: 528
docker tag ppc64le/alpine:latest 192.168.17.187:5000/arch/ppc64le_alpine:latest
The push refers to repository [192.168.17.187:5000/arch/ppc64le_alpine]
d80722399fe1: Mounted from ppc64le_alpine 
latest: digest: sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c size: 528
docker tag aarch64/alpine:latest 192.168.17.187:5000/arch/aarch64_alpine:latest
The push refers to repository [192.168.17.187:5000/arch/aarch64_alpine]
6b5c485d13f0: Mounted from aarch64_alpine 
latest: digest: sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef size: 528
docker tag alpine:latest 192.168.17.187:5000/arch/amd64_alpine:latest
The push refers to repository [192.168.17.187:5000/arch/amd64_alpine]
beee9f30bc1f: Mounted from amd64_alpine 
latest: digest: sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 size: 528
>> 4: Attempt creating manifest list on registry 192.168.17.187:5000/arch
DEBU[0000] endpoints: [{false https://192.168.17.187:5000 v2 false true 0xc420062a80} {false http://192.168.17.187:5000 v2 false true 0xc420062a80} {false https://192.168.17.187:5000 v1 false true 0xc420062c00} {false http://192.168.17.187:5000 v1 false true 0xc420062c00}] 
DEBU[0000] repoName: arch/alpine                        
INFO[0000] Retrieving digests of images...              
DEBU[0000] authConfig for 192.168.17.187:5000:          
DEBU[0000] endpoints: [{false https://192.168.17.187:5000 v2 false true 0xc420062f00} {false http://192.168.17.187:5000 v2 false true 0xc420062f00}] 
DEBU[0000] Trying to fetch image manifest of 192.168.17.187:5000/arch/ppc64le_alpine repository from https://192.168.17.187:5000 v2 
INFO[0000] Image "192.168.17.187:5000/arch/ppc64le_alpine:latest" is digest sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c; size: 528 
DEBU[0000] Adding manifest references of "192.168.17.187:5000/arch/ppc64le_alpine:latest" to blob mount requests 
DEBU[0000] Adding manifest "arch/ppc64le_alpine" -> to be pushed to "arch/alpine" as a manifest reference 
DEBU[0000] authConfig for 192.168.17.187:5000:          
DEBU[0000] endpoints: [{false https://192.168.17.187:5000 v2 false true 0xc4204d0600} {false http://192.168.17.187:5000 v2 false true 0xc4204d0600}] 
DEBU[0000] Trying to fetch image manifest of 192.168.17.187:5000/arch/amd64_alpine repository from https://192.168.17.187:5000 v2 
INFO[0000] Image "192.168.17.187:5000/arch/amd64_alpine:latest" is digest sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221; size: 528 
DEBU[0000] Adding manifest references of "192.168.17.187:5000/arch/amd64_alpine:latest" to blob mount requests 
DEBU[0000] Adding manifest "arch/amd64_alpine" -> to be pushed to "arch/alpine" as a manifest reference 
DEBU[0000] authConfig for 192.168.17.187:5000:          
DEBU[0000] endpoints: [{false https://192.168.17.187:5000 v2 false true 0xc420349080} {false http://192.168.17.187:5000 v2 false true 0xc420349080}] 
DEBU[0000] Trying to fetch image manifest of 192.168.17.187:5000/arch/s390x_alpine repository from https://192.168.17.187:5000 v2 
INFO[0000] Image "192.168.17.187:5000/arch/s390x_alpine:latest" is digest sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de; size: 528 
DEBU[0000] Adding manifest references of "192.168.17.187:5000/arch/s390x_alpine:latest" to blob mount requests 
DEBU[0000] Adding manifest "arch/s390x_alpine" -> to be pushed to "arch/alpine" as a manifest reference 
DEBU[0000] authConfig for 192.168.17.187:5000:          
DEBU[0000] endpoints: [{false https://192.168.17.187:5000 v2 false true 0xc420063c80} {false http://192.168.17.187:5000 v2 false true 0xc420063c80}] 
DEBU[0000] Trying to fetch image manifest of 192.168.17.187:5000/arch/aarch64_alpine repository from https://192.168.17.187:5000 v2 
INFO[0000] Image "192.168.17.187:5000/arch/aarch64_alpine:latest" is digest sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef; size: 528 
DEBU[0000] Adding manifest references of "192.168.17.187:5000/arch/aarch64_alpine:latest" to blob mount requests 
DEBU[0000] Adding manifest "arch/aarch64_alpine" -> to be pushed to "arch/alpine" as a manifest reference 
DEBU[0000] Manifest list push url: https://192.168.17.187:5000/v2/arch/alpine/manifests/latest 
DEBU[0000] mediaType of manifestList: application/vnd.docker.distribution.manifest.list.v2+json 
DEBU[0000] authConfig for 192.168.17.187:5000:          
DEBU[0000] Mount of blob sha256:597c87082c114f1e78cf07db96e69306d6771ce8fe5f72cecb82a5c724c1bd9f succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:597c87082c114f1e78cf07db96e69306d6771ce8fe5f72cecb82a5c724c1bd9f" 
DEBU[0000] Mount of blob sha256:bc1c99f4ba60de0d3ca52dc6855483e24c91884e33df71f502bbff6eb909d9b9 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:bc1c99f4ba60de0d3ca52dc6855483e24c91884e33df71f502bbff6eb909d9b9" 
DEBU[0000] Mount of blob sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72" 
DEBU[0000] Mount of blob sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819" 
DEBU[0000] Mount of blob sha256:452f204f05aae9837957231219a5d48e346220f0ac71874632109c1f8be49b9e succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:452f204f05aae9837957231219a5d48e346220f0ac71874632109c1f8be49b9e" 
DEBU[0000] Mount of blob sha256:ca1c6795bfb97df28a926fd646127ba4944b69beb1cea7b00d62787b8b3c0108 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:ca1c6795bfb97df28a926fd646127ba4944b69beb1cea7b00d62787b8b3c0108" 
DEBU[0000] Mount of blob sha256:8e8812ba3b2d44c581d76fa0ad56a60e346c10cb1a778b10089d3d385502e0a4 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:8e8812ba3b2d44c581d76fa0ad56a60e346c10cb1a778b10089d3d385502e0a4" 
DEBU[0000] Mount of blob sha256:bc1d962af60541fd028aaa6f21d62a662d718b3571577acf3fb65c04a61b1150 succeeded, location: "https://192.168.17.187:5000/v2/arch/alpine/blobs/sha256:bc1d962af60541fd028aaa6f21d62a662d718b3571577acf3fb65c04a61b1150" 
DEBU[0000] manifest reference push URL: https://192.168.17.187:5000/v2/arch/alpine/manifests/sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c 
DEBU[0000] referenced manifest "arch/ppc64le_alpine" pushed; digest matches: sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c 
DEBU[0000] manifest reference push URL: https://192.168.17.187:5000/v2/arch/alpine/manifests/sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 
DEBU[0000] referenced manifest "arch/amd64_alpine" pushed; digest matches: sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 
DEBU[0000] manifest reference push URL: https://192.168.17.187:5000/v2/arch/alpine/manifests/sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de 
DEBU[0000] referenced manifest "arch/s390x_alpine" pushed; digest matches: sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de 
DEBU[0000] manifest reference push URL: https://192.168.17.187:5000/v2/arch/alpine/manifests/sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef 
DEBU[0000] referenced manifest "arch/aarch64_alpine" pushed; digest matches: sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef 
Digest: sha256:1825baa9aeac87267c3132a89d87a10e8292d5e5042d25aded65923200c82bec 1388

root@HT061:/home/ht061/桌面/registry# cat test-registry.yml
image: __REGISTRY__/alpine:latest
manifests: 
  - 
    image: __REGISTRY__/ppc64le_alpine:latest
    platform: 
      architecture: ppc64le
      os: linux
  - 
    image: __REGISTRY__/amd64_alpine:latest
    platform: 
      architecture: amd64
      os: linux
  - 
    image: __REGISTRY__/s390x_alpine:latest
    platform: 
      architecture: s390x
      os: linux
  - 
    image: __REGISTRY__/aarch64_alpine:latest
    platform: 
      architecture: arm64
      os: linux
      variant: v8

root@HT061:/home/ht061/桌面/registry# cat test-registry.sh
#!/bin/bash

## This script will test creating a manifest list against a specified registry
## that claims to support the Docker v2 distribution API and the v2.2 image
## specification.

## It expects `manifest-tool` to be in the $PATH as well as the docker client.
## You must be authenticated via `docker login` to the registry provided or
## whatever method that registry provides for inserting docker authentication.

## It will pull 4 images from DockerHub (alpine for 4 architectures)
## and tag them against the provided registry; push them as images to that
## registry/repo and then use the `manifest-tool` to assemble them into a
## manifest list and push using the V2 API and V2 features (like cross-repository
## push and references to blobs already existing)

_REGISTRY="${1}"

_IMAGELIST="s390x/alpine
ppc64le/alpine
aarch64/alpine
alpine"

[ -z "${_REGISTRY}" ] && {
	echo "Please provide a registry URL + namespace/repo name as the first parameter"
	exit 1
}

echo "Warning: some commands will fail if you are not authenticated to ${_REGISTRY}"

echo ">> 1: Pulling required images from DockerHub"
for i in $_IMAGELIST; do
	docker pull ${i}:latest
done

echo ">> 2: Tagging and pushing images to registry ${_REGISTRY}"
for i in $_IMAGELIST; do
	target="${i/\//_}"
	[ "${target}" == "${i}" ] && {
		# special case for no arch prefix on amd64 (x86_64 Linux) images  
		target="amd64_${i}"
	}
	echo docker tag ${i}:latest ${_REGISTRY}/${target}:latest
	docker tag ${i}:latest ${_REGISTRY}/${target}:latest
	docker push ${_REGISTRY}/${target}:latest
done

echo ">> 4: Attempt creating manifest list on registry ${_REGISTRY}"

sed s,__REGISTRY__,${_REGISTRY}, test-registry.yml >test-registry.yaml
manifest-tool --debug push from-spec test-registry.yaml


harbor-2.0.0 测试

支持此功能:

手动上传manifest:

root@HT061:/opt/harbor-2.0.0/harbor/timi# docker images | grep alpine | grep ygt.h | grep 0.0.1 
ygt.harbor.csse/timi/s390x_alpine            0.0.1                                      452f204f05aa        8 weeks ago         5.65MB
ygt.harbor.csse/timi/ppc64le_alpine          0.0.1                                      597c87082c11        8 weeks ago         6.63MB
ygt.harbor.csse/timi/amd64_alpine            0.0.1                                      a187dde48cd2        8 weeks ago         5.6MB
ygt.harbor.csse/timi/aarch64_alpine          0.0.1                                      8e8812ba3b2d        3 years ago         3.83MB
root@HT061:/opt/harbor-2.0.0/harbor/timi# 

#上传
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker push ygt.harbor.csse/timi/s390x_alpine:0.0.1
The push refers to repository [ygt.harbor.csse/timi/s390x_alpine]
b0f4d46f0129: Layer already exists 
0.0.1: digest: sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de size: 528
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker push ygt.harbor.csse/timi/ppc64le_alpine:0.0.1
The push refers to repository [ygt.harbor.csse/timi/ppc64le_alpine]
d80722399fe1: Layer already exists 
0.0.1: digest: sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c size: 528
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker push ygt.harbor.csse/timi/amd64_alpine:0.0.1
The push refers to repository [ygt.harbor.csse/timi/amd64_alpine]
beee9f30bc1f: Layer already exists 
0.0.1: digest: sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 size: 528
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker push ygt.harbor.csse/timi/aarch64_alpine:0.0.1
The push refers to repository [ygt.harbor.csse/timi/aarch64_alpine]
6b5c485d13f0: Layer already exists 
0.0.1: digest: sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef size: 528
root@HT061:/opt/harbor-2.0.0/harbor/timi# 

#创建manifest
root@HT061:/opt/harbor-2.0.0/harbor/timi#  docker manifest create ygt.harbor.csse/timi/alpine:0.0.1 ygt.harbor.csse/timi/s390x_alpine:0.0.1 ygt.harbor.csse/timi/ppc64le_alpine:0.0.1 ygt.harbor.csse/timi/amd64_alpine:0.0.1 ygt.harbor.csse/timi/aarch64_alpine:0.0.1
Created manifest list ygt.harbor.csse/timi/alpine:0.0.1

#push manifest
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker manifest push   ygt.harbor.csse/timi/alpine:0.0.1
Pushed ref ygt.harbor.csse/timi/alpine@sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef with digest: sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef
Pushed ref ygt.harbor.csse/timi/alpine@sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 with digest: sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221
Pushed ref ygt.harbor.csse/timi/alpine@sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c with digest: sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c
Pushed ref ygt.harbor.csse/timi/alpine@sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de with digest: sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de
sha256:e959c02d88f4d4fceabba95f2136a1932d56d3ab438adc581403f8ae65655ea5
root@HT061:/opt/harbor-2.0.0/harbor/timi# 

#查看manifest
root@HT061:/opt/harbor-2.0.0/harbor/timi# docker manifest inspect  ygt.harbor.csse/timi/alpine:0.0.1
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:6ace9a4351a3f02894c273cee7adc519318d6186f0d364fa37e418394576c5ef",
         "platform": {
            "architecture": "arm64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:04d04970e33c492fa411b508455d02a85978492db0403b8a714f365432c04f1c",
         "platform": {
            "architecture": "ppc64le",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:842db5c820b72e078691118de1791c9baa15ba09c87235e70990186ac0f825de",
         "platform": {
            "architecture": "s390x",
            "os": "linux"
         }
      }
   ]
}
root@HT061:/opt/harbor-2.0.0/harbor/timi# 

参考