[TOC]
0x00 基础介绍 描述: 作为公司内部 PaaS toB 产品的打包发布人员,容器镜像对我们打工人而言就像是工地上的砖头 🧱,而我的一部分工作就是将这些砖头在各个仓库之间搬来搬去,最终将这些砖头打包放在产品的安装包中,形成一个完整的 PaaS 产品安装包。
Q: 在 PaaS (平台即服务)中的大家常说的ToB与ToC到底是什么? 
ToC 面向普通用户服务, 主要是让用户体验感好,解决用户使用方面的问题记录,并返回给前后端开发。 ToB 是面向企业用户服务, 产品可用、其中最关键是让Boss使用Happly!
 
Q: 假如有如下场景,我们从dockerhub公共仓库中下载一个GB以上的镜像,到本地的私有仓库中,我想通常你会这样做先docker pull 到本地,然后使用docker tag更改为私有仓库地址加上镜像名称版本,最后再使用docker push上传镜像到私有仓库中,以供其它内网机器拉取并使用。虽然该方法是可行,但是如果有多个大于GB以上的镜像需要上传到私有仓库,每次都要先解压layer到本地,然后再压缩layer上传到私有仓库中,你能想象此过程花费的时间有多久吗? 对于我们运维工程师来说时间就是金钱,所以需想尽一切方法来节约时间成本,那有没有一种办法可以直接将 registry 上的 blob 复制到另一个 registry,中间过程不涉及对镜像 layer 的解压缩,这岂不美哉。 
解决方案当然是存在的,如果你不想使用docker进行images镜像拉取上传,我们完成可以使用skope工具来完全替代 docker-cli 来搬运镜像,skopeo是一个命令行实用程序,可对容器映像和映像存储库执行各种操作。
 
什么是Skopeo?  skopeo 使用 API V2 Registry,例如 Docker Registry、Atomic Registry、私有Registry、本地目录和本地 OCI 镜像目录。skopeo 不需要运行守护进程,它可以执行的操作包括:
通过各种存储机制复制镜像,例如,可以在不需要特权的情况下将镜像从一个Registry复制到另一个Registry 
检测远程镜像并查看其属性,包括其图层,无需将镜像拉到本地 
从镜像库中删除镜像 
当存储库需要时,skopeo 可以传递适当的凭据和证书进行身份验证 
 
镜像存储特点  根据 Robin 大佬在 《镜像仓库中镜像存储的原理解析》文章里得出的结论:
通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。 
两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。 
两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同 
 
项目信息  Github 官方地址: https://github.com/containers/skopeo  Gitee mirror: https://gitee.com/mirrors/skopeo 
0x01 安装编译 描述: Skopeo 官方安装&编译方式参考文档: https://github.com/containers/skopeo/blob/main/install.md 
本节安装实践环境将在Ubuntu 20.04 LTS 以及 docker 20.10.12 中进行实践源码编译以及 apt 仓库源下载安装实践。
1.源码编译(静态) 描述: 要构建 skopeo 二进制文件您至少需要 Go 1.12 版本以上, 其次构建 skopeo 有两种方法,即在容器中或者在本地环境中构建(安装环境较为复杂), 此处为了方便演示将采用容器方式进行编译构建。 
      
  
      
     
      
        
        [TOC]
0x00 基础介绍 描述: 作为公司内部 PaaS toB 产品的打包发布人员,容器镜像对我们打工人而言就像是工地上的砖头 🧱,而我的一部分工作就是将这些砖头在各个仓库之间搬来搬去,最终将这些砖头打包放在产品的安装包中,形成一个完整的 PaaS 产品安装包。
Q: 在 PaaS (平台即服务)中的大家常说的ToB与ToC到底是什么? 
ToC 面向普通用户服务, 主要是让用户体验感好,解决用户使用方面的问题记录,并返回给前后端开发。 ToB 是面向企业用户服务, 产品可用、其中最关键是让Boss使用Happly!
 
