背景

Anthos 1.8开始,Anthos 的裸金属版本(Anthos clusters on bare metal,  以下简称 ABM )支持容器影像仓库的镜像( Registry Mirror,以下简称RM )。通过这个功能,可以将一个私有的影像仓库设置为 GCR 的镜像( RM ),这样 K8S 集群就可以访问这个私有仓库中的容器影像,假装成访问 GCR 中的影像。

例如,假设将私有镜像仓库 https://172.18.0.20/library 设置为影像仓库的镜像,当 ABM 需要访问容器影像 gcr.io/google-containers/pause 时,会先尝试获取 https://172.18.0.20/library/google-containers/pause, 如果找不到,才会去 gcr.io 获取。


本文将介绍影像仓库镜像( RM )的使用场景和设置办法。

使用场景

一、降低公网访问流量

如图,当部署在数据中心的 ABM 中的工作负载启动时,可以直接访问私有仓库,降低了公网流量的使用,也避免了由于网路故障带来的不确定性。


二、实现不同环境间的无缝衔接

在实际工作中,企业往往有多个互相隔离的环境用于开发测试生产,使用仓库镜像技术,可以实现同一个工作负载的声明直接用于不同环境之间,即方便了运维人员,也避免了开发测试过程中由于不断修改声明带来的隐患。如下图

建立容器影像仓库镜像

让 ABM 使用仓库镜像的方法在此文档中已经介绍了,但是文中只覆盖了如何将 ABM 相关的容器影像上载到 RM 中,而对于企业自己构建的镜像,以及存在于 gcr.io 上大量的公开镜像,并没有涉及。

使用 Docker Push 存在的问题

使用 docker push 可以将镜像在不同的仓库之间传递,其基本的流程如下

$ docker pull gcr.io/google-containers/pause:latest

$ docker save gcr.io/google-containers/pause:latest  > google-containers_pause.tar

# Copy the tarball to private environment

$ docker load < google-containers_pause.tar

$ docker tag gcr.io/google-containers/pause:latest 172.18.0.20/library/google-containers/pause:latest

$ docker push  172.18.0.20/library/google-containers/pause:latest

这样 ABM 集群就可以从 172.18.0.20/library/google-containers/pause:latest 获取 gcr.io/google-containers/pause:latest 了。

不过这个方法存在一个问题,docker 在这个过程中,不会保持容器镜像的 Digest ,例如我们可以用 crane 这个工具(下文详述)查看:

$ crane digest gcr.io/google-containers/pause:latest

sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f

$ crane digest 172.18.0.20/library/google-containers/pause:latest

sha256:569cfa0ff435f3a076a1b06a1f45d772ee3f5d4fbf6b39242a573c0cff632d69

如果不修改 Digest ,则在拉取影像时就会发生无法找到容器影像的错误。一种解决办法是,将所有通过 Digest 来访问容器影像的k8s声明文件修改:

比如如果您的 Yaml 文件中有

  image: gcr.io/google-containers/pause:latest@sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f

您需要改成

  image: gcr.io/google-containers/pause:latest@sha256:569cfa0ff435f3a076a1b06a1f45d772ee3f5d4fbf6b39242a573c0cff632d69

但如果我们一一更改所有容器影像的 Digest ,这就失去了创建 RM 的一大好处了。


使用 Crane 工具和 curl 来传输容器影像

crane 是 go-containerregistry 中提供一个命令行工具,用来跟影像仓库交互以管理容器影像。


下载安装 crane :在 https://github.com/google/go-containerregistry/releases 选取您需要的版本进行下载,解压后将 crane 和 gcrane 这两个可执行文件 cp 至 /usr/local/bin 下即可。

另外我们还需要安装 jq 来协助处理 json :

apt install -y jq


如果传输的两个仓库之间网络直接可达,可以直接用 crane copy 来传输:

crane copy gcr.io/google-containers/pause:latest

  172.18.0.20/library/google-containers/pause:latest

crane copy 是可以完全保持影像的 digest 的。


如果两个仓库之间不能直接网络可达,则可以在一个站点上使用附件中的 dumpimage.sh 脚本来保证容器影像的 Digest 保持不变,它会先将影像保存成文件,然后将文件传输到另一个站点后导入私有仓库。具体做法如下:

./dumpimage.sh gcr.io/google-containers/pause:latest pause

