如何下载google和redhat的镜像?

45 篇文章 2 订阅

目录

背景

解决方案:镜像加速器

解决方案:用魔法打败魔法

最后


背景

CNCF 项目是google发起的,所以一些新的镜像都在google的镜像中,阿里云还没同步过来。例如安装CEPH所需要的镜像。其中几个是google的,阿里云的加速拉取不到

 k8s.gcr.io/sig-storage/csi-attacher:v3.4.0
 k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.5.0
 k8s.gcr.io/sig-storage/csi-provisioner:v3.1.0
 k8s.gcr.io/sig-storage/csi-resizer:v1.4.0
 k8s.gcr.io/sig-storage/csi-snapshotter:v5.0.1
 quay.io/ceph/ceph:v16.2.7
 quay.io/cephcsi/cephcsi:v3.5.1
 quay.io/csiaddons/k8s-sidecar:v0.2.1
 quay.io/csiaddons/volumereplication-operator:v0.3.0
 rook/ceph:v1.8.7

目前常用的 Docker Registry 公开服务有:

  • docker.io :Docker Hub 官方镜像仓库,也是 Docker 默认的仓库

  • gcr.iok8s.gcr.io :谷歌镜像仓库

  • quay.io :Red Hat 镜像仓库

  • ghcr.io :GitHub 镜像仓库

当使用 docker pull 仓库地址/用户名/仓库名:标签 时,会前往对应的仓库地址拉取镜像,标签无声明时默认为 latest, 仓库地址无声明时默认为 docker.io 。

众所周知的原因,在国内访问这些服务异常的慢,甚至 gcr.io 和 quay.io 根本无法访问。

解决方案:镜像加速器

针对 Docker Hub ,Docker 官方和国内各大云服务商均提供了 Docker 镜像加速服务。

你只需要简单配置一下(以 Linux 为例):

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["镜像加速器"]
}
EOF

sudo systemctl daemon-reload
sudo service docker restart

便可以通过访问国内镜像加速器来加速 Docker Hub 的镜像下载。

不过这种办法也只能针对 docker.io ,其它的仓库地址并没有真正实际可用的加速器(至少我目前没找到)。

解决方案:用魔法打败魔法

既然无法治本,那治治标还是可以的吧。

若我们使用一台魔法机器从 gcr.io 或 quay.io 等仓库先把我们无法下载的镜像拉取下来,然后重新上传到 docker.io ,是不是就可以使用 Docker Hub 的镜像加速器来下载了。

镜像仓库迁移的功能,我这里采用了 Go Docker SDK ,整体实现也比较简单。

package main

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"text/template"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/spf13/pflag"
)

var (
	content    = pflag.StringP("content", "", "", "原始镜像,格式为:{ \"hub-mirror\": [] }")
	maxContent = pflag.IntP("maxContent", "", 10, "原始镜像个数限制")
	username   = pflag.StringP("username", "", "", "docker hub 用户名")
	password   = pflag.StringP("password", "", "", "docker hub 密码")
	outputPath = pflag.StringP("outputPath", "", "output.sh", "结果输出路径")
)