Q: 假如有如下场景,我们从dockerhub公共仓库中下载一个GB以上的镜像,到本地的私有仓库中,我想通常你会这样做先docker pull 到本地,然后使用docker tag更改为私有仓库地址加上镜像名称版本,最后再使用docker push上传镜像到私有仓库中,以供其它内网机器拉取并使用。虽然该方法是可行,但是如果有多个大于GB以上的镜像需要上传到私有仓库,每次都要先解压layer到本地,然后再压缩layer上传到私有仓库中,你能想象此过程花费的时间有多久吗? 对于我们运维工程师来说时间就是金钱,所以需想尽一切方法来节约时间成本,那有没有一种办法可以直接将 registry 上的 blob 复制到另一个 registry,中间过程不涉及对镜像 layer 的解压缩,这岂不美哉。 
解决方案当然是存在的,如果你不想使用docker进行images镜像拉取上传,我们完成可以使用skope工具来完全替代 docker-cli 来搬运镜像,skopeo是一个命令行实用程序,可对容器映像和映像存储库执行各种操作。
 
什么是Skopeo?  skopeo 使用 API V2 Registry,例如 Docker Registry、Atomic Registry、私有Registry、本地目录和本地 OCI 镜像目录。skopeo 不需要运行守护进程,它可以执行的操作包括:
通过各种存储机制复制镜像,例如,可以在不需要特权的情况下将镜像从一个Registry复制到另一个Registry 
检测远程镜像并查看其属性,包括其图层,无需将镜像拉到本地 
从镜像库中删除镜像 
当存储库需要时,skopeo 可以传递适当的凭据和证书进行身份验证 
 
镜像存储特点  根据 Robin 大佬在 《镜像仓库中镜像存储的原理解析》文章里得出的结论:
通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。 
两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。 
两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同 
 
项目信息  Github 官方地址: https://github.com/containers/skopeo  Gitee mirror: https://gitee.com/mirrors/skopeo 
0x01 安装编译 描述: Skopeo 官方安装&编译方式参考文档: https://github.com/containers/skopeo/blob/main/install.md 
本节安装实践环境将在Ubuntu 20.04 LTS 以及 docker 20.10.12 中进行实践源码编译以及 apt 仓库源下载安装实践。
1.源码编译(静态) 描述: 要构建 skopeo 二进制文件您至少需要 Go 1.12 版本以上, 其次构建 skopeo 有两种方法,即在容器中或者在本地环境中构建(安装环境较为复杂), 此处为了方便演示将采用容器方式进行编译构建。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ git clone  --depth=1 https://github.com/containers/skopeo.git   $ cd  skopeo $ sed -i 's#proxy.golang.org#https://goproxy.cn#g'  skopeo/Makefile $ sudo apt-get install go-md2man   $ whereis go-md2man   $ BUILD_IMAGE="golang:latest"  $ docker run --name skopeo-build -v $PWD :/src -v /usr/bin/go-md2man:/go/bin/go-md2man -w /src -e CGO_ENABLED=0 -e GOPROXY=https://goproxy.cn,direct ${BUILD_IMAGE}  \ sh -c 'make BUILDTAGS=containers_image_openpgp GO_DYN_FLAGS='     $ cd  ./bin  $ ./skopeo --help           
构建关键参数解析: 
CGO_ENABLED=0 : 设置该环境变量, 禁用 CGO 会导致 Go 在可能的情况下更喜欢静态连接库,而不是动态链接到系统库 (解决可以在Ubuntu或者其它linux发行版中执行编译后二进制文件)。 
GOPROXY=https://goproxy.cn,direct  : Golong 依赖下载镜像站,加快go get依赖拉拉取。 
BUILDTAGS=containers_image_openpgp : 设置该make参数消除了对libgpgme 及其配套库的依赖, Skopeo 的一些特性依赖于非 Go 库,例如 libgpgme 和 libdevmapper。 
GO_DYN_FLAGS= : 清空该make参数 (否则会强制创建动态可执行文件) 
 
2.分发包安装 描述: skopeo 可能已经打包在您的发行版中,此处以ubuntu 20.04为例进行安装。
1 2 3 4 5 6 7 8 9 10 11 sudo apt-get -y update sudo apt-get -y install skopeo . /etc/os-release echo  "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID} / /"  | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.listcurl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID} /Release.key | sudo apt-key add - sudo apt-get update sudo apt-get -y upgrade sudo apt-get -y install skopeo 
 