这会生成一个目录 pause ,包含了该影像的所有必要内容。将这个目录传输到私有仓库的站点后,执行该目录中的 auto.sh :

cd pause

export TARGETHOST=172.18.0.20

export AUTHCRED="Basic $(jq -r '.auths."172.18.0.20".auth' ~/.docker/config.json)"

export SSL_CERT_FILE=/etc/docker/certs.d/172.18.0.20/ca.crt

bash auto.sh

用 crane digest 验证一下:

$ crane digest 172.18.0.20/library/google-containers/pause:latest

sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f

$ crane digest gcr.io/google-containers/pause:latest

sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f

可见通过这种方式传输的影像其 Digest 是不变的。

附件

dumpimage.sh :

#!/bin/bash

# Copyright 2021 Google LLC

# Author: Jun Sheng

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#     https://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.


if ! which crane

then

  echo install crane

  exit 1

fi

if ! which jq

then

  echo need jq

  exit 1

fi


fetch_plain(){

  local DSTIMG=$1

  local TAG=$2

  local DIGEST=$3


  cat <<EOF >> $OUTPUT.sh

#!/bin/bash

crane push $DIGEST.tar $TARGETHOST/${TARGETNAMESPACE:-library}/$DSTIMG

curl -H "Content-Type: application/vnd.docker.distribution.manifest.v2+json"

   -H "Authorization: $AUTHCRED"

   --data-binary @$DIGEST.json -X PUT

    https://$TARGETHOST/v2/${TARGETNAMESPACE:-library}/${DSTIMG%:*}/manifests/sha256:$DIGEST

crane tag $TARGETHOST/${TARGETNAMESPACE:-library}/${DSTIMG%:*}@sha256:$DIGEST $TAG

EOF

}


fetch_multi() {

  local SRCIMG=$1

  local DSTIMG=$2

  local TAG=$3

  local DIGEST=$4

  for DGST in $(cat $DIGEST.json|jq -r .manifests[].digest)

  do

    crane manifest $SRCIMG@$DGST > ${DGST#sha256:}.json

    crane pull $SRCIMG@$DGST ${DGST#sha256:}.tar

    fetch_plain $DSTIMG $TAG ${DGST#sha256:}

  done

  cat <<EOF >> $OUTPUT.sh

#!/bin/bash

curl -H "Content-Type: application/vnd.docker.distribution.manifest.list.v2+json"

   -H "Authorization: $AUTHCRED"

   --data-binary @$DIGEST.json -X PUT

    https://$TARGETHOST/v2/${TARGETNAMESPACE:-library}/${DSTIMG%:*}/manifests/sha256:$DIGEST

crane tag $TARGETHOST/${TARGETNAMESPACE:-library}/${DSTIMG%:*}@sha256:$DIGEST $TAG

EOF

}


main() {

  local SRCIMG=$1

  if [ "$SRCIMG" == "${SRCIMG%%/*}" ]

  then

    local DSTIMG="$SRCIMG"

  else

    HPART="${SRCIMG%%/*}"

    if [ $HPART == ${HPART%%.*} ]

    then

      local DSTIMG="$SRCIMG"

    else

      local DSTIMG=${SRCIMG#*/}

    fi

  fi

  local TAG=${DSTIMG#*:}

  if [ $TAG == $DSTIMG ]

  then

    local TAG=latest

  fi


  local DIGEST=$(crane digest $SRCIMG$HASH|sed 's/^sha256://')

  export OUTPUT="auto"

  rm -f $OUTPUT.sh

  cat <<EOF > $OUTPUT.sh

#!/bin/bash

# Copyright 2021 Google LLC

#


if [ x"$AUTHCRED" = x ]

then

  echo AUHCRED not defined

exit 1

fi


EOF

  crane manifest $SRCIMG$HASH > $DIGEST.json

  case $(cat $DIGEST.json|jq -r .mediaType) in

    application/vnd.docker.distribution.manifest.list.v2+json)

      fetch_multi $SRCIMG $DSTIMG $TAG $DIGEST

      ;;

    application/vnd.docker.distribution.manifest.v2+json)

      crane pull $SRCIMG $DIGEST.tar

      fetch_plain $DSTIMG $TAG $DIGEST

      ;;

  esac

}


set -e

mkdir $2 

cd $2

main $1



相关推荐