go语言保存docker镜像(支持windows mac linux)-(不使用tls连接docker引擎版本)

架构

app-> docker 引擎-> docker 镜像仓库

借助远程 docker 引擎 从 docker 镜像仓库获取容器镜像

此程序作为docker 引擎的客户端 save 镜像到 客户端本地
保存为tar 包

go.mod

module yunwei

go 1.16

require (
	github.com/Microsoft/go-winio v0.4.18 // indirect
	github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
	github.com/aws/aws-sdk-go v1.38.24 // indirect
	github.com/containerd/containerd v1.4.4 // indirect
	github.com/coreos/go-etcd v2.0.0+incompatible // indirect
	github.com/docker/distribution v2.7.1+incompatible // indirect
	github.com/docker/docker v20.10.6+incompatible // indirect
	github.com/docker/go-connections v0.4.0 // indirect
	github.com/docker/go-units v0.4.0 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.0.1 // indirect
	github.com/spf13/viper v1.7.1 // indirect
	github.com/tidwall/gjson v1.7.5 // indirect
	github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect
	github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
	golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect
)

go 语言作为docker 客户端连接 docker 引擎时不使 用tls

package main

// go doc  github.com/docker/docker/api/types  ImageSummary

//  build

// GOOS=linux  GOARCH=amd64 go build  simg.go


// /simg.exe -i hub.xxx.cn/xxx/busybox:v1   -h 10.0.x.xx:2375  -u xxx -p xx


import (
	"archive/tar"
	"compress/gzip"
	"context"
	"encoding/base64"
	"encoding/json"
	"flag"
	"io"
	"path/filepath"
	//"errors"
	//"path"
	"time"

	"github.com/tidwall/gjson"
	"io/ioutil"
	"os"
	"strconv"

	"fmt"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"strings"
)


func pullImageFromRepo(imageName ,host ,user,passwd string) {

	cli, err := client.NewClientWithOpts(client.WithHost("tcp://"+host), client.WithAPIVersionNegotiation())

	if err != nil {
		fmt.Println(err)
	}

	ctx := context.Background()

	authConfig := types.AuthConfig{
		Username: user,
		Password: passwd,
	}
	encodedJSON, err := json.Marshal(authConfig)
	if err != nil {
		panic(err)
	}
	authStr := base64.URLEncoding.EncodeToString(encodedJSON)

	out, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{RegistryAuth: authStr})
	if err != nil {
		panic(err)
	}

	defer out.Close()
	io.Copy(os.Stdout, out)

}

func main() {

	var image string
	var host string

	var username  string

	var password  string


	flag.StringVar(&image,"i","","镜像")

	flag.StringVar(&host,"h","","docker 引擎地址")

	flag.StringVar(&username,"u","","镜像仓库用户名")

	flag.StringVar(&password,"p","","镜像仓库密码")




	flag.Parse()

	fmt.Printf("image:%v\n",image)


	fmt.Printf("host:%v\n",host)


	imageName := image
	pullImageFromRepo(imageName,host,username,password)
	id := getImageId(imageName,host)
	if id == "" {
		fmt.Println("镜像不存在")
		return
	}

	strArry := strings.Split(imageName, "/")
	path := strArry[(len(strArry) - 1)]
	pathArray := strings.Split(path, ":")
	saveDir := pathArray[0] + "-" + pathArray[1]
	fmt.Println(saveDir)

	//
	done := make(chan struct{})
	defer close(done)

	f := func(done <-chan struct{}) {
		for {

			select {

			//case <-done:
			//	return
			default:
				time.Sleep(time.Second * 2)
				fmt.Printf(".")
				//fmt.Printf("Sec")

				//fmt.Fprintf(os.Stdout, "%s", ".")
				//fmt.Fprintf(os.Stdout, ".")
				//time.Sleep(time.Second * 1)
			case <-done:
				return

			}

		}

	}
	go f(done)

	//
	saveImage(id, saveDir,host)
	// 打开tar包  打开成功后删除原tar 包
	fmt.Println("从远程docker 引擎工作区拉取镜像元数据成功")
	fmt.Println("开始配置镜像元信息")
	untarFromPath(saveDir, "./"+saveDir+"/"+saveDir+".tar")
	err := os.Remove("./" + saveDir + "/" + saveDir + ".tar")
	if err != nil {
		fmt.Println(err)
	}
	// 配置元信息
	arry := strings.Split(imageName, ":")
	configMetdata(arry[0], arry[1], saveDir, imageName)
	// 开始打包
	//tarFun(saveDir+".tar", saveDir, saveDir)
	fmt.Println("镜像元信息配置成功开始打包镜像")
	tarFunWindows(saveDir+".tar", saveDir, saveDir)
	err = os.RemoveAll(saveDir)
	if err != nil {
		fmt.Println(err)
	}
}