3.容器安装运行 Skopeo 容器镜像可在 quay.io/skopeo/stable:latest 获得, 例如我们采用podman命令进行如下操作:1 podman run docker://quay.io/skopeo/stable:latest copy --help  
0x02 快速上手 1.命令浅析 描述: skopen 是操作各种容器映像和容器映像仓库的工具,其使用方法及其可用命令如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ./skopeo --help      Usage:   skopeo [flags]   skopeo [command ] Available Commands:    copy             delete           help              inspect          list-tags        login              logout              manifest-digest    standalone-sign      standalone-verify    sync               Flags:     --command -timeout duration        --debug                           --insecure-policy                 --override-arch ARCH              --override-os OS                  --override-variant VARIANT        --policy string                   --registries.d DIR                --tmpdir string               -h, --help                        help  for  skopeo  -v, --version                    Version for  Skopeo 
2.Skopeo初体验 描述: 在使用体验skopeo之前,我们需要了解一哈 Skopeo 可以在那些图像和存储库类型上执行镜像操作(官网文档走一波):
Repository types 
Describe 
Example 
 
 
containers-storage:docker-reference 
适用于后端是 Podman, CRI-O, Buildah 的情况 
containers-storage: 
 
dir:path 
适用于将manifest, layer tarballs 和 signatures 存储为单独文件的现有本地目录路径的情况 
dir:/tmp/alpine:latest 
 
docker://docker-reference 
适用于Registry中实现”Docker Registry HTTP API V2”的镜像的情况 
docker://harbor.weiyigeek.top/myblog:v2.8 
 
docker-archive:path[:docker-reference] 
适用于采用docker save命令导出镜像以tar格式存储的文件的情况 
docker-archive:alpine.tar 
 
docker-daemon:docker-reference 
适用于存储在 docker 守护进程内部存储中的图像的情况 
docker-daemon:alpine:latest 
 