func main() {
	pflag.Parse()

	fmt.Println("验证原始镜像内容")
	var hubMirrors struct {
		Content []string `json:"hub-mirror"`
	}
	err := json.Unmarshal([]byte(*content), &hubMirrors)
	if err != nil {
		panic(err)
	}
	if len(hubMirrors.Content) > *maxContent {
		panic("content is too long.")
	}
	fmt.Printf("%+v\n", hubMirrors)

	fmt.Println("连接 Docker")
	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
	if err != nil {
		panic(err)
	}

	fmt.Println("验证 Docker 用户名密码")
	if *username == "" || *password == "" {
		panic("username or password cannot be empty.")
	}
	authConfig := types.AuthConfig{
		Username: *username,
		Password: *password,
	}
	encodedJSON, err := json.Marshal(authConfig)
	if err != nil {
		panic(err)
	}
	authStr := base64.URLEncoding.EncodeToString(encodedJSON)
	_, err = cli.RegistryLogin(context.Background(), authConfig)
	if err != nil {
		panic(err)
	}

	fmt.Println("开始转换镜像")
	output := make([]struct {
		Source string
		Target string
	}, 0)

	wg := sync.WaitGroup{}

	for _, source := range hubMirrors.Content {
		if source == "" {
			continue
		}

		target := *username + "/" + strings.ReplaceAll(source, "/", ".")

		wg.Add(1)
		go func(source, target string) {
			defer wg.Done()

			fmt.Println("开始转换", source, "=>", target)
			ctx := context.Background()

			// 拉取镜像
			pullOut, err := cli.ImagePull(ctx, source, types.ImagePullOptions{})
			if err != nil {
				panic(err)
			}
			defer pullOut.Close()
			io.Copy(os.Stdout, pullOut)

			// 重新标签
			err = cli.ImageTag(ctx, source, target)
			if err != nil {
				panic(err)
			}

			// 上传镜像
			pushOut, err := cli.ImagePush(ctx, target, types.ImagePushOptions{
				RegistryAuth: authStr,
			})
			if err != nil {
				panic(err)
			}
			defer pushOut.Close()
			io.Copy(os.Stdout, pushOut)

			output = append(output, struct {
				Source string
				Target string
			}{Source: source, Target: target})
			fmt.Println("转换成功", source, "=>", target)
		}(source, target)
	}

	wg.Wait()

	if len(output) == 0 {
		panic("output is empty.")
	}

	tmpl, err := template.New("pull_images").Parse(`{{- range . -}}
docker pull {{ .Target }}
docker tag {{ .Target }} {{ .Source }}
{{ end -}}`)
	if err != nil {
		panic(err)
	}
	outputFile, err := os.Create(*outputPath)
	if err != nil {
		panic(err)
	}
	defer outputFile.Close()
	err = tmpl.Execute(outputFile, output)
	if err != nil {
		panic(err)
	}
	fmt.Println(output)
}

以需要转换的 gcr.io/google-samples/microservices-demo/emailservice:v0.3.5 为例,使用方式:

功能实现了,剩下的就是找台带有魔法的机器了。

GitHub Actions 就是个好选择,我们可以利用提交 issues 来触发镜像仓库迁移的功能。

workflow 的实现如下:

# workflow 名称
name: hub-mirror

# 当新建 issues 时,触发当前 workflow
on:
  issues:
    types:
      - opened

# 需要执行的任务列表
jobs:
  # 镜像转换任务
  mirror_transition:
    # 运行环境
    runs-on: ubuntu-latest
    # 运行条件 => issues 的 label 包含 hub-mirror
    if: contains(github.event.issue.labels.*.name, 'hub-mirror')
    # 镜像转换任务的步骤列表
    steps:
      # 1. 切换分支(默认主分支)
      - name: Check out code
        uses: actions/checkout@v2
      # 2. 设置 go 环境
      - name: Setup go
        uses: actions/setup-go@v2
        with:
          go-version: 1.17
      # 3. 运行 go 代码
      - name: Run code
        run: go run main.go --username=${{ secrets.DOCKERHUB_USERNAME }} --password=${{ secrets.DOCKERHUB_TOKEN }} --content='${{ github.event.issue.body }}' --maxContent=11 --outputPath=output.sh
      # 4. 当成功输出 output.sh 文件时,为 issues 添加评论
      - name: Add comment
        if: ${{ hashFiles('output.sh') }}
        uses: actions/github-script@v5
        with:
          script: |
            const fs = require('fs')
            const data = fs.readFileSync('output.sh', 'utf8')
            const body = "Hello,您可以直接执行该命令:\n" +
                "\n" +
                "```shell\n" +
                "echo -e '" + data.replace(/\n/g, '\\n') + "' | bash\n" +
                "```\n" +
                "\n" +
                "或是手动执行:\n" +
                "\n" +
                "```shell\n" +
                data +
                "```\n" +
                "\n" +
                "希望可以帮助到您,祝您周" + "日一二三四五六".charAt(new Date().getDay()) + "愉快!"
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body
            })
      # 5. 当以上步骤成功时,为 issues 添加 success 标签
      - name: Success issues
        if: ${{ success() }}
        uses: actions/github-script@v5
        with:
          script: |
            github.rest.issues.addLabels({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: ['success']
            })
      # 6. 当以上步骤失败时,为 issues 添加 failure 标签,并为其添加失败原因评论
      - name: Failure issues
        if: ${{ failure() }}
        uses: actions/github-script@v5
        with:
          script: |
            github.rest.issues.addLabels({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: ['failure']
            })
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: "[构建失败,点击查看](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
            })

实际的使用效果:

只要执行最终输出的命令,就可以飞快的使用 Docker Hub 的加速器下载 gcr.io 或 quay.io 等镜像了。

最后

本篇的实现已放在 GitHub :https://github.com/myysophia/hub-mirror

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值