func tarFunWindows(desc string, src string, dirSrc string) error {
	fd, err := os.Create(desc)
	if err != nil {
		return err
	}
	defer fd.Close()
	gw := gzip.NewWriter(fd)
	defer gw.Close()
	tr := tar.NewWriter(gw)
	defer tr.Close()
	err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		hdr, err := tar.FileInfoHeader(info, "")
		if err != nil {
			return err
		}
		strPath := strings.Replace(path, "\\", "/", -1)
		arrary := strings.Split(strPath, "/")
		if arrary[0] == dirSrc && path != dirSrc {
			hdr.Name = strings.Replace(strPath, dirSrc+"/", "", 1)
			err = tr.WriteHeader(hdr)
			if err != nil {
				return err
			}
			fs, err := os.Open(path)
			if err != nil {
				return err
			}
			defer fs.Close()
			if info.Mode().IsRegular() {
				io.Copy(tr, fs)
			}
			return nil
		}
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}

func configMetdata(imageName string, tag string, path string, RepoTags string) {
	data, err := ioutil.ReadFile(path + "/" + "manifest.json")
	if err != nil {
		return
	}
	str := string(data)
	length := gjson.Get(str, "0.Layers.#")
	n, _ := strconv.Atoi(length.String())
	n = n - 1
	//fmt.Println(n)
	//valur := gjson.Get(str, "0.Layers.6")

	strNum := strconv.Itoa(n)
	t := "0.Layers." + strNum
	valur1 := gjson.Get(str, t)

	//fmt.Println(length.String())
	//fmt.Println(valur.String())
	//fmt.Println(valur1.String())

	test := valur1.String()
	arr := strings.Split(test, "/")
	//fmt.Println(arr[0])
	lastId := arr[0]

	//      
	//imageName := "nginx"
	//  {"nginx":{"latest":"9c77a26fdf5fb977da1d0e46e6c3cf5bb198f99d8c9b7d96118493b8fafe1d79"}}

	//tag := "latest"
	//   
	str1 := "{" + `"` + imageName + `"` + `:{"` + tag + `":"` + lastId + `"}}`
	//	fmt.Println(str1)

	//fileName := "image/test.dat"
	fileName := path + "/" + "repositories"
	dstFile, err := os.Create(fileName)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer dstFile.Close()
	dstFile.WriteString(str1 + "\n")
	//
	//fmt.Println(str)
	//fmt.Println(strings.Replace(str, "null", "[\""+RepoTags+"\"]", 1))
	err = os.Remove(path + "/" + "manifest.json")
	if err != nil {
		panic(err)
	}

	//
	//imageName

	//manifestStr := strings.Replace(str, "null", "[\"172.16.100.216/binhu/lz-zyc-synergy-service:latest\"]", 1)
	manifestStr := strings.Replace(str, "null", "[\""+RepoTags+"\"]", 1)

	manifest, err := os.Create(path + "/" + "manifest.json")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer manifest.Close()
	manifest.WriteString(manifestStr + "\n")

}
func getImageId(tag string,host string) string {

	cli, err := client.NewClientWithOpts(client.WithHost("tcp://"+host), client.WithAPIVersionNegotiation())

	if err != nil {
		fmt.Println(err)
	}
	images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
	if err != nil {
		panic(err)
	}
	for _, image := range images {
		tags := image.RepoTags
		for _, name := range tags {
			if name == tag {
				imageId := strings.Split(image.ID, ":")
				id := imageId[1]
				return id
			}
		}
	}
	return ""

}