oci:path:tag 
适用于符合”Open Container Image Layout Specification”的目录中的图像标记 
oci:alpine:latest 
 
 
温馨提示: 同一个镜像存在的方式有可能不同,不同类型方式存储对镜像的 layer 处理的方式也不一样,。
测试环境说明 1 2 3 Docker 官方 hub 仓库 -> docker.io              私有 Harbor 仓库     -> harbor.weiyigeek.top 临时创建的本地仓库    -> 192.168.12.111:5000    
说明: 上述仓库都是在Registry中支持Docker Registry HTTP API V2版本的。
Skopeo login/loout - 远程仓库 Auth 描述: 在使用 skopeo 前如果 src 或 dest 镜像是在 registry 仓库中的并且配置了非 public 的镜像需要相应的 auth 认证, 此时我们可以使用 docker login 或者 skopeo login 的方式登录到 registry 仓库,然后默认会在~/.docker目录下生成 registry 登录配置文件 config.json ,该文件里保存了登录需要的验证信息,skopeo 拿到该验证信息才有权限往 registry push 镜像。
登陆认证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ skopeo login -u WeiyiGeek -p testpassword harbor.weiyigeek.top    docker login -u WeiyiGeek docker.io docker login -u WeiyiGeek harbor.weiyigeek.top docker login -u anonymous -p anonymous 192.168.12.111:5000                  cat ~/.docker/config.json {   "auths" : {     "192.168.12.111:5000" : {         "auth" : "YW5v*******Q=="        },     "harbor.weiyigeek.top" : {         "auth" : "YWR*******LkA="        },     "https://index.docker.io/v1/" : {         "auth" : "d2Vp**************kyZA=="      }   } } 
注销认证 1 $ skopeo logout  myregistrydomain.com:5000 
温馨提示:  如果企业自建harbor仓库(一般都会设置自签证书)或者其它私有仓库配置证书,为了防止出错建议进行以下操作(正式环境请根据需要进行配置)。1 2 3 4 5 6 7 "insecure-registries" : ["harbor.weiyigeek.top" ,"192.168.12.111:5000" ]  mkdir -vp /etc/docker/certs.d/harbor.weiyigeek.top cp -a /deployapp/harbor/harbor.pem  /etc/docker/certs.d/harbor.weiyigeek.top/harbor.crt 
温馨提示:  为了防止后续执行skopeo命令操作镜像时出错, 建议忽略policy策略和证书校验参数如下:1 2 3 --insecure-policy \ --src-tls-verify=false  \  --dest-tls-verify=false  \ 
Skopeo inspect - 检查存储库中的镜像 描述: skopeo 能够检查容器Registry上的存储库并获取图像层。检查命令获取存储库的清单,它能够向您显示有关整个存储库或标签的类似 docker inspect 的 json 输出。与 docker inspect 相比,此工具可帮助您在拉取存储库或标签之前收集有用的信息(使用磁盘空间), 检查命令可以向您显示给定存储库可用的标签、映像具有的标签、映像的创建日期和操作系统等。
支持传输的类型 : containers-storage, dir, docker, docker-archive, docker-daemon, oci, oci-archive, ostree, tarball
步骤 01.显示 busybox:latest 镜像的属性相关信息。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 skopeo inspect docker://docker.io/busybox:latest {   "Name" : "docker.io/library/busybox" ,   "Digest" : "sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678" ,   "RepoTags" : [       "1" ,       "1-glibc" ,       "1-musl" ,       "1-ubuntu" ,       "1-uclibc" ,       "1.21-ubuntu" ,       "1.21.0-ubuntu" ,         .......                 "unstable-uclibc"    ],   "Created" : "2021-12-30T19:19:41.006954958Z" ,   "DockerVersion" : "20.10.7" ,   "Labels" : null,   "Architecture" : "amd64" ,   "Os" : "linux" ,   "Layers" : [       "sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa"    ],   "Env" : [       "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"    ] } 
步骤 02.显示 busybox:latest 镜像的容器配置相关信息。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ skopeo inspect --config docker://docker.io/busybox:latest  | jq {   "created" : "2021-12-30T19:19:41.006954958Z" ,   "architecture" : "amd64" ,   "os" : "linux" ,   "config" : {     "Env" : [       "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"      ],     "Cmd" : [       "sh"      ]   },   "rootfs" : {     "type" : "layers" ,     "diff_ids" : [       "sha256:01fd6df81c8ec7dd24bbbd72342671f41813f992999a3471b9d9cbc44ad88374"      ]   },   "history" : [     {       "created" : "2021-12-30T19:19:40.833034683Z" ,       "created_by" : "/bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4cfde1f280177b458390ed5a6d1b54c6169522bc2c4d838e in / "      },     {       "created" : "2021-12-30T19:19:41.006954958Z" ,       "created_by" : "/bin/sh -c #(nop)  CMD [\"sh\"]" ,       "empty_layer" : true      }   ] } 
步骤 03.显示未经验证的图像 Digest(摘要)1 $ skopeo inspect --format "Name: {{.Name}} Digest: {{.Digest}}"  docker://docker.io/busybox:latest 
Skopeo copy - 仓库镜像拷贝 描述: skopeo 可以在各种存储机制之间复制容器镜像,支持包括容器仓库(The Quay, Docker Hub, OpenShift, GCR, ,Artifactory ...)以及容器存储后端 (Podman, CRI-O, Docker) 等、本地目录、本地 OCI-layout 目录。
例如,此处我从hub仓库复制busybox:latest镜像到私有harbot仓库中,在从私有harbot仓库中拷贝到本地指定目录中。
步骤 01.从 regsitry A 到 registry B 复制 busybox:latest 镜像。1 2 3 4 5 6 skopeo copy --insecure-policy --src-tls-verify=false  --dest-tls-verify=false  --dest-authfile /root/.docker/config.json docker://docker.io/busybox:latest docker://harbor.weiyigeek.top/devops/busybox:latest                
 Tips: 由上述日志可以看到 skopeo 是直接从 registry 中 copy 镜像 layer 的 blob 文件,传输是镜像在 registry 中存储的原始格式。
步骤 02.从 registry B 复制 busybox:latest 镜像到本地 busybox:latest 目录中。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 skopeo copy --insecure-policy --src-tls-verify=false  docker://harbor.weiyigeek.top/devops/busybox:latest dir:busybox:latest ls && tree busybox\:latest/ busybox:latest/ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa    ├── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a    ├── manifest.json └── version 0 directories, 4 files cat busybox\:latest/manifest.json {    "schemaVersion" : 2,    "mediaType" : "application/vnd.docker.distribution.manifest.v2+json" ,    "config" : {       "mediaType" : "application/vnd.docker.container.image.v1+json" ,       "size" : 1456,       "digest" : "sha256:beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a"     },    "layers" : [       {          "mediaType" : "application/vnd.docker.image.rootfs.diff.tar.gzip" ,          "size" : 772788,          "digest" : "sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa"        }    ] } jq '.'  busybox\:latest/beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a {   "architecture" : "amd64" ,   "config" : {     "Hostname" : "" ,     "Domainname" : "" ,     "User" : "" ,     "AttachStdin" : false ,     "AttachStdout" : false ,     "AttachStderr" : false ,     "Tty" : false ,     "OpenStdin" : false ,     "StdinOnce" : false ,     "Env" : [       "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"      ],     "Cmd" : [       "sh"      ],     "Image" : "sha256:da658412c37aa24e561eb7e16c61bc82a9711340d8fb5cf1a8f39d8e96d7f723" ,     "Volumes" : null,     "WorkingDir" : "" ,     "Entrypoint" : null,     "OnBuild" : null,     "Labels" : null   }, ........   "history" : [     {       "created" : "2021-12-30T19:19:40.833034683Z" ,       "created_by" : "/bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4cfde1f280177b458390ed5a6d1b54c6169522bc2c4d838e in / "      },     {       "created" : "2021-12-30T19:19:41.006954958Z" ,       "created_by" : "/bin/sh -c #(nop)  CMD [\"sh\"]" ,       "empty_layer" : true      }   ],   "os" : "linux" ,   "rootfs" : {     "type" : "layers" ,     "diff_ids" : [       "sha256:01fd6df81c8ec7dd24bbbd72342671f41813f992999a3471b9d9cbc44ad88374"      ]   } } 
步骤 03.将busybox:latest镜像从 registry B 复制到本地目录,并以 OCI 格式保存1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ skopeo copy --insecure-policy --src-tls-verify=false  docker://harbor.weiyigeek.top/devops/busybox:latest oci:busybox-latest                tree -h busybox-latest/                            
步骤 04.将 alpine:3.13.1 镜像从 docker 本地存储( /var/lib/docker/image) push 到 registry B中(实际上替代 docker push 功能)1 2 3 4 5 6 7 8 9 10 11 docker images alpine:3.13.1       skopeo copy --insecure-policy --dest-tls-verify=false  --dest-authfile /root/.docker/config.json docker-daemon:alpine:3.13.1 docker://harbor.weiyigeek.top/devops/alpine:3.13.1                
                 
                weiyigeek.top-harbor仓库中的alpine镜像
             