func saveImage(id string, iname string,host string) {
	fmt.Println("开始从远程docker引擎工作区获取镜像")
	err := os.Mkdir(iname, 0666)
	if err == os.ErrExist {
		if err := os.Remove(iname); err != nil {
			fmt.Println("清除环境失败")
		} else {
			panic(err)
		}
	}
	fw, err := os.Create(iname + "/" + iname + ".tar")
	if err != nil {
		panic(err)
	}
	defer fw.Close()


	cli, err := client.NewClientWithOpts(client.WithHost("tcp://"+host), client.WithAPIVersionNegotiation())

	if err != nil {
		fmt.Println(err)
	}
	a := []string{id}
	r, err := cli.ImageSave(context.Background(), a)
	if err != nil {
		fmt.Println(err)
	}
	if _, err := io.Copy(fw, r); err != nil {
		panic(err)
	} else {
		//		fmt.Println(n)
	}
}

func makeDir() string {
	err := os.Mkdir("image", 0666)
	if err != nil {
		fmt.Println(err)
	}
	return "image"

}


func untarFromPath(base string, imageTar string) {
	r, err := os.Open(imageTar)
	if err != nil {
		panic(err)
	}
	defer r.Close()
	tr := tar.NewReader(r)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			return
		}
		if err != nil {
			panic(err)
		}
		fullpath := filepath.Join(base, hdr.Name)
		info := hdr.FileInfo()
		// as dir
		if info.IsDir() {
			os.MkdirAll(fullpath, 0755)
			continue
		}
		dir := filepath.Dir(fullpath)
		os.MkdirAll(dir, 0755)
		// as file
		f, err := os.Create(fullpath)
		if err != nil {
			panic(err)
		}
		_, err = io.Copy(f, tr)
		if err != nil {
			f.Close()
			panic(err)
		}
		f.Chmod(info.Mode())
		f.Close()
	}
}

windows 下运行

可以在cmd powerShell 直接运行

推荐安装 gtibash 在gitbash 的命令行运行

// /simg.exe -i hub.xxx.cn/xxx/busybox:v1   -h 10.0.x.xx:2375  -u xxx -p xx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是搭建私服镜像中心docker-registry和docker-registry-web的步骤: 1.安装DockerDocker Compose 2.创建一个目录来存储docker-compose.yml文件和证书文件 3.创建docker-compose.yml文件并添加以下内容: ```yaml version: '3' services: registry: restart: always image: registry:2 ports: - 5000:5000 environment: REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm REGISTRY_STORAGE_DELETE_ENABLED: "true" REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key volumes: - ./data:/var/lib/registry - ./auth:/auth - ./certs:/certs registry-web: restart: always image: mkuchin/docker-registry-web:v0.1.2 ports: - 8080:8080 environment: REGISTRY_URL: https://registry:5000 REGISTRY_WEB_TITLE: Docker Registry REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key volumes: - ./auth:/auth - ./certs:/certs ``` 4.创建一个目录来存储证书文件和htpasswd文件 5.生成证书文件 ```shell openssl req -newkey rsa:4096 -nodes -sha256 -keyout domain.key -x509 -days 365 -out domain.crt ``` 6.生成htpasswd文件 ```shell htpasswd -Bc auth/htpasswd <username> ``` 7.启动docker-compose ```shell docker-compose up -d ``` 8.访问https://<your-domain>:8080,输入用户名和密码即可登录docker-registry-web界面。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值