Skopeo sync - 镜像同步命令 描述: Skopeo sync可以在容器仓库和本地目录之间同步镜像,其功能类似于阿里云的 image-syncer (https://github.com/AliyunContainerService/image-syncer ) 工具, 实际上其比 image-syncer 更强大、灵活性更强一些,废话不多说实践为王。
skopeo sync 镜像同步文件示例:
步骤 01.将仓库中所有busybox镜像版本同步到本地目录。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ skopeo sync --insecure-policy --src-tls-verify=false  --src docker --dest dir harbor.weiyigeek.top/devops/busybox /tmp                            $ tree -h /tmp/busybox:latest /tmp/busybox:latest ├── [755K]  5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa ├── [1.4K]  beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a ├── [ 527]  manifest.json └── [  33]  version 0 directories, 4 files 
步骤 02.从本地目录/tmp/同步到 docker 的 hub 容器仓库中,此外我们可以通过浏览器看到 weiyigeek 用户下的 busybox 镜像 (https://hub.docker.com/u/weiyigeek)。 1 2 3 4 5 6 7 8 $ skopeo sync --insecure-policy --dest-tls-verify=false  --src dir --dest docker /tmp weiyigeek                      
                 
                weiyigeek.top-hub-docker
             
步骤 03.从hub容器仓库中同步alpine-jenkins-jnlp:v2.285镜像到本地临时容器仓库中。1 2 3 4 5 6 $ skopeo sync --insecure-policy --src-tls-verify=false  --dest-tls-verify=false  --src docker --dest docker weiyigeek/alpine-jenkins-jnlp:v2.285 192.168.12.111:5000/                
步骤 04.以配置文件方式进行同步, 首先我们需要准备一个需要同步的资源清单。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 cat <<'EOF'  > skopeo-sync.yml registry.example.com:   images:     busybox: []     redis:       - "1.0"        - "2.0"        - "sha256:111111"    images-by-tag-regex:       nginx: ^1\.13\.[12]-alpine-perl$   credentials:       username: john       password: this is a secret   tls-verify: true    cert-dir: /home/john/certs quay.io:   tls-verify: false    images:     coreos/etcd:       - latest EOF $ skopeo sync --src yaml --dest docker skopeo-sync.yml my-registry.local.lan/repo/ 
skopeo-sync.yml 文件中镜像匹配复制镜像说明:
registry.example.com/busybox : 所有版本的镜像. 
registry.example.com/redis : 标记为“1.0”和“2.0”的图像以及带有摘要的图像”sha256:0000000000000000000000000000000011111111111111111111111111111111”. 
registry.example.com/nginx : 图片标记为“1.13.1-alpine-perl”和“1.13.2-alpine-perl”. 
quay.io/coreos/etcd : 拉取最新版本的镜像。 
 
描述: 利用该命令我们可以列出 registry 上的某个镜像的所有 tag ,它是使用标准的 registry API 来获取镜像 tag。
简单示例:1 $ skopeo list-tags docker://harbor.weiyigeek.top/devops/busybox:latest 
Skopeo delete - 删除仓库中镜像Tag 描述: 使用该命令我们可以删除镜像Tag,注意此处仅仅只是通过registry API 来删除镜像的 tag(即删除了 tag 对 manifests 文件的引用)并非真正将镜像删除掉,如果想要删除镜像的 layer 还是需要通过 registry GC 的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ skopeo delete docker://harbor.weiyigeek.top/devops/busybox:latest --debug                                     curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json"  -I -X GET http://192.168.12.111:5000/v2/busybox/manifests/latest                         Docker-Content-Digest=$(curl -s --header "Accept: application/vnd.docker.distribution.manifest.v2+json"  -I -X GET http://192.168.12.111:5000/v2/busybox/manifests/latest | grep "Docker-Content-Digest"  | cut -d ' '  -f 2) curl -I -X DELETE http://192.168.12.111:5000/v2/busybox/manifests/${Docker-Content-Digest}  
 
0x03 镜像同步最佳实践 本章节,主要参考我前同事木子博客(https://blog.k8s.li/skopeo.html)。 
1.指定文本中镜像同步 假如,给你一个镜像列表 images-list.txt, 其格式如下, 我们可以直接采用shell脚本调用skopeo进行执行。1 2 3 4 5 6 7 8 cat <<'EOF'  > images-list.txt kubesphere/kube-apiserver:v1.20.6 kubesphere/kube-scheduler:v1.20.6 kubesphere/kube-proxy:v1.20.6 kubesphere/kube-controller-manager:v1.20.6 kubesphere/kube-apiserver:v1.19.8 EOF 
同步的shell脚本 skopeo-copy.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/bin/bash GREEN_COL="\\033[32;1m"  RED_COL="\\033[1;31m"  NORMAL_COL="\\033[0;39m"  SOURCE_REGISTRY=$1  TARGET_REGISTRY=$2  : ${IMAGES_LIST_FILE:="images-list.txt"}  : ${TARGET_REGISTRY:="hub.k8s.li"}  : ${SOURCE_REGISTRY:="docker.io"}  BLOBS_PATH="docker/registry/v2/blobs/sha256"  REPO_PATH="docker/registry/v2/repositories"  set  -eo pipefailCURRENT_NUM=0 ALL_IMAGES="$(sed -n '/#/d;s/:/:/p' ${IMAGES_LIST_FILE} | sort -u) "  TOTAL_NUMS=$(echo  "${ALL_IMAGES} "  | wc -l) skopeo_copy  () { if  skopeo copy --insecure-policy --src-tls-verify=false  --dest-tls-verify=false  \  --override-arch amd64 --override-os linux -q docker://$1  docker://$2 ; then    echo  -e "$GREEN_COL  Progress: ${CURRENT_NUM} /${TOTAL_NUMS}  sync $1  to $2  successful $NORMAL_COL "   else    echo  -e "$RED_COL  Progress: ${CURRENT_NUM} /${TOTAL_NUMS}  sync $1  to $2  failed $NORMAL_COL "    exit  2  fi  } for  image in  ${ALL_IMAGES} ; do  let  CURRENT_NUM=${CURRENT_NUM} +1  skopeo_copy ${SOURCE_REGISTRY} /${image}  ${TARGET_REGISTRY} /${image}  done 
执行命令和结果: 1 2 3 4 5 6 $ bash sync.sh docker.io localhost:5000 Progress: 1/143 sync docker.io/alpine:3.14 to localhost:5000/alpine:3.14 successful Progress: 2/143 sync docker.io/busybox:1.31.1 to localhost:5000/busybox:1.31.1 successful .... Progress: 142/143 sync docker.io/weaveworks/scope:1.13.0 to localhost:5000/weaveworks/scope:1.13.0 successful Progress: 143/143 sync docker.io/wordpress:4.8-apache to localhost:5000/wordpress:4.8-apache successful 
2.使用 registry 存储特性同步 描述: 将镜像从 registry 中同步到本地目录,使用 registry 存储的特性,将本地目录中的镜像转换成 registry 存储的格式, 这样的好处就是可以去除一些 skopeo dir 中重复的 layers,减少镜像的总大小。
convert-images.sh1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #!/bin/bash set  -eo pipefailGREEN_COL="\\033[32;1m"  RED_COL="\\033[1;31m"  NORMAL_COL="\\033[0;39m"  SOURCE_REGISTRY=$1  TARGET_REGISTRY=$2  IMAGES_DIR=$2  : ${IMAGES_DIR:="images"}  : ${IMAGES_LIST_FILE:="images-list.txt"}  : ${SOURCE_REGISTRY:="docker.io"}  : ${TARGET_REGISTRY:="hub.k8s.li"}  BLOBS_PATH="docker/registry/v2/blobs/sha256"  REPO_PATH="docker/registry/v2/repositories"  CURRENT_NUM=0 ALL_IMAGES="$(sed -n '/#/d;s/:/:/p' ${IMAGES_LIST_FILE} | sort -u) "  TOTAL_NUMS=$(echo  "${ALL_IMAGES} "  | wc -l) skopeo_sync  () { if  skopeo sync --insecure-policy --src-tls-verify=false  --dest-tls-verify=false  \  --override-arch amd64 --override-os linux --src docker --dest dir $1  $2  > /dev/null; then    echo  -e "$GREEN_COL  Progress: ${CURRENT_NUM} /${TOTAL_NUMS}  sync $1  to $2  successful $NORMAL_COL "   else    echo  -e "$RED_COL  Progress: ${CURRENT_NUM} /${TOTAL_NUMS}  sync $1  to $2  failed $NORMAL_COL "    exit  2  fi  } convert_images  () { rm -rf ${IMAGES_DIR} ; mkdir -p ${IMAGES_DIR}   for  image in  ${ALL_IMAGES} ; do    let  CURRENT_NUM=${CURRENT_NUM} +1         image_name=${image%%:*}    image_tag=${image##*:}    image_repo=${image%%/*}       skopeo_sync ${SOURCE_REGISTRY} /${image}  ${IMAGES_DIR} /${image_repo}       manifest="${IMAGES_DIR} /${image} /manifest.json"    manifest_sha256=$(sha256sum ${manifest}  | awk '{print $1}' )         mkdir -p ${BLOBS_PATH} /${manifest_sha256:0:2} /${manifest_sha256}     ln -f ${manifest}  ${BLOBS_PATH} /${manifest_sha256:0:2} /${manifest_sha256} /data        mkdir -p ${REPO_PATH} /${image_name} /{_uploads,_layers,_manifests}   mkdir -p ${REPO_PATH} /${image_name} /_manifests/revisions/sha256/${manifest_sha256}    mkdir -p ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /{current,index/sha256}   mkdir -p ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256}       echo  -n "sha256:${manifest_sha256} "  > ${REPO_PATH} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} /link     echo  -n "sha256:${manifest_sha256} "  > ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /current/link   echo  -n "sha256:${manifest_sha256} "  > ${REPO_PATH} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} /link      for  layer in  $(sed '/v1Compatibility/d'  ${manifest}  | grep -Eo "\b[a-f0-9]{64}\b" ); do        mkdir -p ${BLOBS_PATH} /${layer:0:2} /${layer}                       mkdir -p ${REPO_PATH} /${image_name} /_layers/sha256/${layer}        echo  -n "sha256:${layer} "  > ${REPO_PATH} /${image_name} /_layers/sha256/${layer} /link       ln -f ${IMAGES_DIR} /${image} /${layer}  ${BLOBS_PATH} /${layer:0:2} /${layer} /data        done   done  } convert_images 
install.sh : 使用这个脚本将 registry 存储中的镜像转换成 skopeo dir 的方式,然后再将镜像同步到 registry 中。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/bin/bash REGISTRY_DOMAIN="harbor.k8s.li"  REGISTRY_PATH="/var/lib/registry"  cd  ${REGISTRY_PATH} gen_skopeo_dir  () {        BLOB_DIR="docker/registry/v2/blobs/sha256"      REPO_DIR="docker/registry/v2/repositories"           SKOPEO_DIR="docker/skopeo"           for  image in  $(find ${REPO_DIR}  -type  d -name "current" ); do                   name=$(echo  ${image}  | awk -F '/'  '{print $5"/"$6":"$9}' )         link=$(cat ${image} /link | sed 's/sha256://' )         mfs="${BLOB_DIR} /${link:0:2} /${link} /data"                   mkdir -p "${SKOPEO_DIR} /${name} "                   ln ${mfs}  ${SKOPEO_DIR} /${name} /manifest.json                  layers=$(grep -Eo "\b[a-f0-9]{64}\b"  ${mfs}  | sort -n | uniq)         for  layer in  ${layers} ; do                         ln ${BLOB_DIR} /${layer:0:2} /${layer} /data ${SKOPEO_DIR} /${name} /${layer}          done      done  } sync_image  () {         for  project in  $(ls ${SKOPEO_DIR} ); do          skopeo sync --insecure-policy --src-tls-verify=false  --dest-tls-verify=false  \         --src dir --dest docker ${SKOPEO_DIR} /${project}  ${REGISTRY_DOMAIN} /${project}      done  } gen_skopeo_dir 
 
 
 
温馨提示: 此种方式是有些复杂对于大镜像的复制是推荐的, 而对于一些小镜像且显得多余。
3.从 registry 存储中 select 出镜像进行同步 描述: 先将镜像同步到一个 registry 中,再将镜像从 registry 存储中捞出来,该 registry 可以当作一个镜像存储的池子,我们使用 Linux 中硬链接的特性将镜像”复制”一份出来,然后再打一个 tar 包, 这样做的好处就是每次打包镜像的时候都能复用历史的镜像数据,而且性能极快。
步骤 01.先将镜像同步到一个固定的 registry 中。1 $ bash skopeo-copy.sh docker.io localhost:5000 
步骤 02.使用该脚本将镜像从 registry 存储中捞出来1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #!/bin/bash set  -eo pipefailIMAGES_LIST="$1 "  REGISTRY_PATH="$2 "  OUTPUT_DIR="$3 "  BLOB_DIR="docker/registry/v2/blobs/sha256"  REPO_DIR="docker/registry/v2/repositories"  if  [ -d ${OUTPUT_DIR}  ];then   rm -rf ${OUTPUT_DIR} ; fi mkdir -p ${OUTPUT_DIR}  for  image in  $(find ${IMAGES_LIST}  -type  f -name "*.list"  | xargs grep -Ev '^#|^/'  | grep ':' ); do      image_name=${image%%:*}    image_tag=${image##*:}       tag_link=${REGISTRY_PATH} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /current/link   manifest_sha256=$(sed 's/sha256://'  ${tag_link} )   manifest=${REGISTRY_PATH} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256} /data   mkdir -p ${OUTPUT_DIR} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256}       ln -f ${manifest}  ${OUTPUT_DIR} /${BLOB_DIR} /${manifest_sha256:0:2} /${manifest_sha256} /data      mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /{_uploads,_layers,_manifests}   mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/revisions/sha256/${manifest_sha256}    mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /{current,index/sha256}   mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256}       echo  -n "sha256:${manifest_sha256} "  > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /current/link   echo  -n "sha256:${manifest_sha256} "  > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/revisions/sha256/${manifest_sha256} /link   echo  -n "sha256:${manifest_sha256} "  > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_manifests/tags/${image_tag} /index/sha256/${manifest_sha256} /link      for  layer in  $(sed '/v1Compatibility/d'  ${manifest}  | grep -Eo '\b[a-f0-9]{64}\b'  | sort -u); do        mkdir -p ${OUTPUT_DIR} /${BLOB_DIR} /${layer:0:2} /${layer}        mkdir -p ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_layers/sha256/${layer}        ln -f ${BLOB_DIR} /${layer:0:2} /${layer} /data ${OUTPUT_DIR} /${BLOB_DIR} /${layer:0:2} /${layer} /data       echo  -n "sha256:${layer} "  > ${OUTPUT_DIR} /${REPO_DIR} /${image_name} /_layers/sha256/${layer} /link   done  done